The Locker (service iec-locker) is where org credentials
live. A Tawa service never needs someone to copy a value out of it: you hand over a handle,
and the Locker delivers the value to the service itself.
# 1. In the portal: put the item in an ORG vault, leave it Agent-usable, bind it to your service
# 2. Copy it into your service's config, then deploy
tawa config set --secret STRIPE_API_KEY --from-locker lk://insureco/payments/stripe/api-key
tawa deploy --prod
Each item page in the portal has a Use this item panel: its handles (with copy), the services it is bound to (editors can add and remove them), copy-ready recipes for every path below, and its recent use.
lk://<org|me>/<vault>/<item>[/<field>]
/<field> when the item has one secret field. With several, name one, or the Locker
answers FIELD_AMBIGUOUS.lk://me/… is a personal item. No service can ever use one.A service can use an item only when all three hold:
| Rule | Refusal | |
|---|---|---|
| 1 | The item is in an org locker, the org the call names (for config, the service owner's org, spec.owner) | PERSONAL_NOT_RESOLVABLE, ORG_MISMATCH |
| 2 | The item is Agent-usable, not Sealed (Sealed means humans only) | ITEM_SEALED |
| 3 | The item is bound to that service by name (bindings.services) | NOT_BOUND |
A binding is the grant. Binding an item to my-api means my-api may read that item's value
at runtime. An item with no bindings resolves for nobody. The calling service's name comes from
its verified Bio-ID token, never from the request, so a service can only reach items bound to
itself. The Locker refuses a binding to a name no allowlisted client resolves as, since it could
never be used.
--from-locker)The default for a Tawa service. Full reference: config.
tawa config set --secret DB_PASSWORD --from-locker lk://insureco/carriers/paragon/password
tawa deploy --prod
POST /api/resolve, its own Bio-ID client
with the locker-resolve scope, on LOCKER_RESOLVE_CLIENTS). The plaintext goes Locker →
builder → your managed secrets. Never the CLI, a log, the deploy snapshot or Septor.--secret is required. One key per handle.tawa config list shows DB_PASSWORD=******** (locker: lk://…).For a value a service fetches when it needs it, instead of holding it in config.
POST {iec-locker}/api/resolve
Authorization: Bearer <your service's client_credentials token>
{ "handle": "lk://insureco/carriers/paragon/password", "orgSlug": "insureco" }
// or { "legacyAlias": "…", "orgSlug": "…", "key": "…" }
→ { "value": "…", "handle": "lk://…", "field": "password" } (Cache-Control: no-store)
LOCKER_RESOLVE_SERVICE_CLIENTS. A
platform operator adds it; the list fails closed. No scope is involved — locker-resolve is
the builder's.-prod / -sandbox / -uat
(iec-marketing-prod → iec-marketing). When the id does not name the service — an opaque
client_<hex> id, or a renamed service — an operator maps it in
LOCKER_RESOLVE_SERVICE_ALIASES as clientId=service[/env]. Coach, for example, resolves as
cloys-app. Bind items to that name.Jefferson (jci-mcp) has two Locker tools, and neither returns a value:
| Tool | Does |
|---|---|
locker_item_get | An item's metadata: name, handle, fields, bindings. Never values |
locker_item_bindings | { handle | itemId, add: [...], remove: [...] } — change which services an item is bound to |
They act as the signed-in person: jci-mcp sends its own service token (audience the Locker)
and the person's Bio-ID token in X-Forward-User. The Locker applies that person's vault role
and the agency's policy and records both the person and jci-mcp. Owner-tier toolset for now.
Status: rolling out — waiting on a Bio-ID configuration deploy. There is no tool that returns
a value, by design: an agent wires a binding, and the service receives the value.
| Path | How | Reference |
|---|---|---|
| JCI plane apps | secrets.locker: { orgSlug, data: [ { env: KEY, handle } ], sets: [...] } in the app's plane values; ExternalSecrets reads each handle with the app's own token | jci-plane#300 |
Server .env files | daryx ssh env pull <service> --server <server> --to /path/.env --from-locker <handle> — the item must be bound to service daryx | iec-locker#110 |
Every resolve, env render and binding change writes a UseEvent in the Locker — caller, client,
destination (service/env:KEY), outcome, refusals included — and a Septor event
(locker.item.resolved for the service door, locker.item.used for builder config). Never a value.
# ❌ copy a value out of the Locker and paste it
tawa config set --secret STRIPE_API_KEY=sk_live_...
# ✅ hand over the handle
tawa config set --secret STRIPE_API_KEY --from-locker lk://insureco/payments/stripe/api-key
# ❌ a personal handle for a service — always refused
tawa config set --secret KEY --from-locker lk://me/dev/stripe
# ❌ --from-vault for a new secret — deprecated (iec-builder#258)
tawa config set --secret KEY --from-vault some-alias
# ❌ expect a rotated item to reach a running pod
# ✅ re-run --from-locker, then tawa deploy --prod
-prod suffix. Bind the service name.config · Locker guide · Locker encryption model
Two-way threads between an insured and their agents, on the Secure Send/Request crypto and audit. Shipped dark: every /api/threads/* route is 404 until INSUREBIO_THREADS_ENABLED=true on iec-locker.
may_message (no cold outreach); the insured replies freely; the agent side adds carrier/billing participants. Anyone else gets 404.locker-threads, bound to the thread id). Subject, bodies, request titles and answers are enc:v2: envelopes with AAD (thread, record, field).document (loss run, dec page, COI, audit, W-9) is answered by the insured sharing the copy already in their Bio — the Locker asks iec-bio-api for a document grant to the agency and stores only grantId/documentId, never a copy. fields requests use #57 field definitions (no file fields yet) and are answered encrypted.thread.* events carry ids, counts and enums, never content. An agent's every send/request re-checks may_message; a refusal is audited as thread.refused.IEC_BIO_API_URL unset or iec-bio-api down → no thread, message or grant.docs/threads-api.md in iec-locker. Not yet: attachments on messages, insured-initiated threads, upload-then-share.Last updated: October 8, 2026