2026-09-27 16:39:29 +02:00
|
|
|
# Image-only release broker core
|
|
|
|
|
|
|
|
|
|
ACTIVITY-WP-0041-T03 owns this implementation. **Not activated in production.**
|
2026-09-27 20:51:56 +02:00
|
|
|
The modules are not wired to a production API, worker, schedule or credential source.
|
2026-09-27 16:39:29 +02:00
|
|
|
Construction requires `admitted=True` from trusted deployment configuration; that
|
|
|
|
|
switch is a local guard, not proof that an identity has actually been admitted.
|
|
|
|
|
The current deployed revision is unchanged, so this code does not restart soak.
|
|
|
|
|
|
|
|
|
|
## Implemented boundary
|
|
|
|
|
|
|
|
|
|
`activity_core.release_broker.Receipts` checks Ed25519 signatures against a locally
|
|
|
|
|
configured key-to-principal/role registry. Requests cannot introduce trusted keys.
|
|
|
|
|
Build, independent review, health and retention attestations must all name the
|
|
|
|
|
same fixed repository/application, full candidate and rollback commits, and exact
|
|
|
|
|
before/after manifest hashes. Evidence expires within five minutes. Build and
|
|
|
|
|
review require different principals **and keys**. Build evidence must cover exact
|
|
|
|
|
candidate image digests and all three mandatory CI contexts. Retention must cover
|
|
|
|
|
both live and rollback images. Health must attest continuous healthy observation
|
|
|
|
|
of the prior revision for at least 24 hours. The trusted issuers must verify these
|
|
|
|
|
facts against their authorities; signatures alone cannot make assertions true.
|
|
|
|
|
|
|
|
|
|
`gitops_policy` is the shared image-only validator used by both broker and the
|
|
|
|
|
existing CLI. It forbids non-image deployment changes and resource-set changes.
|
|
|
|
|
`release_operations` constructs a one-field update of the validated platform child
|
|
|
|
|
Application and fixed selective root/child sync payloads. No arbitrary command,
|
|
|
|
|
repository path, application, prune flag or override is exposed by these builders.
|
|
|
|
|
|
|
|
|
|
## Durable recovery
|
|
|
|
|
|
|
|
|
|
A local SQLite ledger stores signed receipts, their hashes, the release binding,
|
|
|
|
|
phase and transition history. One active row and an immediate transaction serialize
|
|
|
|
|
all releases and adapter calls on that ledger. The database must reside on durable,
|
|
|
|
|
operator-owned local storage, shared by all instances handling this application;
|
|
|
|
|
this is not a distributed lock across independent databases or network filesystems.
|
|
|
|
|
|
|
|
|
|
Phases are planned → publish_pending → published → synced → complete. Publication
|
|
|
|
|
intent is committed before calling the adapter. Lost responses therefore cannot
|
|
|
|
|
turn a possibly published change into an assumed cancellation. A release that
|
|
|
|
|
expires before any attempt is cancelled; after intent, timeout or failed health
|
|
|
|
|
enters rollback_planned → rollback_published → rollback_synced → rolled_back.
|
|
|
|
|
Failed rollback retains the active slot. Retried publication/sync must be
|
|
|
|
|
idempotent, using compare-and-swap and accepting already-at-target as success.
|
|
|
|
|
The adapter must refuse any unexpected third revision rather than overwriting it.
|
|
|
|
|
|
2026-09-27 17:03:06 +02:00
|
|
|
Tests use generated fixture keys, disposable real Git repositories and a simulated
|
|
|
|
|
Kubernetes transport. They prove signed admission, real Git publication/rollback,
|
|
|
|
|
concurrent-writer rejection and lost-push-response recovery. Kubernetes authorization,
|
|
|
|
|
production credentials and live Git/ArgoCD rollback are **not** proven by these fixtures.
|
2026-09-27 16:39:29 +02:00
|
|
|
|
|
|
|
|
## Still required before activation
|
|
|
|
|
|
2026-09-27 17:03:06 +02:00
|
|
|
1. Admit and exercise the implemented transport adapter against an isolated
|
|
|
|
|
authenticated Git/Kubernetes environment. Supply the actual bounded report/schedule
|
|
|
|
|
invariant probe, pinned cluster UID and immutable credential/context configuration.
|
|
|
|
|
Verify the real authorization denials and restart behavior; simulated Kubernetes
|
|
|
|
|
responses do not prove those. A generic repository-write token is not path enforcement.
|
2026-09-27 16:39:29 +02:00
|
|
|
2. Admit the dedicated principal and credential custody through the owner lane.
|
|
|
|
|
ArgoCD Core has no API-server token lane. Kubernetes Application patch RBAC
|
|
|
|
|
alone cannot restrict fields: keep it behind the reviewed broker boundary.
|
|
|
|
|
Publish negative access tests and revocation behavior; no broad operator key.
|
|
|
|
|
3. Supply independent trusted build/review/health/retention issuers and their key
|
|
|
|
|
custody/rotation. The observer must measure continuous health; it must not
|
|
|
|
|
manufacture a 24-hour interval from two snapshots. Preserve signing public
|
|
|
|
|
keys for audit and protect the ledger from producer writes.
|
2026-09-27 20:51:56 +02:00
|
|
|
4. Deploy/register the implemented Temporal dispatcher and supply its bounded,
|
|
|
|
|
idempotent sanitized evidence sink. Prove authenticated transport failure, concurrency with other
|
2026-09-27 16:39:29 +02:00
|
|
|
publishers, restart, denial and rollback in an isolated deployment environment.
|
|
|
|
|
Then finish the production observation gate and enable the bounded scope.
|
|
|
|
|
|
|
|
|
|
Platform enforcement contract: `railiance-platform/docs/activity-core-release-admission.md`.
|
|
|
|
|
These requirements remain live work in ACTIVITY-WP-0041-T03 and RPF-WP-0048-T02.
|
2026-09-27 17:03:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
## Transport and observer implementation
|
|
|
|
|
|
|
|
|
|
`release_transport.GitArgoBackend` retrieves both full source commits from the fixed
|
|
|
|
|
activity-core repository and compares parsed manifest hashes with the admitted
|
|
|
|
|
binding. It accepts only the audited static `runtime.yaml` Kustomization; proposed
|
|
|
|
|
source cannot execute a renderer/plugin. Git publication creates a commit changing
|
|
|
|
|
only the platform child revision field. Non-force pushes reject concurrent branch
|
|
|
|
|
advances. After a lost response, remote Git is the durable source of truth: a retry
|
|
|
|
|
accepts already-at-target and selective sync uses the current verified platform tip.
|
|
|
|
|
Unexpected third revisions and unrelated changed paths stop the operation.
|
|
|
|
|
|
|
|
|
|
The adapter verifies the operator-pinned kube-system namespace UID before Git
|
|
|
|
|
publication or Kubernetes access. Context/credentials must remain immutable during
|
|
|
|
|
its lifetime. Every subprocess is non-shell, with timeouts and sanitized failures.
|
|
|
|
|
Kubernetes JSON patches compare resourceVersion and spec before setting the fixed
|
|
|
|
|
sync operation. Existing foreign operations are never overwritten. Root sync selects
|
|
|
|
|
only activity-core; child sync never prunes. Health checks require exact revision,
|
|
|
|
|
Synced/Healthy and all three deployments ready at their observed generations, plus
|
|
|
|
|
a mandatory report/schedule invariant probe. That trusted callback must itself use
|
|
|
|
|
bounded I/O; no production probe is supplied or implied by the fixture implementation.
|
|
|
|
|
Polling windows are bounded, with individual command timeout overhead possible.
|
|
|
|
|
|
|
|
|
|
`release_observer.HealthObserver` records samples durably. It requires consecutive
|
|
|
|
|
healthy samples of one revision with gaps no greater than 90 seconds. Failed samples,
|
|
|
|
|
revision changes and missed samples reset the interval; clock regression durably
|
|
|
|
|
invalidates it. Attestation requires a complete 24-hour sampled interval and a fresh
|
|
|
|
|
last sample, including after restart. This is evidence at a bounded sampling cadence,
|
|
|
|
|
not a claim that every instant between samples was observed. Two distant healthy
|
|
|
|
|
snapshots cannot establish the interval. New observers start from their first actual
|
|
|
|
|
sample; they do not backfill the earlier deployment timestamp.
|
|
|
|
|
|
|
|
|
|
Neither module is connected to a production schedule, endpoint or credential source.
|
2026-09-27 20:51:56 +02:00
|
|
|
Trusted signer custody, real invariant probes, production Temporal registration and isolated
|
2026-09-27 17:03:06 +02:00
|
|
|
Kubernetes authorization/rollback tests remain required before activation. Production
|
|
|
|
|
revision and the deployment observation clock are unchanged by this source-only work.
|
2026-09-27 20:51:56 +02:00
|
|
|
|
|
|
|
|
## Temporal dispatch and durable audit delivery
|
|
|
|
|
|
|
|
|
|
`release_dispatch.ReleaseDispatchWorkflow` resumes an already-admitted release ID.
|
|
|
|
|
`ReleaseActivities` binds its broker, backend factory and audit sink at trusted worker
|
|
|
|
|
construction; Temporal inputs cannot supply manifests, keys, transport configuration
|
|
|
|
|
or an admission flag. `release_worker` constructs a separate
|
|
|
|
|
`activity-core-release-tq` worker, which the admitted deployment must explicitly run.
|
|
|
|
|
It does not modify the ordinary orchestrator worker or start a workflow/schedule.
|
|
|
|
|
Use a stable workflow ID such as `activity-core-release:<ledger release ID>` when
|
|
|
|
|
starting it. Broker serialization remains authoritative even with duplicate dispatch.
|
|
|
|
|
|
|
|
|
|
Adapter calls run outside the workflow and resume from the durable ledger after
|
|
|
|
|
activity retry or restart. Failed rollback keeps the release slot and is retried
|
|
|
|
|
with durable timers; continue-as-new bounds workflow history. Temporal receives
|
|
|
|
|
sanitized errors rather than raw transport exceptions. Disabling the broker's
|
|
|
|
|
trusted admission setting prevents subsequent activity calls; operational revocation
|
|
|
|
|
still requires the admitted worker/credential lifecycle, not a workflow input.
|
|
|
|
|
|
|
|
|
|
The transition ledger is also the audit outbox. Acknowledgements are persisted only
|
|
|
|
|
after the configured sink returns. Delivery is at least once with stable event IDs;
|
|
|
|
|
the sink must durably upsert those IDs and use bounded I/O. It receives only release
|
|
|
|
|
ID, phase, time, candidate/rollback commits and the fixed application name. Sink
|
|
|
|
|
credentials, signed envelopes and transport output never enter these events.
|
|
|
|
|
Nonterminal sink failure retains pending events and permits recovery to proceed;
|
|
|
|
|
terminal workflow completion waits for acknowledged audit delivery. One ledger is
|
|
|
|
|
bound to one logical audit sink; changing sinks requires an explicit replay/migration
|
|
|
|
|
of delivery acknowledgements. The sink can fan out to approved evidence destinations.
|
|
|
|
|
|
|
|
|
|
Tests exercise the real ledger through the activities and validate Temporal sandbox
|
|
|
|
|
construction, with in-memory transports/sinks and orchestration stubs. This does not
|
|
|
|
|
prove a deployed Temporal server, real Kubernetes authorization or production sinks.
|
|
|
|
|
Continuous observer scheduling, actual sink adapters, admission credentials and
|
|
|
|
|
authenticated deployment proof remain activation prerequisites in T03.
|