Skip to main content

Scope references

A scope reference is how a manifest, component definition, or CLI invocation names a configuration value without embedding it: secret://acme/env:prod/STRIPE_API_KEY says which value, and the scope lattice plus the run context decide what it resolves to. References are plain strings, safe to commit, and are the only thing that ever crosses a repo boundary — the value stays server-side until a lease-bound resolve serves it.

Grammar

<scheme>://[<workspace>/][<dim>:<value>/]*<KEY>[@<version>]
PartRules
schemesecret, config, or flag — the value plane being addressed
workspaceOptional workspace segment: slug, ws_… ref, or org_… id
<dim>:<value>Zero or more named dimensions: project, env, component — each at most once, in any order (the canonical render order is project, env, component)
KEYThe key: a letter followed by up to 127 letters, digits, ., _, -
@<version>Optional version pin. Omitted means head at resolve time

flag:// references take no component dimension — a flag's runtime targeting dimensions (user, tenant, region) belong to the targeting layer, not the deploy lattice.

Examples:

secret://STRIPE_API_KEY                          # everything contextual
secret://env:prod/STRIPE_API_KEY # env pinned, rest contextual
secret://acme/project:storefront/env:prod/STRIPE_API_KEY
secret://acme/project:storefront/env:prod/STRIPE_API_KEY@3 # pinned version
config://env:stage/deploy.max_concurrency
flag://project:storefront/checkout.new_flow

Contextual omission

An omitted dimension binds to the run context at expand time. A reference in a component.yaml that names no env: resolves against whatever environment the current run targets — the same line means stage in a stage deploy and prod in a prod deploy.

This is not a wildcard. An omitted dimension never widens the lookup, never matches "any environment", and never falls through to a broader rung than the bound context implies. It is an authoring affordance: the CLI pins every dimension from the run context before the reference reaches the wire, so the server only ever sees fully-pinned references.

Fail-closed parsing

An unknown dimension name is a hard parse error, never silently ignored. This matters more than it looks: if a typo like enviroment:prod were dropped, the reference would resolve at a broader scope than the author meant — a silent widening, which is exactly the failure mode the grammar exists to prevent. The same one parser (shared between the platform and the orun CLI through a common test-vector file) also rejects duplicate dimensions, dimensions a scheme does not accept, and malformed keys or versions.

The legacy positional form

The originally-shipped grammar was a fixed positional 4-tuple:

secret://<workspace>/<project>/<env>/<KEY>[@<version>]

It is still accepted and normalized on read — references live in an immutable, content-addressed object graph, so already-published references keep their meaning (and their digest). New references should use named dimensions; positional segments cannot grow a fifth dimension without rewriting every reference ever published, which is why the grammar moved.

"Stored at" vs. "reference it as"

The console and CLI show a secret's fully-pinned address — where the row lives, e.g. secret://acme/project:storefront/env:prod/STRIPE_API_KEY. That is the storage address, not necessarily what you should paste into a manifest.

warning

Pasting a fully-pinned reference into a component.yaml pins it to that one environment: the env:prod segment overrides contextual binding, so a stage deploy of the same component resolves the prod value's scope — and typically fails or, worse, quietly serves the wrong environment's key. In shared manifests, omit the dimensions the run context should supply (secret://STRIPE_API_KEY) and let expand-time binding do its job. Fully-pinned references belong where one specific value at one specific rung is genuinely meant.