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

7.9 KiB

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.