Configuration & secrets
Environment variables and secrets — the three scopes, which one wins, and why a misplaced one fails silently.
Secrets & variables
- •Add secrets (API keys, database URLs, tokens) under a project’s Secrets. They’re encrypted at rest and never shown in the UI or logs.
- •For React/static apps, build-time variables are inlined during the build; for Node services they’re available to the running process.
Three scopes — and which one wins
- •Every secret is stored at exactly one scope: workspace (tenant), project, or environment.
- •Workspace — shared by every project you own. Project — every environment of that one project. Environment — that one environment (e.g. staging) only.
- •They are merged when a deploy resolves its configuration, most specific first: environment overrides project, project overrides workspace. The same key at two scopes is not an error — the narrower one simply wins.
- •Pick the narrowest scope that is still correct. Anything whose value DIFFERS per environment — OAuth callback URLs, API base URLs, webhook endpoints, any value naming a hostname — belongs at environment scope. A callback URL set at project scope points staging at production’s URL.
A secret on the wrong scope fails silently
- •Nothing warns you. The secret exists, the console lists it, the deploy goes green — and the variable is still absent from the container, because it was attached to a different project or a different environment than the one you deployed.
- •Symptom: the app crashes on boot, or one page returns 500 carrying the app’s own “missing/invalid environment variable” error, while the deployment shows as succeeded.
- •Check what actually reaches the app, one scope at a time: curl -s -H "Authorization: Bearer $SHIPYARD_TOKEN" "$SHIPYARD_API/tenants/<t>/secrets?scopeType=environment&scopeId=<environmentId>" — then again with scopeType=project&scopeId=<projectId>, and scopeType=tenant. The union of those three is exactly what the app receives.
- •Only key names come back. Values are write-only and can never be read through the API or the console, so verification is always by key, not by value.
Deleting a project does not carry its secrets forward
- •Secrets belong to the project (or environment) they were created on. Delete a project and create a new one with the same name and you have a DIFFERENT project — none of the old secrets come with it, and the new one starts empty.
- •The slug is what tells them apart. On delete the old project’s slug is freed for reuse by renaming it to <slug>~<id>, so the replacement takes the clean slug. Display names are not unique; ids and slugs are.
- •So after recreating a project, re-add every variable — not just the ones you remember. List what the old app actually had before you delete it: GET /tenants/<t>/secrets?scopeType=project&scopeId=<oldProjectId>, and the same for each of its environments.
- •Resolve the target from GET /tenants/<t>/projects and key off id and slug. Then GET /tenants/<t>/projects/<projectId> for environments[].id.
- •Before writing a secret, confirm the environment id you are about to use is the one actually serving the URL you are fixing: GET /tenants/<t>/projects/<p>/environments/<e>/runtime returns that environment’s live URL. Match it against the URL in your browser — production and staging are easy to mix up, and a value on the wrong one is invisible.
Changes need a redeploy
- •Variables are injected when the build runs and when the container starts — so adding, rotating or re-scoping a secret does NOT change anything already running.
- •Deploy again after changing one. For Node/Python a rollback onto the same release is enough: it restarts the container with a freshly resolved environment and skips the rebuild.
- •Set secrets BEFORE the first deploy of a new app, so the first build already has its build-time variables.
Setting them from the API
- •POST $SHIPYARD_API/tenants/<t>/secrets with { "scopeType": "environment", "scopeId": "<environmentId>", "key": "SSO_CALLBACK_URL", "value": "…" }.
- •scopeType is one of tenant | project | environment. scopeId is the project or environment id, and is omitted only for tenant scope.
- •Rotate or rename an existing one: PATCH $SHIPYARD_API/tenants/<t>/secrets/<secretId> with { "value": "…" } or { "key": "NEW_NAME" }. Remove it: DELETE the same path.
- •Keep your own copy of anything you cannot regenerate — the platform will not hand a value back to you.
Logs
- •Watch logs live from a deployment’s detail page: build logs for every deploy, Node/Python runtime logs (container stdout/stderr) for server apps, and an Access log for Static/React apps (failed and non-GET requests, since static apps run no process).
- •Secrets are automatically redacted from all log output. See “Deploy with the API” for the matching API endpoints and a troubleshooting reference.
