Environment Variables Setup - striae-org/striae GitHub Wiki

This guide is the source of truth for Striae environment and secret handling after the proxy/auth migration.

All deployment automation is Bash-based. On Windows, run commands in Git Bash or WSL.

Script Inventory

  • npm run deploy-config -> scripts/deploy-config.sh
  • npm run update-env -> scripts/deploy-config.sh --update-env
  • npm run install-workers -> scripts/install-workers.sh
  • npm run deploy-workers -> deploy all worker services
  • npm run deploy-workers:secrets -> scripts/deploy-worker-secrets.sh
  • npm run deploy-pages:secrets -> scripts/deploy-pages-secrets.sh
  • npm run deploy-pages -> scripts/deploy-pages.sh
  • npm run deploy:all -> scripts/deploy-all.sh
  • npm run enable-totp-mfa -> scripts/enable-totp-mfa.mjs

Required Before Setup: admin-service.json

Create the Firebase Admin credential file before running config/secrets scripts:

mkdir -p app/config
cp -f app/config-example/admin-service.json app/config/admin-service.json

Populate the file with real Firebase service-account JSON.

Required fields:

  • project_id
  • client_email
  • private_key

Imported by scripts as:

  • PROJECT_ID
  • FIREBASE_SERVICE_ACCOUNT_EMAIL
  • FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY

Variable Ownership Model

Use this model to avoid drift during deployments:

  • .env is local source input for deployment scripts.
  • Worker secrets are pushed with deploy-worker-secrets.sh.
  • Pages secrets are pushed with deploy-pages-secrets.sh.
  • Generated runtime/config files are updated by deploy-config.sh.

Important:

  • Firebase Web SDK values (API_KEY, AUTH_DOMAIN, PROJECT_ID, etc.) are client config, not server secrets.
  • Firebase Admin values remain private and are loaded from app/config/admin-service.json.
  • Cloudflare Access worker JWT variables (CF_ACCESS_*) are not part of the default script-driven deployment flow.

What deploy-config Updates

npm run deploy-config performs the full config sync/replace pass:

  1. Creates .env from .env.example when needed.
  2. Copies/syncs config examples and worker template files.
  3. Preserves app/config/admin-service.json during sync.
  4. Imports Firebase Admin credentials from app/config/admin-service.json.
  5. Prompts for required values and auto-generates selected secrets.
  6. Generates manifest signing, export encryption, data-at-rest encryption, and user-KV encryption key pairs/key ids when missing.
  7. Normalizes key registry env vars for export/data-at-rest/user-KV encryption (*_KEYS_JSON + *_ACTIVE_KEY_ID) to support key-rotation fallback.
  8. Replaces placeholders in generated files.
  9. Runs validation checkpoint (required vars, format checks, placeholder checks, generated-file checks).

deploy-config.sh also prompts for IMAGE_SIGNED_URL_SECRET, which the Image Worker uses to mint and verify signed image-access tokens.

IMAGE_SIGNED_URL_BASE_URL is auto-derived from PAGES_CUSTOM_DOMAIN by deploy-config.sh (for example https://your-domain.com/api/image) and overrides the base URL used when the Image Worker builds signed delivery URLs. It is optional but should be set for all standard Page-proxied deployments.

Files updated by deploy-config.sh include:

  • wrangler.toml (PAGES_PROJECT_NAME)
  • workers/*/wrangler.jsonc (worker names, account id, bucket/kv bindings)
  • app/config/config.json (app URL, signing, export encryption key metadata)
  • app/config/firebase.ts (Firebase Web SDK config placeholders)

Use npm run update-env only when you intentionally want to reset from .env.example and refresh generated config files.

Secret Deployment Contracts

Worker secrets

npm run deploy-workers:secrets currently sets:

  • Audit Worker: optional DATA_AT_REST_ENCRYPTION_ENABLED, DATA_AT_REST_ENCRYPTION_PRIVATE_KEY, DATA_AT_REST_ENCRYPTION_PUBLIC_KEY, DATA_AT_REST_ENCRYPTION_KEY_ID, DATA_AT_REST_ENCRYPTION_KEYS_JSON, DATA_AT_REST_ENCRYPTION_ACTIVE_KEY_ID
  • User Worker: PROJECT_ID, FIREBASE_SERVICE_ACCOUNT_EMAIL, FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY; optional DATA_AT_REST_ENCRYPTION_PRIVATE_KEY, DATA_AT_REST_ENCRYPTION_KEY_ID, DATA_AT_REST_ENCRYPTION_KEYS_JSON, DATA_AT_REST_ENCRYPTION_ACTIVE_KEY_ID, USER_KV_ENCRYPTION_PRIVATE_KEY, USER_KV_ENCRYPTION_KEYS_JSON, USER_KV_ENCRYPTION_ACTIVE_KEY_ID; when USER_KV_WRITE_ENDPOINTS_ENABLED is true: USER_KV_ENCRYPTION_KEY_ID, USER_KV_ENCRYPTION_PUBLIC_KEY
  • Data Worker: MANIFEST_SIGNING_PRIVATE_KEY, MANIFEST_SIGNING_KEY_ID, EXPORT_ENCRYPTION_PRIVATE_KEY, EXPORT_ENCRYPTION_KEY_ID; optional DATA_AT_REST_ENCRYPTION_ENABLED, DATA_AT_REST_ENCRYPTION_PRIVATE_KEY, DATA_AT_REST_ENCRYPTION_PUBLIC_KEY, DATA_AT_REST_ENCRYPTION_KEY_ID, DATA_AT_REST_ENCRYPTION_KEYS_JSON, DATA_AT_REST_ENCRYPTION_ACTIVE_KEY_ID, EXPORT_ENCRYPTION_KEYS_JSON, EXPORT_ENCRYPTION_ACTIVE_KEY_ID
  • Image Worker: DATA_AT_REST_ENCRYPTION_PUBLIC_KEY, DATA_AT_REST_ENCRYPTION_KEY_ID, IMAGE_SIGNED_URL_SECRET; optional DATA_AT_REST_ENCRYPTION_PRIVATE_KEY, DATA_AT_REST_ENCRYPTION_KEYS_JSON, DATA_AT_REST_ENCRYPTION_ACTIVE_KEY_ID, IMAGE_SIGNED_URL_BASE_URL
  • PDF Worker: ACCOUNT_ID, BROWSER_API_TOKEN
  • Lists Worker: LISTS_ADMIN_SECRET

EXPORT_ENCRYPTION_PRIVATE_KEY and EXPORT_ENCRYPTION_KEY_ID are mandatory for case package, confirmation package, and archive package workflows, which are encrypted by default and fail closed when encryption material is missing.

EXPORT_ENCRYPTION_KEYS_JSON and EXPORT_ENCRYPTION_ACTIVE_KEY_ID are optional but recommended for key rotation. When present, the Data Worker attempts decryption with package key ID first and then registry fallback keys.

MANIFEST_SIGNING_PRIVATE_KEY and MANIFEST_SIGNING_KEY_ID are mandatory for the Data Worker to sign case manifests.

USER_KV_ENCRYPTION_PRIVATE_KEY, USER_KV_ENCRYPTION_PUBLIC_KEY, and USER_KV_ENCRYPTION_KEY_ID are mandatory for user profile reads/writes in the User Worker. User profile requests fail closed when this key material is missing or when stored KV values are not encrypted envelopes. USER_KV_ENCRYPTION_PUBLIC_KEY and USER_KV_ENCRYPTION_KEY_ID are only pushed when USER_KV_WRITE_ENDPOINTS_ENABLED is true (the default); set it to false for read-only deployments.

USER_KV_ENCRYPTION_KEYS_JSON and USER_KV_ENCRYPTION_ACTIVE_KEY_ID are optional rotation helpers for user-KV decryption fallback.

DATA_AT_REST_ENCRYPTION_ENABLED must be true in all standard deployments; data-at-rest encryption is mandatory and validation will fail if it is disabled.

For detailed encryption implementation, key registry architecture, and rotation strategy, see Data-at-Rest Encryption.

app/config/config.json is also populated with the public export encryption key material (EXPORT_ENCRYPTION_PUBLIC_KEY, EXPORT_ENCRYPTION_KEY_ID) for browser-side package encryption.

IMAGE_SIGNED_URL_SECRET is an Image Worker secret only and is not deployed as a Pages secret. Signed image GET requests carry an st query token that Pages forwards directly to the Image Worker via service binding; the Image Worker validates the token against IMAGE_SIGNED_URL_SECRET.

IMAGE_SIGNED_URL_BASE_URL overrides the base URL the Image Worker uses when building signed delivery URLs. It is auto-derived from PAGES_CUSTOM_DOMAIN by deploy-config.sh. Only set a custom value when routing signed image delivery through a non-standard domain.

BROWSER_API_TOKEN must be a Cloudflare API token with Browser Rendering - Edit permission.

LISTS_ADMIN_SECRET is the Bearer token guarding the lists-worker write endpoints (POST /members, DELETE /members, POST /primershear, DELETE /primershear). It is auto-generated by deploy-config.sh and pushed to the lists-worker by deploy-workers:secrets.

Pages secrets

npm run deploy-pages:secrets requires:

  • PROJECT_ID

Worker routing is handled by Cloudflare Service Bindings defined in wrangler.toml; no *_WORKER_DOMAIN secrets are required in the Pages environment.

Registration Gateway Workflow

Use this workflow to restrict new account registration to specific email addresses or domains.

The registration allowlist is stored in the STRIAE_LISTS KV namespace under the key "allow" (managed by the lists-worker). To add or remove entries:

# Add an entry
curl -X POST https://lists.<your-domain>/members \
  -H "Authorization: Bearer $LISTS_ADMIN_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"entry": "[email protected]"}'

# Or write the full CSV directly via wrangler
wrangler kv key put --remote --namespace-id=<STRIAE_LISTS_KV_ID> "allow" "[email protected],@organization.com"

Each entry is either an exact email address ([email protected]) or a domain wildcard (@organization.com). Matching is case-insensitive. When the list is empty, registration is unrestricted.

To inspect the current list:

curl https://lists.<your-domain>/members
# or
wrangler kv key get --remote --namespace-id=<STRIAE_LISTS_KV_ID> "allow"

PrimerShear Email List Workflow

Use this workflow when you want verified users on an allowlist to receive the primershear PDF format.

The primershear allowlist is stored in the STRIAE_LISTS KV namespace under the key "primershear" (managed by the lists-worker). To add or remove entries:

# Add an entry
curl -X POST https://lists.<your-domain>/primershear \
  -H "Authorization: Bearer $LISTS_ADMIN_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"entry": "[email protected]"}'

# Or write the full CSV directly via wrangler
wrangler kv key put --remote --namespace-id=<STRIAE_LISTS_KV_ID> "primershear" "[email protected]"

To inspect the current list:

curl https://lists.<your-domain>/primershear
# or
wrangler kv key get --remote --namespace-id=<STRIAE_LISTS_KV_ID> "primershear"

Recommended Deployment Sequence

npm run deploy:all runs this sequence:

  1. deploy-config.sh
  2. install-workers.sh
  3. Worker deploys (npm run deploy-workers)
  4. deploy-worker-secrets.sh
  5. deploy-pages-secrets.sh
  6. deploy-pages.sh

Redeploy Guidance After Secret Changes

  • Worker-secret-only changes usually do not require re-running worker code deployment.
  • Pages secret changes should be followed by a Pages deployment so current deployments pick up updated values.
  • Any deploy-config change that modifies generated source/config files should be followed by normal deploy steps for affected services.

Validation and Troubleshooting

Validate generated state without changing files:

bash ./scripts/deploy-config.sh --validate-only

Common issues:

  • Missing app/config/admin-service.json: create from template and populate required fields.
  • Placeholder errors: run npm run deploy-config and complete prompts.
  • Invalid domain values: remove protocol/path/trailing slash from domain vars.
  • Missing worker Wrangler files: ensure wrangler.jsonc is copied from each wrangler.jsonc.example.
  • JSON parse failures in app requests (Unexpected token '<'): confirm requests go through /api/*, verify Pages secrets, and inspect response status/body for upstream auth/challenge failures.

Security Notes

  • Never commit .env or real credentials.
  • Keep app/config/admin-service.json private.
  • Avoid logging secrets or sensitive validation material.

Related Documentation