activity-core/docs/custodian-runtime-binding-proposal.md
tegwick 0319fe8be8 docs: specify disabled Custodian runtime delivery choices
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a15374-f59f-7a31-86d2-d23b5d2ed181
2026-10-19 14:01:28 +02:00

124 lines
7.9 KiB
Markdown

# Custodian runtime binding proposal
Review artifact, 2026-10-19. Not a deployment or activation approval. Both native
ActivityDefinitions remain `enabled: false`. No manifests, images or schedules
were changed for this proposal. Read with `custodian-bounded-operations.md`.
## Verified source boundary
The worker in `k8s/railiance/20-runtime.yaml` runs as UID/GID 1000 and already
uses `actcore-ops-artefacts` for freshness and owner-return subdirectories. Its
image does not contain the Custodian commands; its current mounts contain
neither the accepted fleet workplans/decisions nor the new private state.
`CUSTODIAN_REPO_ROOT=/var/custodian` only binds existing prompts/working memory;
it does not satisfy any `ACTIVITY_CORE_CUSTODIAN_*` binding.
The existing immutable ConfigMap plus read-only mount pattern documented in
`k8s/backup-release/README.md` is sufficient for the small command bundle. No
new packaging service is needed. Proposed source pin:
`the-custodian@60d893af1bf77f457504b64ff1c9ffb5c6ae6579`.
These four files total 30,169 bytes and use Python standard-library modules;
the daily command imports/calls the two sibling helper scripts.
| Path under owner root | SHA-256 |
| --- | --- |
| scripts/security_flow_daily.py | 4a4e84702b3fb38d086695bb6d601430d8914bd79286b4f683794206f110c353 |
| scripts/security_flow_report.py | eac9fdcc56d7f3ff613e6848c7523fb9a421d82a3b564a51c36f8c2b842eef02 |
| scripts/sync_resolved_decisions.py | 182433ca7cd31e2b2773246ec1be02569ba6f089ac009add6543e5e48777b5ec |
| scripts/weekly_allocation_report.py | 93f546243331308d7e60f8db36e988d9847537a2fe46cce49ee2fffba1f7f247 |
A reviewed immutable ConfigMap can map these four keys to `scripts/<name>`
below `/opt/custodian-commands`. Kubernetes ConfigMap volume projections use
symlinks, which the executor deliberately rejects. Therefore copy the mounted
immutable bundle into a dedicated emptyDir in an init container, verify all four
hashes there, then mount that copy read-only in the worker. Do not bind the
projected ConfigMap directly or weaken executor symlink checks. Alternatively,
copy the same pinned files into the existing worker image during its reviewed
build. The latter avoids the init-container copy and is the narrower runtime
mount boundary, but requires an accepted source-delivery/build input. Neither
alternative has been chosen or implemented here.
## Exact proposed bindings and outstanding choices
| Binding | Proposed value / owner decision |
| --- | --- |
| ACTIVITY_CORE_CUSTODIAN_ROOT | `/opt/custodian-commands`, regular files, read-only, hashes above |
| ACTIVITY_CORE_CUSTODIAN_FLEET_ROOT | `/var/lib/custodian-inputs/fleet`, read-only accepted snapshots |
| ACTIVITY_CORE_CUSTODIAN_STATE_ROOT | `/var/lib/activity-core/custodian`, persistent, UID 1000, mode 0700 |
| ACTIVITY_CORE_CUSTODIAN_REPOS | Explicit operator-approved subset of the daily report estate; each must have `workplans/` |
| ACTIVITY_CORE_CUSTODIAN_HUB | `http://state-hub.state-hub.svc.cluster.local:8000`, subject to admitted network verification |
| ACTIVITY_CORE_CUSTODIAN_ALLOCATION_REQUEST | `/var/lib/activity-core/custodian/requests/current.json`, regular file protected against concurrent writers |
For daily input, preserve `<repo>/workplans/**/*.md` including dependency records
and `<repo>/decisions/*.md` only for approved decision-sync scope. The decision
helper scans every repo under the fleet root independently of the report list.
Do not mount a whole workstation home, `.git`, credential directories or arbitrary
mutable repositories. Fleet snapshots need per-repo revision, capture time,
refresh owner and an explicit staleness policy. No existing publication contract
for this combined input was found in the worker manifests. Choosing its publisher
is a real prerequisite; a pinned old snapshot alone cannot support daily freshness.
Weekly input remains the existing retained collector outputs plus request JSON,
not a new collector. Its owner must bind actual input/report paths and evaluation
time, freeze the request across retries, and publish a new request for the next
week. The executor does not advance a static `current.json` automatically.
Snapshot publication must be atomic relative to execution and preserve retained
inputs needed to reproduce reports. Never substitute synthetic readings.
Storage choice: reuse a new `custodian` subdirectory of existing
`actcore-ops-artefacts` only after its owner accepts capacity, retention and backup
coverage, or provision a dedicated claim with those properties. The existing
rollout document describes a 1Gi local-path claim, and October 4 evidence reports
it Bound; neither proves present capacity or backup coverage. Precreate state,
`security-flow`, `requests` and output/input retention directories with correct
ownership. Do not chown/chmod the whole shared claim. A PVC subPath is acceptable
if actual mounted paths are regular directories and pass executor validation.
The proposed direct Hub service matches the worker's existing
`STATE_HUB_API_BASE`; the shared config also contains an edge-relay URL. Daily
message delivery requires a central message ID. A relay queued receipt is pending
evidence and is not interchangeable with that ID; do not select relay delivery
without an explicit adapter/replay contract. Needed operations are GET open
decisions, POST decision resolution, POST messages, and the existing progress
sink. Runtime network authorization remains unverified. The shared worker already
has secret env and kube mounts: this proposal does not establish credential isolation.
Placement choices for owner review:
1. Existing production worker: choose image-baked scripts or verified copied
immutable bundle, choose fleet publisher and state claim above, then review
normal owner GitOps changes. This reuses the present Temporal task queue.
2. Workstation execution: requires an explicitly routed worker/task queue and
a durable always-on ownership/connectivity contract. Merely starting another
worker on `orchestrator-tq` is unsafe because either worker may receive these
activities. No workstation routing or second scheduler is proposed.
## Verification and rollback before activation
Prepare the accepted source bundle and manifest diff outside active kustomization
until the choices above are recorded. Preserve both disabled definitions; native
schedule synchronization creates disabled definitions paused. Verify their DB
versions and Temporal paused state after any later deployment, without firing.
Mount/env changes exceed image-only promotion authority; follow owner-reviewed
GitOps promotion, not a widened image-only helper.
Local source tests already cover parsing both disabled definitions, missing-binding
refusal before subprocess, fixed commands, symlink rejection, bounded timeouts
and sanitized receipts. At delivery review, independently verify pinned hashes,
worker Python imports, UID/modes, mount read-only flags, free retained capacity,
scoped fleet revision/age and actual request/output confinement. Use fixture inputs
for initial process verification; daily dry-run still reads Hub and therefore is
not an offline test. Obtain separate authorization for real Hub operations.
After operator-approved runtime placement, retain exact worker image/bundle and
input revisions, operation context, owner report hash, progress sink result and
owner acknowledgment. Scheduled activation and first unattended run remain
ACTIVITY-WP-0047-T02/T03 and CUST-WP-0071-T04/T05 gates. An exit-zero fixture
or a paused schedule does not satisfy them.
Rollback: keep both schedules paused, restore the previous reviewed worker
image/config/bundle references through GitOps, and keep old bundle objects until
no rollback ReplicaSet references them. Preserve private reports, checkpoints
and requests; do not delete the state claim. Inspect partially delivered daily
checkpoints before retrying, because rollback cannot undo Hub writes. Do not
reuse or overwrite an immutable weekly output for revised inputs.