UUniCAS DOCS
Console GitHub
UNICAS / DOCUMENTATIONEdit source

#Deployment and local configuration

This repository deploys one UniCAS Cloudflare Worker containing the tenant and administrator HTTP planes, the administrator BFF and UI, and MCP/OAuth ingress. Keep these configuration classes separate:

  1. Deployment identity authenticates Wrangler to Cloudflare.
  2. Runtime secrets live in Cloudflare Workers secrets.
  3. Local settings come from the current process and gitignored .wrangler/ state, or from Docker Compose interpolation and its named volume.

Never commit tokens, private keys, .dev.vars, .env, or .wrangler/.

#Local development

Run the host runtime:

pnpm dev

It starts:

Surface Default URL
Administrator console http://localhost:4070/admin/
Browser-facing UniCAS edge http://localhost:4070/stacks/...
Direct Miniflare edge http://127.0.0.1:8794
Mock OIDC discovery http://127.0.0.1:8793/.well-known/openid-configuration

Host-mode Miniflare state persists under .wrangler/miniflare. Stop the stack with Ctrl+C; restarting pnpm dev reuses that state.

Vite proxies tenant, managed-issuer, and discovery routes to the direct edge so the local browser topology matches production's single public origin.

The local runtime uses its mock OIDC provider by default. To use Google OIDC, set both GOOGLE_OIDC_CLIENT_ID and GOOGLE_OIDC_CLIENT_SECRET. Optionally set GOOGLE_OIDC_ISSUER; register http://localhost:4070/admin/auth/callback as the redirect URI.

Other local settings:

Variable Default Purpose
UNICAS_LOCAL_HOST 127.0.0.1 Host interface for Miniflare and Vite
UNICAS_LOCAL_PUBLIC_HOST Same as UNICAS_LOCAL_HOST Browser-visible host for local OIDC endpoints; Docker sets localhost
UNICAS_ADMIN_ORIGIN http://localhost:4070 Browser-facing administrator origin

To exercise managed issuers and Playground locally, set both MANAGED_ISSUER_PRIVATE_KEY_PKCS8 and MANAGED_ISSUER_KEY_ID before startup. Host and Docker modes forward the pair together; supplying only one leaves the managed issuer unavailable.

#Docker

Run the same stack in Docker when host Node.js is unavailable:

pnpm dev --docker

Compose forwards UNICAS_ADMIN_ORIGIN and the three OIDC variables above. It publishes ports 4070, 8793, and 8794 and stores .wrangler state in the named Docker volume unicas-state; it does not bind-mount the checkout's .wrangler/ directory. docker compose -f stacks/unicas/local/compose.yaml down preserves that volume, so the next start reuses state.

#Disposable capability fixture

Generate a local ES256 issuer fixture only when a test or non-production stack needs one:

pnpm keys:local

The default output is .wrangler/capability/local.json, created with exclusive owner permissions. The command refuses to overwrite an existing file. Never commit or reuse this fixture as a production key.

#Cloudflare deployment identity

Wrangler reads its identity from the process running the deployment:

$env:CLOUDFLARE_ACCOUNT_ID = "<account-id>"
$env:CLOUDFLARE_API_TOKEN = "<api-token>"
pnpm exec wrangler whoami

Use a dedicated token per developer or CI environment. Scope it to the target account and zone with only the Worker, D1, R2, KV, Durable Object, and route permissions needed for deployment.

The committed Wrangler configuration contains production resource IDs, route names, and the Google OAuth client ID. Those identifiers are public configuration, not credentials, but changing them must be intentional. The previous unidocs-cas deployment and its unicas.shazhou.work route remain online as a legacy environment. This repository deploys isolated unicas-* storage resources and the unicas Worker at https://api.unicas.work and https://console.unicas.work. The apex route remains attached temporarily as a rollback boundary until split-origin validation completes. Existing wire media types and downstream issuer identifiers remain compatibility contracts and are not renamed.

#Worker secrets

Provision secrets with Wrangler so values never appear in shell history:

pnpm --filter @unicas/service-cloudflare exec wrangler secret put GOOGLE_OIDC_CLIENT_SECRET
pnpm --filter @unicas/service-cloudflare exec wrangler secret put SESSION_ENCRYPTION_KEYS
pnpm --filter @unicas/service-cloudflare exec wrangler secret put OAUTH_STATE_ENCRYPTION_KEY
pnpm --filter @unicas/service-cloudflare exec wrangler secret put ADMIN_EMAIL_ALLOWLIST

SESSION_ENCRYPTION_KEYS is a non-empty JSON object mapping key IDs to base64url keys, for example {"2026-09":"<base64url-32-byte-key>"}. Keep old entries during session-key rotation until sessions sealed with them have expired.

Additional features require these secrets:

Secret Required for
CAS_R2_ACCESS_KEY_ID Presigned direct R2 uploads
CAS_R2_SECRET_ACCESS_KEY Presigned direct R2 uploads
MANAGED_ISSUER_PRIVATE_KEY_PKCS8 UniCAS-managed stack issuers
CAS_AUDIT_READER_KEY Protected tenant audit reads

MANAGED_ISSUER_KEY_ID is the corresponding non-secret key ID in wrangler.toml. The current implementation exposes one managed signing key; add key-ring overlap support before rotating it in production.

Optional OIDC/session variables include OIDC_ISSUER, OIDC_DISCOVERY_URL, SESSION_TTL_MS, SESSION_COOKIE_NAME, SESSION_COOKIE_SECURE, and SESSION_COOKIE_SAME_SITE. ADMIN_TEST_ACCOUNT_EMAIL and ADMIN_TEST_ACCOUNT_PASSWORD are test-only and must never be enabled in production.

CAS_OAUTH_DISCOVERY_ALLOWED_ORIGINS is optional. Unset or blank permits public HTTPS issuer discovery; a comma-separated value restricts discovery to those origins. The Worker intentionally keeps the global_fetch_strictly_public compatibility flag so metadata and JWKS discovery cannot reach private network targets.

Before split-origin deployment, add both production redirect URIs without removing the existing apex callbacks:

https://console.unicas.work/admin/auth/callback
https://api.unicas.work/oauth/google/callback

The first serves administrator WebUI and CLI login; the second serves remote MCP OAuth. docs.unicas.work is served by the independent assets-only unicas-docs Worker under stacks/unicas/docs-site; it has no API service bindings or credentials. Validate and deploy it separately:

pnpm docs:check
pnpm deploy:docs:plan
pnpm deploy:docs

The accepted origin ownership model is documented in UniCAS domain topology.

#Read-only deployment validation

Print the repository-controlled deployment sequence without running it:

pnpm deploy:plan

Build Wrangler's actual upload bundle without contacting the deployment API:

pnpm --filter @unicas/service-cloudflare exec wrangler deploy --dry-run

Do not run pnpm deploy --dry-run: pnpm can consume that argument instead of forwarding it, which invokes the real root deploy script. Use only pnpm deploy:plan or the direct Wrangler command above for dry runs.

#Deploy and smoke

After the plan, bundle dry-run, tests, and typecheck pass, deploy with:

pnpm deploy:production

The explicit production command builds the Worker, deploys it, rebuilds the protocol artifacts used by smoke, and then tests the production endpoint. pnpm deploy intentionally refuses to run. A named Wrangler environment must use --skip-smoke; otherwise the deploy command refuses to proceed because the default smoke target is production:

node stacks/unicas/deploy/deploy.mjs --env staging --skip-smoke
node stacks/unicas/deploy/smoke.mjs https://staging.example.com

No named environments are currently declared in wrangler.toml, so the example above is valid only after adding isolated bindings and routes.

The smoke signer reads provisioned private keys from the gitignored .wrangler/cas-deploy/ directory. To target one control-plane-managed stack, set UNICAS_SMOKE_STACK_ID, UNICAS_SMOKE_ISSUER, UNICAS_SMOKE_AUDIENCE, UNICAS_SMOKE_KID, and UNICAS_SMOKE_KEY_FILE. The smoke flow covers lease, read, metadata, Root Ref update, usage, and garbage collection. Never commit the key files or print their contents.