ontoref/reflection/migrations/0016-registry-credential-vault.ncl

130 lines
4.5 KiB
Text
Raw Normal View History

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
{
id = "0016",
slug = "registry-credential-vault",
description = "Add sops vault config to project.ncl and credential_sops fields to registry entries (ADR-017). Enables per-project multi-recipient age-encrypted credential vaults stored as OCI artifacts in ZOT, with isolated DOCKER_CONFIG for every oras invocation.",
check = {
tag = "Grep",
paths = [".ontoref/project.ncl"],
pattern = "sops",
},
instructions = m%"
## Registry credential vault (migration 0016)
Implements ADR-017: per-project sops multi-recipient vaults stored as OCI artifacts
in ZOT. Credentials are never in env vars, never in ambient docker config, never
plaintext on disk. Every oras invocation runs with an isolated DOCKER_CONFIG.
### Reference material
- **Helper contract**: `reflection/modules/secrets.nu` header docstring — defines
`resolve-vault-access` (Layer 0), `resolve-registry-credential` (Layer 2),
`assert-actor-authorized`, plus the 13 named errors callers must handle.
- **Working example**: ontoref's own `.ontoref/project.ncl` and
`.ontology/manifest.ncl` — copy this shape into your project.
- **CLI surface**: `ore secrets {bootstrap|sync|push|open|close|status|audit}` and
`ore vault {status|snapshots|check}` (P2 dispatcher wireup).
### Step 1 — Set the global master key path (one-time per developer)
In `~/.config/ontoref/config.ncl`:
vault = {
master_key_path = "/path/to/your/.kage",
backend = 'restic,
},
This becomes the default for all projects. A project may override it in its own
`.ontoref/project.ncl::sops.master_key_path` if it requires a different key.
### Step 2 — Add the sops block to .ontoref/project.ncl
Inside the `make_project { ... }` call:
sops = {
enabled = true,
vault_id = "<your-project-slug>",
vault_backend = 'restic,
registry_endpoint = "<your-zot-host>",
actor_key_bindings = {
developer = "developer",
ci = "cdci",
agent = "ontoref",
admin = "admin",
},
# master_key_path absent → resolves from ~/.config/ontoref/config.ncl::vault.master_key_path
},
### Step 3 — Add registry_provides + credential_sops to .ontology/manifest.ncl
registry_provides = m.make_registry_provides {
participant = "<your-project-slug>",
registries = m.make_registries_config {
default = "primary",
registries = [
m.make_registry_entry {
id = "primary",
endpoint = "<your-zot-host>",
role = 'primary,
tls = true,
namespaces = {
own = ["domains/<your-slug>/", "modes/<your-slug>/"],
prefixes = ["domains/<your-slug>/", "modes/<your-slug>/"],
},
credential_sops = "registry/ro.sops.yaml", # RO — relative to src-vault root
credential_sops_rw = "registry/rw.sops.yaml", # RW — relative to src-vault root
},
],
},
},
NOTE: `credential_sops` paths are relative to the src-vault root
(`~/.config/ontoref/vaults/<vault_id>/src-vault/`), NOT to the project root.
### Step 4 — Declare uses_registry in domains/modes that call oras
Any domain or mode NCL file that pushes or pulls artifacts must add:
uses_registry = "<registry-entry-id>", # id from make_registry_entry
Enables impact analysis on `ore secrets close` to find affected services.
### Step 5 — Bootstrap the vault (admin only, one-time per project)
export SOPS_AGE_RECIPIENTS="age1<developer>,age1<ci>,age1<admin>,..."
ore secrets bootstrap
This creates `~/.config/ontoref/vaults/<vault_id>/access.sops.yaml`, initializes
the local restic/kopia repo, and (next step) pushes the first src-vault artifact.
### Step 6 — Push the bootstrapped vault to ZOT
export COSIGN_KEY_PATH=/path/to/cosign.key # required by ADR-017
ore secrets push
The artifact is signed with cosign — unsigned vault pushes are rejected by the
`src-vault-cosign-signed` hard constraint.
### Step 7 — Non-admin actors: sync the vault
ore secrets sync
Pulls `src-vault/<vault_id>:latest` from ZOT and decrypts access.sops.yaml.
### Step 8 — Verify ADR-017 constraints
ore secrets audit
Runs all checks: bootstrap-credentials, no-credential-env, multi-recipient
mandatory. Failures here block downstream operations.
### Step 9 — Verify visible state
ore secrets status # vault dir + last access entry + master_key resolution
ore vault status # backend + repo presence + last snapshot
Both should report a healthy state with no errors.
"%,
}