**Grant scope (#3652).** A grant is `rw` by default — it opens both keyless `use` and `set`/`rm` on that
key. `mmi-cli vault secrets grant <repo> <login> <key> --read` grants **consume-only** reach: keyless `use`
works, rotate and remove still refuse. Only a `--read` grant may name a **wildcard** key — `*` for the
whole namespace, `<provider>/*` for one provider group — so "may read every crown jewel" is one grant
instead of forty. A wildcard never confers write; asking for one as `rw` is refused, not narrowed.
`mmi-cli oracle org access capabilities` prints `grant (read)` or `grant (rw)` next to each name.

**Windows: a multiline wrapped command must be file-backed (#6048).** `cmd.exe` truncates at the first
newline, so an inline `node -e "<multiline script>"` after `--` reaches the child truncated and fails
before your code runs. Write the script to a **project-local ignored file** (e.g. `tmp/fetch-models.mjs`
— git-ignored, never tracked) and wrap the file instead; the key is still injected into the child's env:

```bash
mmi-cli vault secrets use GEMINI_API_KEY -- node tmp/fetch-models.mjs
```

## Rotation checklist

Before rotating, enumerate every copy of the key so no tier stays stale:

```bash
mmi-cli vault secrets list --repo owner/Repo
```

Check the bare canonical plus any `dev/`, `rc/`, `main/` overrides the list shows. Rotate the provider-side
key first, then write every declared coordinate that still needs that key. For keys with a provider probe
(`RECALL_API_KEY` today), `secrets set` validates the new value before printing success; `secrets verify
<KEY>` repeats the same probe later without printing the value. If the verifier fails, treat the rotation as
incomplete even if the vault write itself succeeded.

## Step 2 — org-infra elevation (master-only)

Your own repo's full tree (dev/rc/main) is already self-serve (#2032). The **org-infra** namespaces
(`shared`/`cloudflare`/`docs`/`mmi-hub`, every tier) stay master-only. A project-admin who needs one of
those keys asks the master, who grants a **scoped, auditable** standing access to that one key (or revokes
it). These verbs are **master-only** — the backend 403s anyone else.

```bash
# MASTER: let @oguz-mut consume one _org provider key from their MM-Chat project
mmi-cli vault secrets grant mutmutco/MM-Chat oguz-mut google/SOME_SHARED_KEY
# MASTER: withdraw it
mmi-cli vault secrets revoke mutmutco/MM-Chat oguz-mut google/SOME_SHARED_KEY
```

The master can also operate the org-infra key directly while guiding (master ⊇ project-admin). Org-infra
access by a project-admin **always originates from the master** — own-project access does not.

## Coordinate — where does a new project secret belong?

- Shared across stages → bare `<KEY>` at the project slug root (the usual canonical value).
- Genuinely stage-distinct → `<stage>/<KEY>` override for `dev`, `rc`, or `main`.
- The project-admin owns both shapes across their own project. A path under `_org`/shared infrastructure is
  not a project coordinate and stays master-managed.

## Tier-to-tier copy (provider keys, #1433)

**Encryption / stage-distinct keys** (`*_ENC_KEY`, `SECRET_KEY_BASE`, etc.) must be **generated per stage** —
never copied. **Provider sandbox keys** (e.g. Recall/Gemini for Katip dev/rc) **may** be shared across dev/rc
when they point at the same sandbox project; prod should stay distinct when the provider contract requires it.

Sanctioned copy within the own-project vault (org-infra/cross-project sources remain master-gated):
```bash
mmi-cli vault secrets copy --from rc --to dev --keys RECALL_API_KEY,GEMINI_API_KEY
mmi-cli vault secrets copy --from rc --to dev --keys RECALL_API_KEY --dry-run   # plan only
```
Prefer this over manual copy piping — audit-logged, blocklist enforced.



## Detail moved from SKILL.md (verbatim)

**Authority:** a project-admin self-serves their own product repo's full vault tree (`mmi-cli oracle org access
role` → `project-admin` + `train`): stageless, `dev`, `rc`, and `main`. Do not redirect them to master for
an own-project coordinate. Only org-infra grants and cross-project administration stay master-only.

A bare `<KEY>` is the **stageless canonical** at the slug root — one value every stage shares (#2244)
and the path deploys resolve (#2523). A staged `dev/<KEY>` is a **per-stage override** and WINS for that
stage at deploy (#2522); `set`/`use` warn (names only) when an override shadows the canonical. The
`dev`/non-`dev` `tier` field in older JSON is compatibility metadata, not an authority boundary: an
own-project `main/SECRET_KEY_BASE` or `rc/DB_URL` remains project-admin self-service.

**Declare-first (#2528):** the registry catalog is the SSOT; SSM is a projection of it. A `set` to a
coordinate not declared in the catalog is **rejected** — the error names the attempted path and the fix.
Declare the key first via `mmi-cli oracle org project set <owner/repo> --secrets-file <catalog.json>` (`stages: []`
= the shared stageless canonical, `stages: ["dev"]` = a per-stage override), then retry. See what is
declared with `mmi-cli vault secrets catalog` or `secrets list`; ask for a key you lack via `mmi-cli vault secrets
request <KEY>`. There is no master bypass. `rm` of an undeclared path stays allowed, so cleanup keeps
working. Google OAuth is one stageless pair per repo: bare `GOOGLE_CLIENT_ID` + `GOOGLE_CLIENT_SECRET`
at the slug root (the staged `{dev,rc,main}/GOOGLE_*` shape is retired; `org oauth set-creds` writes the
bare keys).

```bash
# CONSUME a secret keyless — injected into the command's env, NEVER printed (raw `secrets get` was
# removed, the floor wins, #2844):
mmi-cli vault secrets use SCRAPER_API_KEY -- <cmd>

**Never** pass a value as an argument (`secrets set KEY thevalue` is wrong — there is no value arg). The
confirmation prints the **name and tier only**, never the value.

- **Never echo a value** — not in chat, a commit, a log, or an issue. No command emits a value to stdout;
  consume one keyless with `secrets use <KEY> -- <cmd>` (#2844). Treat every secret as write-once.
- Authority is **GitHub**: project-admin = repo `admin`, or repo `write`/`maintain` plus the registry `projectAdmins` entry; master =
  org owner. The backend checks both with the org App token, so the decision is server-side (your token
  scope can't widen it).
- Runtime consumes declared keys through its scoped broker token (and central deploy injects the declared
  stage set without a repo `.env`); CI reads keylessly through its OIDC role. Neither uses raw `/secrets`
  output. `secrets use <KEY>` is the safe local command-consumption path.
- Every self-service write is **attributable** — the backend logs actor + repo + KEY name (never the value).
