137 lines
7.2 KiB
Text
137 lines
7.2 KiB
Text
|
|
{
|
||
|
|
id = "0018",
|
||
|
|
slug = "recipient-routing",
|
||
|
|
description = "Adopt per-file recipient routing for tenant isolation (ADR-019). Optional: only projects requiring multi-tenant credential separation need to apply. Adds recipient_groups + recipient_rules to project.ncl::sops; bootstrap then generates <vault_dir>/.sops.yaml from declarations and sops encrypts each file with the union of declared groups.",
|
||
|
|
|
||
|
|
# Adoption is opt-in. Projects without multi-tenant needs MAY skip.
|
||
|
|
# Mark as 'applied' when the project explicitly chose a path:
|
||
|
|
# - Path A (single-team / legacy): project.ncl has sops but no recipient_groups
|
||
|
|
# - Path B/C: project.ncl declares both recipient_groups and recipient_rules
|
||
|
|
# Either path satisfies the check below.
|
||
|
|
check = {
|
||
|
|
tag = "Grep",
|
||
|
|
paths = [".ontoref/project.ncl"],
|
||
|
|
pattern = "sops",
|
||
|
|
},
|
||
|
|
|
||
|
|
instructions = m%"
|
||
|
|
## Per-file recipient routing for tenant isolation (migration 0018, ADR-019)
|
||
|
|
|
||
|
|
This migration is **OPTIONAL**. Apply only if your project requires multi-tenant
|
||
|
|
credential separation, AI-agent restricted access, or developer/admin role mixing
|
||
|
|
that the single-recipient-set model from migration 0016 cannot express.
|
||
|
|
|
||
|
|
### Decide your path
|
||
|
|
|
||
|
|
┌────────────────────────────────────────────────────────────────────┐
|
||
|
|
│ Will multiple actors (clients/agents/teams) operate this project? │
|
||
|
|
└─────────────────────────────────┬──────────────────────────────────┘
|
||
|
|
│
|
||
|
|
┌─────── No ──────┴─────── Yes ──────────────┐
|
||
|
|
▼ ▼
|
||
|
|
┌──────────────────────────┐ ┌──────────────────────────────────┐
|
||
|
|
│ Path A — Single team │ │ Will those actors need DISTINCT │
|
||
|
|
│ Migration 0016 sufficient│ │ credential sets per actor? │
|
||
|
|
│ — no further action │ └──────────────────┬───────────────┘
|
||
|
|
└──────────────────────────┘ │
|
||
|
|
┌─────── No ────────────┴──── Yes ──────┐
|
||
|
|
▼ ▼
|
||
|
|
┌─────────────────────────────────┐ ┌────────────────────────┐
|
||
|
|
│ Stay with migration 0016 │ │ Path B/C: this 0018 │
|
||
|
|
│ Re-evaluate when needs change │ │ Apply steps below │
|
||
|
|
└──────────────────────────────────┘ └────────────────────────┘
|
||
|
|
|
||
|
|
### Step 1 — Templates
|
||
|
|
|
||
|
|
Three templates ship under install/resources/templates/sops/:
|
||
|
|
|
||
|
|
single-team/ — Path A reference (no migration needed)
|
||
|
|
multi-tenant/ — Path B reference (clients/services/teams isolated)
|
||
|
|
agent-first/ — Path C reference (AI agent has narrow read-only access)
|
||
|
|
|
||
|
|
Pick one, copy its project.ncl.snippet and manifest.ncl.snippet into the
|
||
|
|
matching files in your project. Adapt placeholders to your tenancy.
|
||
|
|
|
||
|
|
### Step 2 — Declare groups and rules
|
||
|
|
|
||
|
|
Inside .ontoref/project.ncl::sops, add:
|
||
|
|
|
||
|
|
recipient_groups = {
|
||
|
|
admin = [\"age1...\"], # admins (full access)
|
||
|
|
ops = [\"age1...\"], # ops/observability
|
||
|
|
clientA = [\"age1clientA-lead...\"], # one group per client/team/agent
|
||
|
|
clientB = [\"age1clientB-lead...\"],
|
||
|
|
agents = [\"age1agent...\"], # AI agents (typically RO)
|
||
|
|
},
|
||
|
|
recipient_rules = [
|
||
|
|
{ path = \"access\\\\.sops\\\\.yaml$\", groups = [\"admin\", \"ops\"] },
|
||
|
|
{ path = \"registry/clientA-.*\\\\.sops\\\\.yaml$\", groups = [\"admin\", \"clientA\"] },
|
||
|
|
{ path = \"registry/clientB-.*\\\\.sops\\\\.yaml$\", groups = [\"admin\", \"clientB\"] },
|
||
|
|
{ path = \"registry/agent-readonly\\\\.sops\\\\.yaml$\", groups = [\"admin\", \"agents\"] },
|
||
|
|
],
|
||
|
|
|
||
|
|
### Step 3 — Add per-tenant RegistryEntry items in manifest.ncl
|
||
|
|
|
||
|
|
For each rule, declare a corresponding RegistryEntry whose credential_sops
|
||
|
|
matches the rule's path pattern:
|
||
|
|
|
||
|
|
m.make_registry_entry {
|
||
|
|
id = \"clientA\",
|
||
|
|
endpoint = \"<your-zot-host>\",
|
||
|
|
credential_sops = \"registry/clientA-ro.sops.yaml\",
|
||
|
|
credential_sops_rw = \"registry/clientA-rw.sops.yaml\",
|
||
|
|
namespaces = {
|
||
|
|
own = [\"domains/clientA/\", \"modes/clientA/\"],
|
||
|
|
prefixes = [\"domains/clientA/\"],
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
### Step 4 — Bootstrap or migrate
|
||
|
|
|
||
|
|
NEW PROJECT ore secrets bootstrap
|
||
|
|
(generates <vault_dir>/.sops.yaml + access.sops.yaml,
|
||
|
|
skips default registry/ro|rw.sops.yaml — operator
|
||
|
|
populates per-tenant files via 'ore secrets open')
|
||
|
|
|
||
|
|
EXISTING PROJECT ore secrets rekey
|
||
|
|
(regenerates <vault_dir>/.sops.yaml from new declarations,
|
||
|
|
re-encrypts every existing *.sops.yaml with the matching rule)
|
||
|
|
|
||
|
|
### Step 5 — Verify
|
||
|
|
|
||
|
|
ore secrets describe # see groups, rules, recipients per file
|
||
|
|
ore secrets audit # 6 checks pass:
|
||
|
|
# bootstrap-credentials OK
|
||
|
|
# no-credential-env OK
|
||
|
|
# multi-recipient-mandatory OK
|
||
|
|
# recipient-routing-coherent OK
|
||
|
|
# recipient-routing-coverage OK
|
||
|
|
# no-multi-vault OK
|
||
|
|
|
||
|
|
### What does NOT change
|
||
|
|
|
||
|
|
- vault_id remains one per project (multi-vault is explicitly out of scope)
|
||
|
|
- Master .kage continues per-developer
|
||
|
|
- Restic/Kopia repo and OCI artifact remain unique per project
|
||
|
|
- Vault lock semantics unchanged
|
||
|
|
- All ADR-017 invariants preserved
|
||
|
|
|
||
|
|
### When to escalate to multi-vault (not implemented)
|
||
|
|
|
||
|
|
- Hard isolation: separate master keys per environment (prod vs staging)
|
||
|
|
- Hard isolation: separate restic repos to prevent any cross-pull visibility
|
||
|
|
- Compliance grade: regulator requires environments cannot share vault
|
||
|
|
contents even when encrypted
|
||
|
|
|
||
|
|
When such a case appears, capture it in a new ADR superseding/extending
|
||
|
|
ADR-019 before adopting multi-vault.
|
||
|
|
|
||
|
|
### References
|
||
|
|
|
||
|
|
ADR-019 decision + 6 checkable constraints
|
||
|
|
reflection/qa.ncl::credential-vault-templates three-path overview
|
||
|
|
install/resources/templates/sops/ ready-to-copy snippets
|
||
|
|
reflection/migrations/0016 Layer 0/1/2 prerequisites
|
||
|
|
"%,
|
||
|
|
}
|