Every app needs API keys and config. None of it belongs in git, issues, or chat. This page covers the two channels the platform gives you: self-serve secrets (Forgejo → sealed → running pod), and code-declared feature flags you can flip at runtime.
App secrets: put them in Forgejo, get them as env vars
Your app repo's Settings → Actions → Secrets is the secret store: https://git.open-platform.sh/<org>/<app>/settings/actions/secrets. Add secrets under their natural names (OPENWEATHER_API_KEY, STRIPE_KEY). Each becomes an environment variable of the same name in the running app.
The sync-env workflow moves them. It runs automatically on every push to main. To run it now, open Actions → sync-env → Run workflow. The run's log prints a reconciliation table (key → scope), and the app rolls to pick up the new env.
Two conventions worth knowing:
- Multiline or awkward values: put
KEY=VALUElines inside a single secret namedAPP_ENV. Its entries merge on top of same-named secrets and win. - Reserved names are dropped:
REGISTRY_TOKEN,FORGEJO_TOKEN/GITEA_TOKEN/GITHUB_TOKEN,APP_ENVitself, and anythingPLAT_-prefixed never reach your app's env.
Prod vs dev scope: PLAT_DEV
By default, every secret is production-only. The builder never sees it while building your app. To share a secret with the builder's throwaway build environment, add a Forgejo Actions Variable (not a secret) named PLAT_DEV at https://git.open-platform.sh/<org>/<app>/settings/actions/variables. Its value is a newline- or comma-separated list of secret names.
The platform seals the listed keys into a separate dev store and injects that store into the builder's environment. The listed keys stay out of the production runtime secret. Naming a nonexistent secret in PLAT_DEV is a hard error, never a silent skip.
What happens underneath
Forgejo Actions secrets are write-only. The sync-env workflow is the one place that can read them. It POSTs them to the platform's in-cluster /ci/sync-env endpoint. The endpoint checks that the run's token has push access on this exact repo. It classifies each key, then seals the prod and dev stores as sops-encrypted Secrets in the gitops overlay. It bumps a revision annotation, which rolls your pod. The gitops audit commit records the human who triggered the sync.
What never to do
Warning: Never paste a credential into an issue, a PR comment, or an agent brief. Comments are readable by everyone with repo access and live forever in history. Secrets set through Settings → Actions → Secrets are write-only, even to you. Steer agents with secrets: set the key, and list it in PLAT_DEV if the builder needs it at build time. Never quote the value.
Feature flags: merge dark, flip later
Every app generated from the template ships a flags layer. There is no SDK and no external service. Flags live in the app's own Postgres.
Declare in code. Edit src/flags.ts in a PR:
defineFlags({
"checkout.express": {
default: false,
description: "...",
kind: "release",
retire: "2026-08-15",
},
});
Kinds are release | ops | experiment | permission. A release flag must declare a retire date. A test fails the suite once that date passes — delete the expired flag, not the test.
Toggle at runtime (~15s to land, no redeploy). These are the runtime surfaces:
| Surface | What it does |
|---|---|
GET /api/flags |
Resolved flags for the current caller |
PUT /api/flags/:key {value, scope} |
Manager toggle; scope '' (global), team:<name>, or user:<login>; value: null clears |
PUT /api/flags/:key/me {value} |
Caller's own value, for flags declared userOverridable: true |
GET /.well-known/plat/flags |
Manage-gated discovery of all definitions |
Env FLAG_<KEY> |
Emergency kill-switch; beats everything; needs a redeploy, survives DB loss |
Precedence, most operational first: env kill-switch → user override → team override → global override → preview auto-light → code default.
Previews auto-light release flags. In a PR preview environment, boolean release flags resolve on. You and the reviewer see the dark feature, while prod keeps the off default until you flip it. Explicit overrides still win.
The doctrine this enables: prefer a flag over a dependency edge. Reserve issue blocked-by edges for true cross-repo contract dependencies. Sequence features within one app by merging each dark behind a release flag, plus one "flip and retire" issue — see Building with Issues.
Two honest edges:
- The manager toggle surface requires the platform's header-trust auth mode (
PLAT_FORWARD_AUTH=1). In the default in-app OAuth mode, env kill-switches and code defaults still work, butPUT /api/flags/:keyis unavailable. - Team-scoped overrides are declared but dormant. The edge does not yet send the
X-Plat-Teamsheader, so team rows never match a caller today.
For everyone
Experience
- Build Your First App
- Building with Issues
- Dependency Trees
- Routines
- Orgs and Planning
- Preview Environments
- Connect Your Agent
Operate
Under the hood
Grow
This wiki ships inside open-platform/mitosis (wiki/) — edit it there, not here. Grown by the platform it describes.