ontoref/reflection/migrations/0018-recipient-routing.ncl
Jesús Pérez 82a358f18d
Some checks failed
Nickel Type Check / Nickel Type Checking (push) Has been cancelled
Rust CI / Security Audit (push) Has been cancelled
Rust CI / Check + Test + Lint (push) Has been cancelled
feat: #[onto_mcp_tool] catalog, OCI credential vault layer, validate ADR-018 mode hierarchy
ontoref-derive: #[onto_mcp_tool] attribute macro registers MCP tool unit-structs in
  the catalog at link time via inventory::submit!; annotated item is emitted unchanged,
  ToolBase/AsyncTool impls stay on the struct. All 34 tools migrated from manual wiring
  (net +5: ontoref_list_projects, ontoref_search, ontoref_describe,
  ontoref_list_ontology_extensions, ontoref_get_ontology_extension).

  validate modes (ADR-018): reads level_hierarchy from workflow.ncl and checks every
  .ncl mode for level declared, strategy declared, delegate chain coherent, compose
  extends valid. mode resolve <id> shows which hierarchy level handles a mode and why.
  --self-test generates synthetic fixtures in a temp dir for CI smoke-testing.

  validate run-cargo: two-step Cargo.toml resolution — workspace layout first
  (crates/<check.crate>/Cargo.toml), single-crate fallback by package name or repo
  basename. Lets the same ADR constraint shape apply to workspace and single-crate repos.

  ontology/schemas/manifest.ncl: registry_topology_type contract — multi-registry
  coordination, push targets, participant scopes, per-namespace capability.

  reflection/requirements/base.ncl: oras ≥1.2.0, cosign ≥2.0.0, sops ≥3.9.0, age
  ≥1.1.0, restic declared as Hard/Soft requirements with version_min, check_cmd, and
  install_hint (ADR-017 toolchain surface).

  ADR-019: per-file recipient routing for tenant isolation without multi-vault. Schema
  additions: sops.recipient_groups + sops.recipient_rules in ontoref-project.ncl.
  secrets-bootstrap generates .sops.yaml from project.ncl in declarative mode. Three
  new secrets-audit checks: recipient-routing-coherent, recipient-routing-coverage,
  no-multi-vault. Adoption templates: single-team/, multi-tenant/, agent-first/.
  Integration templates: domain-producer/, mode-producer/, mode-consumer/.

  UI: project_picker surfaces registry badge (⟳ participant) and vault badge
  (⛁ vault_id · N, green=declarative / amber=legacy) per project card. Expanded panel
  adds collapsible Registry section with namespace, endpoint, and push/pull capability.
  manage.html gains Runtime Services card — MCP and GraphQL toggleable without restart
  via HTMX POST /ui/manage/services/{service}/toggle.

  describe.nu: capabilities JSON includes registry_topology and vault_state per project.
  sync.nu: drift check extended to detect //! absence on newly registered crates.
  qa.ncl: six entries — credential-vault-best-practice (layered data-flow diagram),
  credential-vault-templates (paths A/B/C), credential-vault-troubleshooting (15 named
  errors), integration-what-and-why (ADR-042 OCI federation), integration-how-to-implement,
  integration-troubleshooting.

  on+re: core.ncl + manifest.ncl updated to reflect OCI, MCP, and mode-hierarchy nodes.
  Deleted stale presentation assets (2026-02 slides + voice notes).
2026-05-12 04:46:15 +01:00

136 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
"%,
}