16 — secrets by reference (secret:// / getv://)
Credentials addressed by reference, never value. A URI/binding carries secret://keyring/svc/acct or getv://NAME; the value is resolved only in --execute, behind a deny-by-default policy, injected into the request at the executor boundary, and shown as **** on every serialized surface (registry, plan, route table, error store, result).
python3 secret_demo.py
It starts a tiny local server that validates a Bearer token without echoing it, then proves all the invariants:
registry holds only the reference (no value) : True
dry-run does not resolve / leak the secret : True
execute WITHOUT --secret-allow is denied : True
execute WITH --secret-allow runs, auth valid : True / True
the token never appears in the result : True
How it maps to the CLI
urirun connectors from-spec connector.toml | urirun compile - --out reg.json
# header in the spec: Authorization = "Bearer {getv:DEMO_TOKEN}"
export DEMO_TOKEN=... # value in env/keyring, not the binding
urirun run 'sdemo://local/auth/command/call' reg.json --execute \
--allow 'sdemo://*' --secret-allow 'getv://DEMO_TOKEN' # without --secret-allow: denied
Providers: getv://NAME & secret://env/NAME, secret://dotenv/<path>#NAME, secret://keyring/<svc>/<acct> (OS store), secret://vault/<mount>/<path>#<field> (Vault KV v2), secret://oauth/<provider>/<account> (cached token + refresh). secret://browser/... deliberately refuses (infostealer pattern; export to keyring instead). A remote urirun node serve resolves no secrets unless started with --allow-secrets.
Test
pytest test_secret_example.py -q