| name | c0-config |
| description | Use when changing c0 deployment configuration, runtime-editable global settings, auth providers, AI providers, MCP Context Forge, discovered registries, secret references, or open-source deployment configuration. |
c0 Config
Use this skill whenever work changes c0 configuration or the boundary between deployment-managed and runtime-managed state.
Canonical Deployment Configuration
The selected config/<stage>.config.jsonc file is the only source of truth for non-secret operator configuration. Keep configuration readable, reviewable, and shareable there rather than encoding objects in environment variables.
- Keep complete, independent
config/dev.config.jsonc, config/test.config.jsonc, config/pre.config.jsonc, and config/prod.config.jsonc files in the repository's config/ directory. Do not add cross-file inheritance, partial overrides, or profile merging.
- Keep the stage JSONC files,
config/example.config.jsonc, and config/c0.config.schema.json tracked. Ignore secret-bearing config/.env and config/.*.vars files while tracking their example templates.
- Map preview stages such as
pre-123 to config/pre.config.jsonc. Other supported stages map directly to config/<stage>.config.jsonc.
- Fail before creating Alchemy resources when the selected file does not exist or fails schema validation.
- Parse and schema-decode only the selected file once at the Alchemy deployment boundary.
- Use that same resolved object for infrastructure decisions and Worker bindings so build-time and runtime configuration cannot drift structurally.
- Keep
schemaVersion explicit and regenerate config/c0.config.schema.json when the schema changes.
- Point every stage file at the generated
config/c0.config.schema.json. Run nub run config:check after editing any stage file or its schema. Run nub run config:schema intentionally when the generated schema needs to change.
- Define the external JSON contract with Effect Schema and generate the editor schema from that same contract.
Schema.Struct is appropriate for plain JSON DTOs; use Schema.Class only when configuration values need class identity, constructors, methods, or branding.
Do not parse stage JSONC in a Worker or web request. Do not add a serialized JSON environment variable or a duplicate defaults layer.
Cloudflare Binding Boundary
Alchemy compiles the resolved config into bounded bindings:
- Pass cohesive, small server domains as native Cloudflare JSON bindings, such as
C0_CONFIG_AUTH or C0_CONFIG_MCPCF.
- Pass browser-safe values as explicit
VITE_* scalars. Never expose a secret or the full server configuration to the browser.
- Do not pass the entire configuration as one large binding. Cloudflare applies binding-count and per-binding size limits.
- Keep deployment-time binding budget checks close to the infrastructure code.
- Derive Worker env types from the Alchemy resources. Do not hand-write or cast a parallel env contract when resource inference is available.
Native JSON bindings arrive as objects. Runtime code should schema-decode them as objects and must not accept legacy JSON strings as a hidden fallback.
Secrets
Secret values never belong in a stage config file. Reference them explicitly at the field that consumes them:
{
"apiKey": {
"env": "C0_LITELLM_API_KEY"
}
}
- Secret reference names must be explicit, stable uppercase environment binding names.
- Do not derive secret names from JSON paths or KV keys.
- Only secrets explicitly marked with
"generateIfMissing": true may be generated by Alchemy.
- Generated secrets must use stable Alchemy logical ids so they persist in Alchemy state across deployments.
- A referenced active secret must resolve or deployment must fail closed. Do not silently fall back to a KV secret when the JSONC field explicitly names a missing deployment secret.
- Disabled integrations and providers should not require their otherwise-unused secrets.
config/.dev.vars, config/.test.vars, config/.pre.vars, and config/.prod.vars contain secrets and deployment credentials only, never non-secret configuration objects.
- Use
nub run config:write-stage-vars -- <stage> <path> to reconcile a stage file with the active secret references before syncing or deploying it.
- Never print secret values during validation or handoff. Names-only inspection is safe.
Deployment and Runtime Precedence
For a domain that supports Admin editing, use this precedence:
- An explicit domain in the selected stage config is deployment-managed and locked in Admin.
- If the domain is omitted, read its editable value from
C0_CONFIG KV.
- If neither exists, use the domain's documented default or unconfigured state.
The lock message must identify the active stage config location, for example config/prod.config.jsonc:aiProviders.litellm, and explain that the field must be removed from deployment configuration and redeployed before Admin can edit it.
Do not add long-lived compatibility fallbacks for old env names. Migrate callers and delete the obsolete path.
Authentication is deployment-managed because it defines which providers may establish identity. Provider kind does not grant authority: provider capabilities independently control sign-in, user provisioning, and explicit account linking. Keep implicit account linking disabled and keep provider secrets as explicit secret references.
Runtime-Owned State
Use C0_CONFIG KV for runtime-editable settings and externally discovered registries. Keep stable string keys and JSON values.
Runtime-editable setup values:
config/ai-providers/litellm
secrets/ai-providers/litellm/api-key
config/mcpcf
secrets/mcpcf/admin-api-token
Runtime-discovered registry data:
registry/ai-providers/litellm/models
registry/mcpcf/server-index
registry/mcpcf/servers/{serverId}
registry/ai-search/sources/{sourceId}
Do not put discovered catalogs or runtime-created sources in JSONC, infrastructure bindings, .vars files, GitHub variables, or stage metadata. Refresh and edit them through the owning runtime/admin workflow. Sensitive KV values must use the app-level encrypted-value envelope.
Exports from Admin should distinguish these boundaries:
- Export deployment-managed setup as a JSONC fragment plus separate secret assignments.
- Export runtime registry data as portable runtime data, not as environment overrides.
- Never export discovered registries into GitHub secrets.
Configuration Digest
Compute the deployment digest from canonicalized resolved configuration and expose it for health diagnostics, support, and cache invalidation. The digest is observability metadata, not a startup equality gate: Alchemy already uses one resolved object for infrastructure and runtime bindings.
Storage Boundaries
- Use D1 for c0-owned relational state with a schema we control, especially user-owned rows and reporting tables.
- Keep
user_mcpcf_server_configs in D1 because it is user-related relational state.
- Use
USER_WORKFLOW_KV for user-namespaced workflow kv-put and kv-get storage.
- Keep
REPOS_CACHE for repo/workflow-builder internal caches.
- Keep
WORKFLOW_SESSION_RESPONSE_CACHE for workflow session-node response caching.
- Do not add hidden D1/KV fallback paths. When state moves, provide an explicit migration and make the new source authoritative.
Change Checklist
When adding or moving a configuration field:
- Classify it as public build-time, server deployment-time, secret, runtime-editable, or runtime-discovered.
- Add it to the shared Effect schema and every complete stage file where it applies.
- Add an explicit secret reference only if the value is secret.
- Compile it through the existing Alchemy resolver into the narrowest appropriate binding.
- Update runtime precedence, Admin lock state, and export behavior where the domain is runtime-editable.
- Regenerate the JSON schema and Alchemy-derived env types when their sources change.
- Update stage secret generation/sync workflows if a new active secret name is introduced.
- Validate with
nub run config:check, focused tests, and the repository-required typecheck, lint, and format commands.