MASON-WP-0004-T02: draft custody descriptions and a metadata-only apply tool
custody-describe.py drafts the five custody fields from CCRs, the routing catalog and the owner proposals (no OpenBao access), and applies accepted entries with bao kv metadata patch only, silently. 29/38 drafts complete; gaps left empty for owners. 4 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 327203@bnt-lap001 Assistant-Session: 55028ae3-4914-4bd1-8f5c-815f3527aef0
This commit is contained in:
parent
ccd8e62eab
commit
6f529413c8
4 changed files with 739 additions and 1 deletions
528
manifests/custody-descriptions.yaml
Normal file
528
manifests/custody-descriptions.yaml
Normal file
|
|
@ -0,0 +1,528 @@
|
|||
operators/nextcloud:
|
||||
description: ''
|
||||
missing:
|
||||
- description
|
||||
- used_by
|
||||
- rotation
|
||||
- on_loss
|
||||
on_loss: ''
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: ''
|
||||
sources: []
|
||||
used_by: ''
|
||||
operators/nextcloud/backup:
|
||||
description: ''
|
||||
missing:
|
||||
- description
|
||||
- used_by
|
||||
- rotation
|
||||
- on_loss
|
||||
on_loss: ''
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: ''
|
||||
sources: []
|
||||
used_by: ''
|
||||
platform/workloads/activity-core/llm-connect/llm-connect-provider-secrets:
|
||||
description: llm-connect provider access through OpenBao workload KV and External Secrets
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Replace OPENROUTER_API_KEY directly in OpenBao and record non-secret
|
||||
rotation evidence.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0003-llm-connect-openrouter-api-key.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0003-llm-connect-openrouter-api-key.yaml
|
||||
- ops-warden catalog openrouter-llm-connect
|
||||
used_by: ExternalSecret to Secret llm-connect-provider-secrets in the activity-core namespace
|
||||
platform/workloads/activity-core/ops-run-workers/rein-aharness-metered-railiance01:
|
||||
description: Deliver the bearer token of ops_run queue worker rein-aharness-metered@railiance01 to the
|
||||
activity-core runtime Secret through External Secrets.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: activity-core re-mints the worker token at its path. ESO syncs it within
|
||||
1h, and the queue accepts the new token per ACTIVITY_CORE_WORKERS.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0030-activity-core-ops-run-worker-rein-aharness-metered-railiance01.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0030-activity-core-ops-run-worker-rein-aharness-metered-railiance01.yaml
|
||||
used_by: ExternalSecret actcore-ops-run-worker-tokens merges ACTIVITY_CORE_WORKER_TOKEN_METERED into
|
||||
Secret actcore-runtime-secret in namespace activity-core.
|
||||
platform/workloads/activity-core/ops-run-workers/rein-aharness-railiance01:
|
||||
description: Deliver the bearer token of ops_run queue worker rein-aharness@railiance01 to the activity-core
|
||||
runtime Secret through External Secrets.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: activity-core re-mints the worker token at its path. ESO syncs it within
|
||||
1h, and the queue accepts the new token per ACTIVITY_CORE_WORKERS.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0029-activity-core-ops-run-worker-rein-aharness-railiance01.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0029-activity-core-ops-run-worker-rein-aharness-railiance01.yaml
|
||||
used_by: ExternalSecret actcore-ops-run-worker-tokens merges ACTIVITY_CORE_WORKER_TOKEN into Secret
|
||||
actcore-runtime-secret in namespace activity-core.
|
||||
platform/workloads/agent-harness/forgejo-deploy-key:
|
||||
description: agent-harness Forgejo deploy key (write sandbox; binky-control at cutover)
|
||||
missing:
|
||||
- used_by
|
||||
on_loss: 're-establish by railiance-platform: On railiance01 generate a new ed25519 keypair under ~/.local/agent-harness/ssh/
|
||||
(mode 600); do not paste the private key into chat or Git.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: ops-warden/wiki/playbooks/agent-harness-secrets.md#lane-2-forgejo-deploy-key
|
||||
sources:
|
||||
- ops-warden catalog agent-harness-forgejo-deploy
|
||||
used_by: ''
|
||||
platform/workloads/approval-engine/audit-sender:
|
||||
description: Provide only the approval-engine sender with source=approval-engine, tenants=[tenant:platform],
|
||||
may_write=true, may_read=false, evidence_kind=load-bearing, secret_policy=redact.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Reviewed overlap-first rotation: append a replacement to this sender
|
||||
only, deliver it, prove acceptance, then remove the predecessor and prove refusal. The first-provision
|
||||
helper refuses rotation and unexpected existing values.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0021-approval-engine-audit.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0021-approval-engine-audit.yaml
|
||||
used_by: 'ClusterSecretStore openbao-approval-engine-audit restricted to namespace approval-engine;
|
||||
ExternalSecret and Secret approval-engine/approval-engine-audit, key audit-token. Source: manifests/factory-audit-senders.yaml.
|
||||
A missing namespace stays a workload-owner prerequisite; this packet creates none.'
|
||||
platform/workloads/approval-engine/operator-client:
|
||||
description: Hold the approval-engine-operator confidential client secret in platform custody and project
|
||||
it into the KeyCape runtime so KeyCape can verify presented client_credentials for the operator client.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: KeyCape mints a replacement client secret; platform writes the new
|
||||
KV version under the same attended authority. Rotation is independent of CCR-2026-0017.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0018-keycape-approval-engine-operator-client.yaml#lifecycle.rotate;
|
||||
railiance-platform/credential-change-requests/CCR-2026-0020-approval-engine-operator-client-read.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0018-keycape-approval-engine-operator-client.yaml
|
||||
- CCR-2026-0020-approval-engine-operator-client-read.yaml
|
||||
used_by: 'ClusterSecretStore openbao-keycape-approval-engine-operator, limited to namespace sso, to
|
||||
ExternalSecret sso/keycape-approval-engine-operator-client and Secret sso/keycape-approval-engine-operator-client
|
||||
with key client-secret. KeyCape resolves it as KEYCAPE_APPROVAL_ENGINE_OPERATOR_CLIENT_SECRET through
|
||||
a secretKeyRef. Manifests: argocd/platform-addons/openbao-secretstore/openbao-keycape-approval-clients.clustersecretstore.yaml
|
||||
and keycape-approval-clients.externalsecrets.yaml.'
|
||||
platform/workloads/audit-core/senders:
|
||||
description: audit-core sender registry (write and operator-read tokens)
|
||||
missing: []
|
||||
on_loss: audit-core re-mints sender tokens and every projected copy is reseeded; senders queue to their
|
||||
local outboxes meanwhile. See ops-mason/plans/audit-core-openbao-runtime-custody.md.
|
||||
owner: ops-mason
|
||||
review: draft
|
||||
rotation: ops-warden/wiki/playbooks/audit-core-senders.md#worker-checklist
|
||||
sources:
|
||||
- ops-warden catalog audit-core-senders
|
||||
- ops-mason plan (hand-filled 2026-10-09)
|
||||
used_by: audit-core on railiance01 (sender registry authority); seed source for per-sender projections
|
||||
such as tenant-engine/audit-core-sender
|
||||
platform/workloads/core-hub/forgejo-source-read:
|
||||
description: Hold a dedicated Forgejo PAT scoped exactly read:repository and project it through External
|
||||
Secrets into namespace core-hub as the Secret that the rapp-core-hub publisher mounts read-only at
|
||||
/var/run/secrets/repo-manager/forgejo/token.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Every 180 days or on owner change. Mint a replacement read:repository
|
||||
PAT for core-hub-source, write it to OpenBao from stdin, let ESO refresh (5m), pass make verify-publisher,
|
||||
then revoke the predecessor PAT and record evidence.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0031-core-hub-forgejo-source-read.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0031-core-hub-forgejo-source-read.yaml
|
||||
- ops-warden catalog core-hub-forgejo-source-read
|
||||
used_by: ClusterSecretStore openbao-core-hub-forgejo-source (conditions limited to namespace core-hub)
|
||||
feeding ExternalSecret core-hub/repo-manager-forgejo-source, which materializes Secret repo-manager-forgejo-source
|
||||
with the single key token (remoteRef workloads/core-hub/forgejo-source-read property FORGEJO_SOURCE_TOKEN),
|
||||
explicit empty target metadata template, creationPolicy Owner, deletionPolicy Retain. rapp-core-hub
|
||||
then sets publisher.existingSecret to repo-manager-forgejo-source with tokenKey token. Both ma…
|
||||
platform/workloads/core-hub/runtime:
|
||||
description: Project the existing Core Hub protected-API token into the private railiance01 runtime
|
||||
through a namespace-limited ExternalSecret while preserving the token across the relocation window.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Coordinate a new random token with all protected API consumers, update
|
||||
the OpenBao field through attended custody, wait for ESO sync, roll Core Hub and consumers, verify
|
||||
protected calls, then invalidate the old token.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0013-core-hub-runtime-api-token.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0013-core-hub-runtime-api-token.yaml
|
||||
used_by: ClusterSecretStore openbao-core-hub-runtime, limited to namespace core-hub, to ExternalSecret
|
||||
core-hub/core-hub-api-token and Secret core-hub-api-token. The rapp package consumes this Secret separately
|
||||
from runtime and migration PostgreSQL lease Secrets.
|
||||
platform/workloads/coulomb-social/backup-primary:
|
||||
description: Read this application backup publisher key pair into the admitted node delivery process;
|
||||
no other workload credential or escrow.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Separate owner-approved CAS rotation and atomic host replacement contract
|
||||
required. This helper refuses different existing bytes; retain old provider identity until new publication/recovery
|
||||
acceptance, then revoke predecessor at provider.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0036-coulomb-social-backup-primary.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0036-coulomb-social-backup-primary.yaml
|
||||
used_by: /etc/coulomb-social-backup/primary.env
|
||||
platform/workloads/coulomb/whynot-design/npm-publish:
|
||||
description: npm package publishing through ops-warden caller-scoped fetch/exec
|
||||
missing:
|
||||
- used_by
|
||||
on_loss: 'Re-issue per rotation: Replace NPM_AUTH_TOKEN value directly in OpenBao and record non-secret
|
||||
rotation evidence.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0001-whynot-design-npm-publish.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0001-whynot-design-npm-publish.yaml
|
||||
- ops-warden catalog whynot-design-npm-publish
|
||||
used_by: ''
|
||||
platform/workloads/email-connect/backup-primary:
|
||||
description: Read this application backup publisher key pair into the admitted node delivery process;
|
||||
no other workload credential or escrow.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Separate owner-approved CAS rotation and atomic host replacement contract
|
||||
required. This helper refuses different existing bytes; retain old provider identity until new publication/recovery
|
||||
acceptance, then revoke predecessor at provider.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0037-email-connect-backup-primary.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0037-email-connect-backup-primary.yaml
|
||||
used_by: /etc/email-connect-backup/primary.env
|
||||
platform/workloads/email-connect/transactional:
|
||||
description: Deliver EMAIL_CONNECT_INGEST_TOKEN and IONOS SMTP username/password into the email-connect
|
||||
namespace via External Secrets so the transactional receiver can send invitation and verification
|
||||
mail without baking secrets into the image, Git, or user-engine.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Overlap-first: put new SMTP password and/or ingest token in OpenBao;
|
||||
wait for ESO refresh (or force sync); roll email-connect and user-engine as needed; revoke old provider
|
||||
password / retire old bearer.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0010-email-connect-transactional.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0010-email-connect-transactional.yaml
|
||||
- ops-warden catalog email-connect-transactional
|
||||
used_by: ClusterSecretStore openbao-email-connect (tokenSecretRef openbao-email-connect-eso-token; namespace
|
||||
condition email-connect) at railiance-platform/argocd/platform-addons/openbao-secretstore/openbao-email-connect.clustersecretstore.yaml
|
||||
→ ExternalSecret email-connect/email-connect-runtime → Secret email-connect-runtime. Package manifests
|
||||
in email-connect/deploy/k8s/railiance/ (not applied until this CCR is approved and the KV secret is
|
||||
provisioned).
|
||||
platform/workloads/forgejo/forgejo-admin:
|
||||
description: Forgejo admin API token for package prune, registry ops, org webhooks, and operator bootstrap
|
||||
on workstations and activity-core worker
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Mint a new PAT in Forgejo, update API_TOKEN in OpenBao, record non-secret
|
||||
rotation evidence, and restart dependent automation after downstream consumers load from OpenBao.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0006-forgejo-admin-api-token-lane.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0006-forgejo-admin-api-token-lane.yaml
|
||||
- ops-warden catalog forgejo-admin-api-token
|
||||
used_by: railiance-platform tools/cmd/forgejo-package-prune, activity-core weekly-forgejo-package-prune
|
||||
shell resolver, and railiance-apps Forgejo tools (load_token pattern after lane verified)
|
||||
platform/workloads/glas-harness/claude-agent-dev:
|
||||
description: Dedicated Claude Code inference for the Glas local agent-dev profile, delivered by the
|
||||
sandbox owner.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Create a replacement provider key, write with expected-version CAS,
|
||||
stop old runs, verify new delivery and inference, then revoke the predecessor at Anthropic and prove
|
||||
its denial.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0016-glas-claude-anthropic.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0016-glas-claude-anthropic.yaml
|
||||
used_by: Sand-boxer delivers ANTHROPIC_API_KEY only to the selected Claude workload; neither the Glas
|
||||
caller nor the runtime receives an OpenBao token.
|
||||
platform/workloads/inbound-library/imap:
|
||||
description: inbound-library AppRole for non-interactive library@coulomb.social IMAP read
|
||||
missing: []
|
||||
on_loss: 'rotate by ops-mason: bao write -f auth/approle/role/inbound-library/secret-id -> new secret_id;
|
||||
deliver mode-0600 to ~/.local/inbound-library/approle; shred old secret_id file.'
|
||||
owner: ops-mason
|
||||
review: draft
|
||||
rotation: ops-warden/wiki/playbooks/inbound-library-imap-approle.md#worker-checklist
|
||||
sources:
|
||||
- ops-warden catalog inbound-library-imap-approle
|
||||
- ops-mason plan (hand-filled 2026-10-09)
|
||||
used_by: inbound-library systemd user timer on the workstation, via AppRole inbound-library (reads once
|
||||
per run, then revokes its token)
|
||||
platform/workloads/informed-decision/audit-sender:
|
||||
description: Provide only the informed-decision sender with source=informed-decision, tenants=[tenant:platform],
|
||||
may_write=true, may_read=false, evidence_kind=load-bearing, secret_policy=redact.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Reviewed overlap-first rotation: append a replacement to this sender
|
||||
only, deliver it, prove acceptance, then remove the predecessor and prove refusal. The first-provision
|
||||
helper refuses rotation and unexpected existing values.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0022-informed-decision-audit.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0022-informed-decision-audit.yaml
|
||||
used_by: 'ClusterSecretStore openbao-informed-decision-audit restricted to namespace informed-decision;
|
||||
ExternalSecret and Secret informed-decision/informed-decision-audit, key token. Source: manifests/factory-audit-senders.yaml.
|
||||
A missing namespace stays a workload-owner prerequisite; this packet creates none.'
|
||||
platform/workloads/informed-decision/sitting-requester:
|
||||
description: create-only sitting requester KeyCape verifier custody; approval:create only; subject informed-decision;
|
||||
audience approval-engine.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Rotate through KeyCape and platform using a new version with predecessor
|
||||
refusal proof.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0026-informed-decision-sitting-requester-verifier.yaml#lifecycle.rotate;
|
||||
railiance-platform/credential-change-requests/CCR-2026-0027-informed-decision-sitting-requester-reader.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0026-informed-decision-sitting-requester-verifier.yaml
|
||||
- CCR-2026-0027-informed-decision-sitting-requester-reader.yaml
|
||||
- CCR-2026-0032.yaml
|
||||
used_by: sso/keycape-informed-decision-sitting-requester-client via ESO; env KEYCAPE_INFORMED_DECISION_SITTING_REQUESTER_CLIENT_SECRET
|
||||
platform/workloads/issue-core/issue-core/issue-core-runtime:
|
||||
description: issue-core runtime ingestion through OpenBao workload KV and External Secrets
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Replace issue-core runtime secret values directly in OpenBao and record
|
||||
non-secret rotation evidence.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0002-issue-core-ingestion-api-key.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0002-issue-core-ingestion-api-key.yaml
|
||||
- ops-warden catalog issue-core-ingestion-api-key
|
||||
used_by: ExternalSecret issue-core/issue-core-runtime -> Secret issue-core-runtime in the issue-core
|
||||
namespace
|
||||
platform/workloads/keycape/secrets-engine-openbao:
|
||||
description: Hold the secrets-engine-openbao client secret authoritatively in platform KV and deliver
|
||||
it to the KeyCape issuer through sso-only ESO
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Roll-forward only: custody child writes a new version with cas=<current>
|
||||
under a standing rotation approval for this CCR (event-driven + 90d); never a read or restore of an
|
||||
old version'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0033.yaml#lifecycle.rotate; railiance-platform/credential-change-requests/CCR-2026-0034.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0033.yaml
|
||||
- CCR-2026-0034.yaml
|
||||
used_by: sso/keycape-secrets-engine-openbao-client key client-secret; issuer-side delivery only
|
||||
platform/workloads/net-kingdom/keycape-factor-issuer:
|
||||
description: ''
|
||||
missing:
|
||||
- description
|
||||
- used_by
|
||||
- rotation
|
||||
- on_loss
|
||||
on_loss: ''
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: ''
|
||||
sources: []
|
||||
used_by: ''
|
||||
platform/workloads/net-kingdom/keycape-factor-read:
|
||||
description: Deliver only a provider-issued coulomb factor-read JWT to KeyCape, separating it from issuer
|
||||
credentials and signing keys.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Issue replacement before expiry, CAS update exact KV, verify ESO projection
|
||||
and consumer acceptance; retain no plaintext artifacts.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0023-keycape-factor-read.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0023-keycape-factor-read.yaml
|
||||
used_by: Namespace-restricted ClusterSecretStore openbao-keycape-factor-read -> sso/keycape-factor-read
|
||||
Secret admin-token. Mount only the JWT in KeyCape; provider password remains in separate custody.
|
||||
platform/workloads/policy-nexus/forgejo-source-read:
|
||||
description: Hold a dedicated Forgejo PAT with read:repository only and deliver it as the FORGEJO_SOURCE_TOKEN
|
||||
secret to the policy-nexus Actions workflow.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Mint a replacement read:repository-only PAT, update OpenBao and the
|
||||
Actions secret through attended custody, pass one candidate build, then revoke the predecessor.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0014-policy-nexus-forgejo-source-read.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0014-policy-nexus-forgejo-source-read.yaml
|
||||
- ops-warden catalog policy-nexus-forgejo-source-read
|
||||
used_by: Repository Actions secret FORGEJO_SOURCE_TOKEN on coulomb/policy-nexus. Delivery is attended
|
||||
and must not expose the value in command output, process arguments, Git, State Hub, or workflow logs.
|
||||
platform/workloads/railiance-clock/authority-signing:
|
||||
description: Hold one ES256 authority private signing key in OpenBao and deliver it only to the private
|
||||
railiance01 Railiance Clock service; no public listener or consumer-wide distribution.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Stop authority, perform separately versioned attended CAS rotation
|
||||
and atomic host replacement, then admit new public key and epoch for consumers.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0028-railiance-clock-authority-signing.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0028-railiance-clock-authority-signing.yaml
|
||||
used_by: /var/lib/railiance-clock/signing.pem
|
||||
platform/workloads/railiance-telemetry/smtp:
|
||||
description: Native password-reset notifier; password mounted read-only under /run/secrets/authelia-smtp/password.
|
||||
missing:
|
||||
- used_by
|
||||
- on_loss
|
||||
on_loss: ''
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0035-authelia-platform-smtp.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0035-authelia-platform-smtp.yaml
|
||||
used_by: ''
|
||||
platform/workloads/railiance/backup/object-storage:
|
||||
description: Object-store credentials for Railiance backup destinations.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Overlap-first. Put a new key in OpenBao, wait for ESO refresh, revoke
|
||||
the old Scaleway key.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0012-backup-object-storage.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0012-backup-object-storage.yaml
|
||||
used_by: 'ClusterSecretStore openbao-backup-object-storage (first namespace condition: databases) →
|
||||
ExternalSecret databases/platform-pg-backup-s3 → Secret platform-pg-backup-s3 (ACCESS_KEY_ID / ACCESS_SECRET_KEY
|
||||
projected from ACCESS_KEY / SECRET_KEY). Drafts live under railiance-platform/argocd/platform-addons/openbao-secretstore/
|
||||
and rapp-postgres/helm/platform-pg-backup-s3.externalsecret.yaml. Do not apply until this CCR is approved
|
||||
and the KV values are real.'
|
||||
platform/workloads/railiance/backup/offsite-lane:
|
||||
description: age-encrypted offsite backup upload through Nextcloud WebDAV file drop
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Mint a new Nextcloud upload token, update NC_WEBDAV_TOKEN in OpenBao,
|
||||
and record non-secret rotation evidence.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0004-railiance-backup-offsite-lane.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0004-railiance-backup-offsite-lane.yaml
|
||||
- ops-warden catalog railiance-backup-offsite-lane
|
||||
used_by: railiance-platform lib/railiance-backup-common.sh and forgejo-backup via RAILIANCE_BACKUP_NC_TOKEN
|
||||
env (fetched through OpenBao or warden access)
|
||||
platform/workloads/railiance/scaleway/bootstrap:
|
||||
description: Create reef-storage object-store members; not the Barman runtime key
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Put a new bootstrap key; do not reuse the scoped backup key.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0011-scaleway-object-storage-bootstrap.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0011-scaleway-object-storage-bootstrap.yaml
|
||||
- ops-warden catalog scaleway-bootstrap
|
||||
used_by: reef-storage/tools/create-platform-audit-bucket.sh (reads, never prints)
|
||||
platform/workloads/rapp-qonto/keycape-client:
|
||||
description: rapp-qonto KeyCape workload client
|
||||
missing:
|
||||
- used_by
|
||||
on_loss: 'rotate by key-cape: Generate a fresh newline-free secret through an approved execution transport;
|
||||
never print it. NOT automatable — no admitted execution and rollback contract (see delegation.blocked_on).'
|
||||
owner: key-cape
|
||||
review: draft
|
||||
rotation: ops-warden/wiki/CredentialRouting.md#routing-catalog-index
|
||||
sources:
|
||||
- ops-warden catalog rapp-qonto-keycape-client
|
||||
used_by: ''
|
||||
platform/workloads/reuse/reuse-surface/runtime-secrets:
|
||||
description: reuse-surface federation hub runtime secrets through OpenBao workload KV and External Secrets
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Replace field values directly in OpenBao, wait for ESO refresh, rollout
|
||||
reuse-surface, then run make reuse-forgejo-webhook when rotating the webhook HMAC.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0005-reuse-surface-runtime-secrets-lane.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0005-reuse-surface-runtime-secrets-lane.yaml
|
||||
- ops-warden catalog reuse-surface-hub-write-token
|
||||
used_by: ExternalSecret reuse/reuse-surface-runtime -> Secret reuse-surface-env; ClusterSecretStore
|
||||
openbao-reuse uses private railiance01 OpenBao and exact reuse/reuse-surface-eso Kubernetes identity.
|
||||
Caller-facing OIDC policy workload-kv-read-reuse-surface-runtime remains separately retained.
|
||||
platform/workloads/secrets-engine/approval-client:
|
||||
description: Hold the secrets-engine-approval confidential client secret in platform custody and project
|
||||
it into the KeyCape runtime so KeyCape can verify presented client_credentials without the value living
|
||||
in Git, a chart value, or a hand-created Kubernetes Secret.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: KeyCape mints a replacement client secret; platform writes the new
|
||||
KV version under the same attended authority; ESO refresh delivers it and KeyCape restarts or re-reads.
|
||||
Rotation is independent of the operator client.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0017-keycape-secrets-engine-approval-client.yaml#lifecycle.rotate;
|
||||
railiance-platform/credential-change-requests/CCR-2026-0019-secrets-engine-approval-client-read.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0017-keycape-secrets-engine-approval-client.yaml
|
||||
- CCR-2026-0019-secrets-engine-approval-client-read.yaml
|
||||
used_by: 'ClusterSecretStore openbao-keycape-secrets-engine-approval, limited to namespace sso, to ExternalSecret
|
||||
sso/keycape-secrets-engine-approval-client and Secret sso/keycape-secrets-engine-approval-client with
|
||||
key client-secret. KeyCape resolves it as KEYCAPE_SECRETS_ENGINE_APPROVAL_CLIENT_SECRET through a
|
||||
secretKeyRef, matching the live KEYCAPE_RAPP_QONTO_CLIENT_SECRET shape observed on image main-153258b.
|
||||
Manifests: argocd/platform-addons/openbao-secretstore/openbao-keycape-approval-clients.clustersecretstor…'
|
||||
platform/workloads/secrets-engine/approval-requester:
|
||||
description: T03 create-only requester KeyCape verifier custody; execute only the three fixed T03 request
|
||||
records.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Rotate through KeyCape and platform using a new version with predecessor
|
||||
refusal proof.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0024-t03-requester-verifier.yaml#lifecycle.rotate;
|
||||
railiance-platform/credential-change-requests/CCR-2026-0025-t03-requester-reader.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0024-t03-requester-verifier.yaml
|
||||
- CCR-2026-0025-t03-requester-reader.yaml
|
||||
used_by: sso/keycape-secrets-engine-requester-client via ESO; env KEYCAPE_SECRETS_ENGINE_REQUESTER_CLIENT_SECRET
|
||||
platform/workloads/state-hub/repository-rename-preflight:
|
||||
description: Deliver one HMAC signing key to the transitional State Hub API; no rename execution, general
|
||||
credential vending, or second identity authority.
|
||||
missing: []
|
||||
on_loss: 'Re-issue per rotation: Stop every API replica and verify no matching pods before CAS rotation;
|
||||
wait ESO, recreate API replicas with one version, verify old token invalidation and health.'
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: railiance-platform/credential-change-requests/CCR-2026-0015-state-hub-preflight-signing.yaml#lifecycle.rotate
|
||||
sources:
|
||||
- CCR-2026-0015-state-hub-preflight-signing.yaml
|
||||
used_by: Namespace SecretStore openbao-state-hub-rename-preflight -> ExternalSecret/Secret state-hub-rename-preflight;
|
||||
required explicit API-only secretKeyRef. Owner creation, Retain deletion policy.
|
||||
platform/workloads/telemetry/grafana-admin:
|
||||
description: ''
|
||||
missing:
|
||||
- description
|
||||
- used_by
|
||||
- rotation
|
||||
- on_loss
|
||||
on_loss: ''
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: ''
|
||||
sources: []
|
||||
used_by: ''
|
||||
platform/workloads/tenant-engine/audit-core-sender:
|
||||
description: tenant-engine's audit-core sender token, a copy of its entry in the audit-core sender registry.
|
||||
Not the registry itself.
|
||||
missing: []
|
||||
on_loss: Reseed from platform/workloads/audit-core/senders with the seed helper (founder, attended).
|
||||
audit-core re-mints if the registry entry is also gone; emission waits in tenant-engine's local outbox
|
||||
meanwhile.
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: ops-mason/plans/tenant-engine-audit-core-sender-projection.md; reseed with railiance-platform/scripts/tenant_engine_audit_core_sender_seed.py
|
||||
sources:
|
||||
- ops-mason plan (hand-filled 2026-10-09)
|
||||
used_by: ESO ClusterSecretStore openbao-tenant-engine-audit-core-sender (k8s role tenant-engine-audit-core-sender-eso)
|
||||
-> Secret tenant-engine/tenant-engine-audit-core-sender key token
|
||||
platform/workloads/user-engine/runtime:
|
||||
description: 'user-engine runtime secrets: the portal edge marker and the identity-provisioner service
|
||||
token. Not a user credential.'
|
||||
missing:
|
||||
- on_loss
|
||||
on_loss: ''
|
||||
owner: railiance-platform
|
||||
review: draft
|
||||
rotation: 'ops-mason/plans/user-engine-openbao-runtime-custody.md (rows 8-9: ExternalSecret is the sole
|
||||
writer and rotation path)'
|
||||
sources:
|
||||
- ops-mason plan (hand-filled 2026-10-09)
|
||||
used_by: user-engine and identity-provisioner via External Secrets Operator
|
||||
152
scripts/custody-describe.py
Executable file
152
scripts/custody-describe.py
Executable file
|
|
@ -0,0 +1,152 @@
|
|||
#!/usr/bin/env python3
|
||||
"""custody-describe — draft and apply the five custody fields (MASON-WP-0004-T02).
|
||||
|
||||
./scripts/custody-describe.py draft > manifests/custody-descriptions.yaml
|
||||
./scripts/custody-describe.py check manifests/custody-descriptions.yaml
|
||||
./scripts/custody-describe.py apply manifests/custody-descriptions.yaml # founder
|
||||
|
||||
`draft` reads only documents: railiance-platform CCRs, the ops-warden routing
|
||||
catalog and the owner proposals. It never calls OpenBao. Every entry it writes
|
||||
is `review: draft`; a field it cannot source stays empty rather than guessed,
|
||||
because a confidently wrong `used_by` is worse than an empty one.
|
||||
|
||||
`apply` writes custom_metadata only (`bao kv metadata patch`), for entries
|
||||
marked `review: accepted` with all five fields filled. It never reads or writes
|
||||
a value, prints nothing but paths and booleans, and runs under the founder's
|
||||
admin session (warden access openbao-platform-admin-login --exec), since
|
||||
ops-mason-survey cannot write metadata.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import yaml
|
||||
|
||||
REQUIRED = ("description", "owner", "used_by", "rotation", "on_loss")
|
||||
MAX_VALUE = 512 # OpenBao custom_metadata value limit
|
||||
HOME = os.path.expanduser("~")
|
||||
CCR_DIR = f"{HOME}/railiance-platform/credential-change-requests"
|
||||
CATALOG = f"{HOME}/ops-warden/registry/routing/catalog.yaml"
|
||||
PROPOSALS = os.path.join(os.path.dirname(__file__), "..", "docs", "credential-owner-proposals-2026-10-09.md")
|
||||
|
||||
|
||||
def _clip(text: str | None) -> str:
|
||||
text = " ".join(str(text or "").split())
|
||||
return text if len(text) <= MAX_VALUE else text[: MAX_VALUE - 1] + "…"
|
||||
|
||||
|
||||
def _first_sentence(text: str | None) -> str:
|
||||
text = " ".join(str(text or "").split())
|
||||
m = re.match(r"(.+?\.)(\s|$)", text)
|
||||
return _clip(m.group(1) if m else text)
|
||||
|
||||
|
||||
def _proposals() -> dict[str, str]:
|
||||
"""Path -> proposed owner, from section C of the owner proposals."""
|
||||
owners = {}
|
||||
for line in open(PROPOSALS):
|
||||
m = re.match(r"\| `([^`]+)` \| [^|]+ \| ([a-z0-9-]+) \(consumer", line)
|
||||
if m:
|
||||
owners[m.group(1)] = m.group(2)
|
||||
return owners
|
||||
|
||||
|
||||
def _ccrs() -> dict[str, list[tuple[str, dict]]]:
|
||||
by_path: dict[str, list[tuple[str, dict]]] = {}
|
||||
for f in sorted(glob.glob(f"{CCR_DIR}/CCR-*.yaml")):
|
||||
d = yaml.safe_load(open(f))
|
||||
if not isinstance(d, dict):
|
||||
continue
|
||||
bao = d.get("openbao") or {}
|
||||
path = bao.get("kv_path") or bao.get("path")
|
||||
if path:
|
||||
by_path.setdefault(path, []).append((os.path.basename(f), d))
|
||||
return by_path
|
||||
|
||||
|
||||
def _catalog() -> dict[str, dict]:
|
||||
cat = yaml.safe_load(open(CATALOG))
|
||||
entries = cat.get("entries") if isinstance(cat, dict) else cat
|
||||
return {str(e.get("path_template")): e for e in entries or [] if e.get("path_template")}
|
||||
|
||||
|
||||
def draft() -> dict:
|
||||
ccrs, catalog, out = _ccrs(), _catalog(), {}
|
||||
for path, owner in _proposals().items():
|
||||
entry = {"review": "draft", "sources": []}
|
||||
fields = dict.fromkeys(REQUIRED, "")
|
||||
fields["owner"] = owner
|
||||
if path in ccrs:
|
||||
# The first CCR placed the path; later ones add readers.
|
||||
name, d = ccrs[path][0]
|
||||
fields["description"] = _first_sentence((d.get("target") or {}).get("purpose") or d.get("title"))
|
||||
fields["used_by"] = _clip((d.get("delivery") or {}).get("target"))
|
||||
refs = [f"railiance-platform/credential-change-requests/{n}#lifecycle.rotate" for n, _ in ccrs[path]]
|
||||
fields["rotation"] = _clip("; ".join(refs[:2]))
|
||||
rotate = (d.get("lifecycle") or {}).get("rotate")
|
||||
if rotate:
|
||||
fields["on_loss"] = _clip(f"Re-issue per rotation: {rotate}")
|
||||
entry["sources"] += [n for n, _ in ccrs[path]]
|
||||
cat = catalog.get(path)
|
||||
if cat:
|
||||
fields["description"] = fields["description"] or _first_sentence(cat.get("title"))
|
||||
rot = cat.get("rotation") or {}
|
||||
if not fields["rotation"] and cat.get("wiki_ref"):
|
||||
fields["rotation"] = _clip(f"ops-warden/{cat['wiki_ref']}")
|
||||
if not fields["on_loss"] and rot.get("steps"):
|
||||
fields["on_loss"] = _clip(f"{rot.get('method', '')} by {rot.get('owner', '')}: {rot['steps'][0]}")
|
||||
entry["sources"].append(f"ops-warden catalog {cat.get('id')}")
|
||||
entry.update(fields)
|
||||
entry["missing"] = [k for k in REQUIRED if not entry[k]]
|
||||
out[path] = entry
|
||||
return out
|
||||
|
||||
|
||||
def check(doc: dict) -> int:
|
||||
bad = 0
|
||||
for path, e in doc.items():
|
||||
missing = [k for k in REQUIRED if not e.get(k)]
|
||||
long = [k for k in REQUIRED if len(str(e.get(k, ""))) > MAX_VALUE]
|
||||
state = e.get("review")
|
||||
if missing or long or state != "accepted":
|
||||
bad += 1
|
||||
print(f"{'ok ' if not (missing or long) and state == 'accepted' else '-- '}{path} review={state}"
|
||||
f"{' missing=' + ','.join(missing) if missing else ''}{' too-long=' + ','.join(long) if long else ''}")
|
||||
print(f"{len(doc) - bad}/{len(doc)} ready to apply")
|
||||
return 0 if bad == 0 else 1
|
||||
|
||||
|
||||
def apply(doc: dict) -> int:
|
||||
failed = 0
|
||||
for path, e in doc.items():
|
||||
if e.get("review") != "accepted" or any(not e.get(k) for k in REQUIRED):
|
||||
print(f"skip {path}")
|
||||
continue
|
||||
mount, rest = path.split("/", 1)
|
||||
args = ["bao", "kv", "metadata", "patch", f"-mount={mount}"]
|
||||
args += [f"-custom-metadata={k}={_clip(e[k])}" for k in REQUIRED]
|
||||
args.append(rest)
|
||||
ok = subprocess.run(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0
|
||||
failed += not ok
|
||||
print(f"{'patched' if ok else 'FAILED '} {path}")
|
||||
return 1 if failed else 0
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
if len(argv) >= 1 and argv[0] == "draft":
|
||||
yaml.safe_dump(draft(), sys.stdout, sort_keys=True, allow_unicode=True, width=100)
|
||||
return 0
|
||||
if len(argv) == 2 and argv[0] in ("check", "apply"):
|
||||
doc = yaml.safe_load(open(argv[1])) or {}
|
||||
return check(doc) if argv[0] == "check" else apply(doc)
|
||||
print("usage: custody-describe.py draft | check FILE | apply FILE", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
45
tests/test_custody_describe.py
Normal file
45
tests/test_custody_describe.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import importlib.util
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
_spec = importlib.util.spec_from_file_location("custody_describe", Path(__file__).parents[1] / "scripts" / "custody-describe.py")
|
||||
cd = importlib.util.module_from_spec(_spec)
|
||||
_spec.loader.exec_module(cd)
|
||||
|
||||
FULL = dict(description="d", owner="o", used_by="u", rotation="r", on_loss="l")
|
||||
|
||||
|
||||
def test_apply_patches_only_accepted_complete_entries_and_only_metadata():
|
||||
doc = {
|
||||
"platform/workloads/a/b": {"review": "accepted", **FULL},
|
||||
"platform/workloads/c/d": {"review": "draft", **FULL},
|
||||
"operators/e": {"review": "accepted", **dict(FULL, used_by="")},
|
||||
}
|
||||
with patch.object(cd.subprocess, "run", return_value=MagicMock(returncode=0)) as run:
|
||||
assert cd.apply(doc) == 0
|
||||
assert run.call_count == 1
|
||||
args = run.call_args.args[0]
|
||||
assert args[:5] == ["bao", "kv", "metadata", "patch", "-mount=platform"]
|
||||
assert args[-1] == "workloads/a/b"
|
||||
assert not any(a in ("get", "put", "patch-data") or "/data/" in a for a in args)
|
||||
assert run.call_args.kwargs["stdout"] is cd.subprocess.DEVNULL and run.call_args.kwargs["stderr"] is cd.subprocess.DEVNULL
|
||||
|
||||
|
||||
def test_apply_reports_failure_without_output():
|
||||
doc = {"platform/workloads/a/b": {"review": "accepted", **FULL}}
|
||||
with patch.object(cd.subprocess, "run", return_value=MagicMock(returncode=2)):
|
||||
assert cd.apply(doc) == 1
|
||||
|
||||
|
||||
def test_check_refuses_drafts_missing_and_overlong_fields():
|
||||
doc = {
|
||||
"p/ok": {"review": "accepted", **FULL},
|
||||
"p/draft": {"review": "draft", **FULL},
|
||||
"p/long": {"review": "accepted", **dict(FULL, on_loss="x" * 600)},
|
||||
}
|
||||
assert cd.check(doc) == 1
|
||||
assert cd.check({"p/ok": doc["p/ok"]}) == 0
|
||||
|
||||
|
||||
def test_clip_respects_openbao_value_limit():
|
||||
assert len(cd._clip("y" * 2000)) == cd.MAX_VALUE
|
||||
|
|
@ -90,7 +90,7 @@ id: MASON-WP-0004-T02
|
|||
status: wait
|
||||
needs_human: true
|
||||
decision_id: 62aa95e4-3ccc-4867-8806-5e3db005b483
|
||||
blocking_reason: "Requires a valid scoped metadata grant (operator) before the described paths can be written and verified."
|
||||
blocking_reason: "Drafts ready (manifests/custody-descriptions.yaml, 29/38 complete). Founder reviews and marks entries accepted, then applies with scripts/custody-describe.py apply under the admin wrapper; owners fill the 9 gaps (T03)."
|
||||
priority: medium
|
||||
state_hub_task_id: "a6a944d7-21c5-5043-a78d-b3809de0ee64"
|
||||
```
|
||||
|
|
@ -106,6 +106,19 @@ Acceptance: `custody-inventory.py` reports these paths without a `!`, and each
|
|||
|
||||
2026-10-03: human gate routed to hub decision 62aa95e4-3ccc-4867-8806-5e3db005b483 (D1: Attended OpenBao provisioning window: one session clears the pending credential lanes) by CUST-WP-0075; resolving that decision raises C-40 here.
|
||||
|
||||
2026-10-09: drafted. `scripts/custody-describe.py draft` builds
|
||||
`manifests/custody-descriptions.yaml` from the railiance-platform CCRs
|
||||
(`target.purpose`, `delivery.target`, `lifecycle.rotate`), the ops-warden
|
||||
catalog and the owner proposals, never from OpenBao; four ops-mason-sourced
|
||||
entries were filled by hand from `plans/`. 29 of 38 have all five fields; 9
|
||||
have gaps left empty on purpose (nextcloud x2, keycape-factor-issuer,
|
||||
grafana-admin, smtp, and four `used_by`). Every entry is `review: draft`.
|
||||
`apply` writes custom_metadata only, for `review: accepted` entries, silently
|
||||
(`tests/test_custody_describe.py`, suite 157 passed). Founder run, after
|
||||
review: `warden access openbao-platform-admin-login --exec -- python3
|
||||
/home/worsch/ops-mason/scripts/custody-describe.py apply
|
||||
/home/worsch/ops-mason/manifests/custody-descriptions.yaml`.
|
||||
|
||||
## Resolve the rest with their owners
|
||||
|
||||
```task
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue