Add attended sender release with sealed sources and recovery
Some checks failed
Transactional mail acceptance / mail (push) Failing after 49s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
This commit is contained in:
tegwick 2026-10-31 17:18:52 +01:00
parent c0c3221855
commit e795b2970c
9 changed files with 900 additions and 12 deletions

View file

@ -0,0 +1,61 @@
{
"schema": "email-connect.sender-attended-entry-preparation.v1",
"recorded_at": "2026-10-31T16:16:52.410337+00:00",
"status": "entry_implemented_owner_return_pending",
"owner_task_id": "da56d175-3126-5eaa-90c5-dd1e9da050d5",
"owner_task_observed_status": "todo",
"live_preparation_result": {
"status": "sender_release_refused",
"reason": "verified_credential_owner_return_required",
"retry_automatically": false
},
"live_proposal_sealed": false,
"node_contacted_by_preparation": false,
"application_changed": false,
"mail_sent": false,
"new_assembly_tests_passed": 21,
"suite_passed": 270,
"suite_existing_optional_skips": 21,
"suite_seconds": 36.83,
"native_kubernetes_effects_in_assembly": "modeled",
"sqlite_and_runtime_probes": "actual_synthetic_subprocess_and_local_wsgi",
"sources": [
{
"path": "scripts/release-sender.py",
"sha256": "98a59ebefc36094a01beaa7359f2508f54e2d59f87646e5219deeaae4b767c5a"
},
{
"path": "scripts/sender_release.py",
"sha256": "3415b03757fcfcaf4fce69e7b7c2e99702f4b238e19d9b2f8d021b01b3de68fc"
},
{
"path": "tests/test_sender_release.py",
"sha256": "d353f78da47c8d4d64b0faba3af5fa79d6756f9df9256bfd9812b3818d83e843"
},
{
"path": "scripts/sender_native.py",
"sha256": "41722d23f7d04a41c7fb428cb25a24438a144684cfbc5dcd875d5299ec6ffc71"
},
{
"path": "scripts/sender_transition.py",
"sha256": "fa80b428b6231b74578baf23ff5977b6ce871a84321718adb2ba15d314f7abe1"
},
{
"path": "scripts/capture-ledger.py",
"sha256": "7994fe7b4fbf72e28a4296212005a88d0ef9fcb626b066e6bcf2885500bc69d6"
},
{
"path": "scripts/ledger_archive.py",
"sha256": "92d9d5e1908c7d433eb3f4ada6460f7d9b639017a4c2311cb7ffaacd262f33c3"
},
{
"path": "scripts/inspect-sender-ledger.py",
"sha256": "262d6d52d5c10fb8583e0828ed34945b647f1d53bf9de5286374fef85cf76a8d"
},
{
"path": "scripts/rehearse-ledger.py",
"sha256": "5c43b655149d8ae577fab80e76358be35f90cb0357df9b726ac7cb23346946a0"
}
],
"all_projection_resource_uids_bound": true
}

View file

@ -0,0 +1,110 @@
# Attended sender release preparation
The source-bound launcher and node coordinator are implemented. Twenty-one new tests
pass; the full owner suite reports **270 passed, 21 existing optional harness
skips in 36.83s**. This does not establish live sender deployment. No runnable
proposal has been sealed: the exact credential owner task RPF-WP-0068-T04 is still
`todo`, and the preparation command refuses before contacting the node.
EMAIL-WP-0010-T02 now waits for that owner return so the live proposal and its
exact digest can be prepared and reviewed. T03/T04 retain attended execution and
independent acceptance. The existing native adapter's five server dry-runs and
pinned-image snapshot rehearsal remain source-valid; those sources did not change.
## Source binding and admission
`scripts/release-sender.py` is the workstation entry point. It defaults to no
action and requires an explicit prepare/show/inspect/execute/recover selection.
Preparation requires a private fresh output path and the complete successful
foundation journal. It checks that the exact RPF-WP-0068-T04 UUID and workplan UUID
are marked done in State Hub before any node call. The owner handoff must include
the review's Warden session revocation/cleanup, CCR verification and rotation
inventory obligations; the consumer never marks that task done itself.
The node repeats the owner check, validates the foundation receipt's fixed digest
and proof fields, verifies current projection specs/Ready/ownership and the
admitted service-account UID, then performs the five native server dry-runs. Its
proposal binds complete before/after observations, the exact foundation contract,
all four projection resource UIDs, successful owner receipt, completed task
identity and eight source hashes. A replaced projection refuses forward execution
even if its spec is unchanged; explicit configuration recovery remains available.
The capsule contains those eight exact files, including launcher and coordinator.
It is saved exclusively as mode 0600 in a private operator directory.
Execution requires the retained capsule, unchanged reviewed source bytes, the
full proposal digest, a human terminal and the exact `DEPLOY <digest>` response.
It rechecks the owner return and native preimages. Source transfer uses a fixed
SSH target and a quoted Python bootstrap; payload data never enters shell code.
The bootstrap verifies source hashes and uses a temporary private directory with
isolated Python imports. No generic archive extraction or persistent installation
is involved.
## Durable execution and recovery
The node creates one exclusive attempt directory under
`/var/lib/email-connect-sender-release/<proposal-sha256>/`. It retains the capsule,
private append-only journal, process identity and ledger snapshots. The journal
is exclusively locked; the existing ledger capture lock excludes concurrent
backup capture. The coordinator invokes the actual native adapter and protocol
for pause, fence, capture/replay, configuration change, resume, runtime checks,
postflight comparison and UID-bound helper cleanup.
The successful forward result is `sender_deployed_verified`; successful explicit
recovery is `configuration_recovered_verified`. The final journal phase is the
durable evidence if the launcher's response is lost. A failed response or timeout
is **unconfirmed**, not proof that the node stopped. The launcher never retries.
`--inspect` checks the retained capsule, process PID plus Linux start ticks, the
journal and current recognized configuration. It checks recovery processes as
well as the original process. A running attempt is reported as running. After
the process exits, it reports the journal phase and actual sender/replica count;
it does not mutate or resume the application. Inspection refuses a truncated
journal or unrelated native drift instead of reporting success.
`--recover` requires the same capsule/digest and a human `RECOVER <digest>`
confirmation. It does not depend on a still-healthy new projection or a renewed
owner approval: it restores the already reviewed original configuration after
capturing the **current** ledger. It retains subsequent accepted and uncertain
sends, suppression and consumed/canceled handles. It never restores an older
database, clears unknown reservations or sends a message. Repeated forward
execution and completed recovery refuse. If source files have changed, restore
the exact reviewed capsule sources before invoking its launcher; never edit or
reseal a retained attempt to bypass a mismatch.
Some ambiguous states require separate inspection: a truncated journal, a crash
during initial metadata creation, an unconfirmed helper creation without a
recorded UID, or unrelated configuration drift. The tool does not silently adopt
pods, discard evidence or invent a recovery from those states. A missing local
capsule must be recovered from the retained private node copy, not reconstructed
from current configuration.
## Validation scope
The assembly tests invoke the actual coordinator, protocol and native adapter.
Kubernetes transport is modeled with synthetic objects; SQLite capture/replay
runs in real subprocesses, and runtime probes contact the actual scoped WSGI
application on local loopback with zero synthetic provider sends. Host path/UID
substitutions are explicit in that harness. It is not a real Kubernetes rollout
or a new pinned-image claim; the separate isolated-image rehearsal covers that
snapshot path.
Tests cover complete forward/recovery, a lost native configuration reply, later
accepted and uncertain sends, capture-lock contention, retained exclusive attempts,
source/proposal/foundation/owner drift, private complete final foundation receipts,
terminal and exact confirmation gates, bootstrap hash refusal, unconfirmed SSH
timeouts and real process lifetime observation. The earlier protocol tests retain
their ten real abrupt child exits around phase boundaries.
Ruff passes. Suite log: `/tmp/email-sender-release-final-check-20261031.log`.
Source hashes and the live owner-gate refusal are in
`sender-attended-entry-2026-10-31.json`.
## Next owner handoff
After RPF-WP-0068-T04 has its verified native return, prepare a fresh private
capsule using `--prepare --packet <new-private-path> --foundation-receipt
<successful-private-journal>`. Preparation is read-only for production. Review
the resulting digest, native preimages and bound sources; publish the concrete
execution/recovery packet before asking for attended execution. The actual
`--execute` and `--recover` calls then use that retained `--packet` and its exact
`--expect-digest`. No live digest or execution instruction is claimed ready here.

View file

@ -1,10 +1,11 @@
# Native sender adapter preparation
EMAIL-WP-0010-T02 remains in progress. `scripts/sender_native.py` connects the
The native adapter is implemented. `scripts/sender_native.py` connects the
sender transition protocol to the existing Kubernetes, ledger capture and
isolated replay tools. It has no CLI; default construction permits reads and
server dry-runs only. The attended entry point and final source-bound release
packet still need preparation. No production sender change occurred.
server dry-runs only. The attended entry point is now implemented separately;
EMAIL-WP-0010-T02 waits for the verified owner return before sealing the final
live release packet. No production sender change occurred.
## Implemented native checks
@ -68,6 +69,11 @@ tests. Ruff passes. Log: `/tmp/email-sender-native-check-20261031.log`.
## Remaining T02 work
Update: the launcher, source binding and assembled protocol/adapter tests are now
implemented in [the attended entry review](sender-attended-entry-2026-10-31.md).
T02 waits for the verified owner receipt before sealing the live proposal and
publishing its concrete execution/recovery digest.
Bind the complete executor and dependencies into the final capsule, implement the
attended entry point with exact proposal digest and owner-return verification,
and prove the assembled forward/recovery path. Prepare the concrete execution and

View file

@ -1,8 +1,10 @@
# Sender transition protocol preparation
EMAIL-WP-0010-T02 is in progress. `scripts/sender_transition.py` is a tested
`scripts/sender_transition.py` is a tested
protocol core, with no production CLI or Kubernetes credentials. It does not
establish native fencing, credential admission or an execution-ready release.
The adapter and attended entry point are now implemented separately; T02 waits
for the verified owner return before sealing the final live proposal.
## Concrete behavior