General Rules
- Store hosted deployment credentials in environment-scoped GitHub Actions secrets.
- Store Vercel app runtime and build-time configuration in the Vercel project environment, not workflow-wide GitHub Actions environment variables.
- Store local web runtime configuration in
apps/web/.env.local. - Do not commit tokens, keys, passwords, or rendered env dumps.
- Do not edit checked-in compose files to inject per-environment secrets.
Local And Self-Hosted Web
GitHub Actions Secrets By Area
Vercel-hosted web apps
Vercel workflows use GitHub Environments namedvercel-preview-<app> and
vercel-production-<app>. Keep the deployment secrets on those environments,
not as workflow-level env: entries. Production environments should require
reviewers and restrict deployment branches to production; preview environments
should require review whenever branch pushes can run repository-controlled code
with deployment credentials.
VERCEL_TOKENVERCEL_ORG_ID- App-specific
VERCEL_PROJECT_IDvalues such asVERCEL_PLATFORM_PROJECT_IDandVERCEL_APPS_PROJECT_ID - Infrastructure deployments use
VERCEL_INFRASTRUCTURE_PROJECT_IDin thevercel-preview-infrastructureandvercel-production-infrastructureGitHub Environments - QR deployments use
VERCEL_QR_PROJECT_IDin thevercel-preview-qrandvercel-production-qrGitHub Environments
Turborepo remote cache
Remote-cache identity is repository-wide rather than app runtime configuration:- Store a dedicated, least-privilege Vercel team cache token as the repository
secret
TURBO_TOKEN. Rotate it independently from deployment tokens and revoke the previous token after trusted canaries pass. - Store the Vercel team slug as the repository variable
TURBO_TEAM. Workflows temporarily acceptsecrets.TURBO_TEAMas a migration fallback; remove that secret after the variable is confirmed. - Leave
TURBO_APIunset for Vercel-managed remote caching. - Do not configure
TURBO_REMOTE_CACHE_SIGNATURE_KEYuntil artifact signing is deliberately enabled and rolled out. - Optional repository variables
ACTIONS_CACHE_NOTICE_PERCENT,ACTIONS_CACHE_WARNING_PERCENT, andACTIONS_CACHE_CRITICAL_PERCENToverride the weekly report’s 80/90/100 thresholds. Cache size and retention are not variables; the report discovers both from GitHub’s repository API.
.github/actions/run-with-turbo-remote-cache/action.yml. The action places them
on the wrapped command step, not the workflow, job, GITHUB_ENV, image layer,
or deployment artifact. Never pass TURBO_TOKEN to pull-request or Dependabot
jobs; secretless jobs use the task-family GitHub cache fallback.
Production and preview app configuration
These values belong in the Vercel project environment for each app and target. Do not export them from GitHub Actions workflows. The Vercel deploy workflows run repository code during install and build, so workflow-wide production runtime secrets are a supply-chain exposure path.PRODUCTION_SUPABASE_URLPRODUCTION_SUPABASE_PUBLISHABLE_KEYPRODUCTION_SUPABASE_SECRET_KEYENCRYPTION_MASTER_KEY
Integration-specific runtime secrets
- SePay OAuth requires
SEPAY_OAUTH_CLIENT_ID,SEPAY_OAUTH_CLIENT_SECRET,SEPAY_OAUTH_TOKEN_ENCRYPTION_SECRET, and a webhook auth secret such asSEPAY_WEBHOOK_API_KEY. - SePay web runtimes also require an app origin through
WEB_APP_URL,NEXT_PUBLIC_WEB_APP_URL, orNEXT_PUBLIC_APP_URLso OAuth callbacks and webhook provisioning can generate stable platform URLs. - SePay workspace enablement is controlled by the workspace secret
ENABLE_SEPAY_INTEGRATION=true. - SePay runtime overrides may optionally set
SEPAY_OAUTH_AUTHORIZE_URL,SEPAY_OAUTH_BASE_URL, andSEPAY_API_BASE_URLwhen targeting non-default environments. - SePay OAuth state is stored in a short-lived HttpOnly callback cookie and is
HMAC-signed. The signer uses
SEPAY_OAUTH_STATE_SECRETwhen set; otherwise it falls back to the requiredSEPAY_OAUTH_CLIENT_SECRET. - Polar (inventory storefront checkout + workspace subscriptions) requires
POLAR_ACCESS_TOKEN(platform-level org token) andPOLAR_WEBHOOK_SECRET(webhook signature verification) onapps/pay. SetPOLAR_SANDBOX=trueto target Polar’s sandbox, and the optionalPOLAR_CURRENCIESto allowlist storefront currencies. Configure Polar to deliver webhooks tohttps://pay.tuturuuu.com/api/payment/webhooks;apps/webno longer owns payment API or webhook routes. See the Polar storefront integration runbook for details. - Square Terminal (Inventory + Storefront pay-at-terminal checkout) stores Square application credentials, OAuth/manual tokens, webhook signature keys, locations, terminal ids, and webhook notification URL overrides encrypted from Inventory settings. Do not configure Square credentials as deployment secrets. See the Square Terminal integration runbook for the complete setup and smoke checklist.
Supabase migrations
SUPABASE_ACCESS_TOKENSTAGING_DB_PASSWORDSTAGING_PROJECT_IDSTAGING_DB_URLPRODUCTION_DB_PASSWORDPRODUCTION_PROJECT_IDPRODUCTION_DB_URL
Modal deployment
MODAL_TOKEN_IDMODAL_TOKEN_SECRETMODAL_ENVIRONMENTas a GitHub Actions variable
Mobile store beta deployment
mobile-deploy-stores.yaml uses the mobile-store-beta GitHub Environment and
runs only from production pushes that touch apps/mobile/**,
scripts/mobile-deployment/**, the workflow file, ci-check.yml, or
tuturuuu.ts. Keep this environment branch-restricted to production.
GitHub Environment secrets:
MOBILE_DEPLOYMENT_CI_TOKEN: issued from the root workspace mobile deployment vault and scoped toproductionmobile deployment.
MOBILE_DEPLOYMENT_CI_TOKEN is not set yet, the mobile store workflow’s
credentials preflight emits a notice and skips the Android and iOS publish jobs.
Set the secret when the deployment vault is ready; invalid non-empty tokens still
fail during bundle fetch so real misconfiguration remains visible.
No Firebase, signing, store API, or mobile build secret values should be stored
in GitHub Environment secrets or variables. Those resources live in the
apps/infrastructure mobile deployment vault at /internal/mobile-deployment,
which is root-workspace-only and requires manage_mobile_deployment_vault.
apps/web deployment requirements:
ENCRYPTION_MASTER_KEY: required before uploads, activation, and CI bundle fetches can decrypt the per-version deployment data key.- Root workspace Drive/R2/Supabase storage must be available. File resources are
stored as encrypted ciphertext blobs under the reserved
.tuturuuu/mobile-deployment-vaultprefix; normal Drive routes deny that prefix.
- Manage all non-file values in the vault’s Secrets panel. Build-time
secrets are rendered into
apps/mobile/.env.githubduring CI; built-in signing and store secrets are exported separately for the Android and iOS release steps. The expected production build-time keys areNEXT_PUBLIC_SUPABASE_URL,NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY,API_BASE_URL,TURNSTILE_SITE_KEY,TURNSTILE_BASE_URL,GOOGLE_WEB_CLIENT_ID,GOOGLE_IOS_CLIENT_ID,MOBILE_TASK_DESCRIPTION_EDITING_ENABLED, andMOBILE_CALENDAR_INTEGRATIONS_ENABLED. Custom secrets are allowed when they use uppercaseA-Z0-9_names, do not collide with built-in secret names, and have single-line UTF-8 values. - CI file payload keys such as
MOBILE_ANDROID_GOOGLE_SERVICES_JSON_B64andMOBILE_IOS_GOOGLE_SERVICE_INFO_PLIST_B64are rejected as secrets; upload those payloads through the file resources below. - Android files: production
google-services.json, upload keystore, and Google Play service-account JSON. - Built-in Android secrets:
ANDROID_KEYSTORE_ALIAS,ANDROID_KEYSTORE_PASSWORD,ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD,GOOGLE_PLAY_PACKAGE_NAME=com.tuturuuu.app.mobile, andGOOGLE_PLAY_TRACK=internal. - iOS files: production
GoogleService-Info.plist, Apple distribution.p12, App Store provisioning profile, and App Store Connect.p8private key. - Built-in iOS secrets:
APPLE_BUNDLE_ID=com.tuturuuu.app.mobile,APPLE_DISTRIBUTION_CERTIFICATE_PASSWORD,APPLE_TEAM_ID,APP_STORE_CONNECT_API_KEY_ID, andAPP_STORE_CONNECT_ISSUER_ID.
mobile-store-beta GitHub Environment as
MOBILE_DEPLOYMENT_CI_TOKEN.
Package publishing
Package publishing uses npm trusted publishing (GitHub OIDC), so noNPM_TOKEN secret is stored. The publish-npm job in each
release-*-package.yaml workflow requests id-token: write, downloads the
prepared tarball, and runs npm publish while npm exchanges the OIDC token for a
short-lived publish credential.
- Each publish job is bound to a per-package GitHub Environment
(
ui-release-productionfor@tuturuuu/ui,types-release-productionfor@tuturuuu/types), and the matching npm trusted publisher must reference this repository, the exact workflow filename, and that environment. - No npm registry token (
NPM_TOKENorNODE_AUTH_TOKEN) is set on these environments. Adding one would be unnecessary and would weaken the build-vs-publish job split.
Docker-Specific Notes
- The Docker web helper auto-generates a stable local Redis token and injects
UPSTASH_REDIS_REST_TOKEN,UPSTASH_REDIS_REST_URL, and the matching internalSRH_TOKENvalue for the bundledserverless-redis-httpcontainer. - Watcher-managed Infrastructure projects do not inherit the integrated Docker
Redis runtime. They start with Redis disabled, strip generic
UPSTASH_REDIS_REST_*and Docker-specificDOCKER_UPSTASH_*values, and receive Redis only from project-scopedMANAGED_PROJECT_<PROJECT_ID>_UPSTASH_REDIS_REST_URLandMANAGED_PROJECT_<PROJECT_ID>_UPSTASH_REDIS_REST_TOKENvariables. SRH_TOKENis the container’s internal environment variable. The user-facing override surface isUPSTASH_REDIS_REST_TOKENif you intentionally replace the helper-generated value.- The local dev compose file keeps a dev-only fallback token for direct local
use, but production Redis compose requires
UPSTASH_REDIS_REST_TOKENduring Compose interpolation. Use the Docker web helper or export a strong token before enabling the productionredisprofile directly. - Redis and
serverless-redis-httphost ports must stay loopback-bound. Do not publish those sidecars on all host interfaces or through Cloudflare Tunnel. apps/webuses Redis for defense-in-depth one-time state such as CLI refresh-token replay protection. IfUPSTASH_REDIS_REST_URLandUPSTASH_REDIS_REST_TOKENare unavailable, CLI refresh requests continue after JWT validation and user lookup, while confirmed Redis-backed replays are still rejected.docker compose configexpands env values; treat its output as sensitive.
Supabase RPC Authorization Hardening
- Treat every new
SECURITY DEFINERfunction inpublicas externally callable until proven otherwise. - In the same migration that creates or replaces the function, enforce both:
- in-function authorization checks (
auth.uid()plus workspace membership/permission gate), and - explicit execute privileges (
revoke all ... from public, then grant only required roles such asauthenticated/service_role).
- in-function authorization checks (
- Do not rely only on
apps/webpage or API RBAC checks for tenant isolation when an RPC is exposed through the Data API. - During review, grep the migration for
security definer,auth.uid(),has_workspace_permission,revoke, andgrant executebefore merge.
Change Management
When a new deployment flow is added:- Add its secret names here.
- Add the workflow or runtime page to the relevant devops doc.
- Keep the boundary clear between repo config, GitHub Actions config, and machine-local config.