Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
605061e
docs(handoff): propose shared v0.2 evidence and receipt contract
abruption Oct 6, 2026
b369cef
docs(handoff): resolve v0.2 receipt and validation freeze gates
abruption Oct 6, 2026
99d8ec3
docs(handoff): align native profiles and explicit wait exits
abruption Oct 6, 2026
d8f56c1
docs(handoff): align frozen design with current main
abruption Oct 8, 2026
de1ad51
feat(identity): pin Claude sends to observed process generations
abruption Oct 9, 2026
fed40b9
fix(identity): bound opt-in inbox effect deadlines
abruption Oct 9, 2026
83fe3dc
test(identity): isolate POSIX deadline fixtures on Windows
abruption Oct 9, 2026
1071b76
feat(handoff): add fenced private POSIX receipt runtime candidate
abruption Oct 9, 2026
9a28c2d
fix(claude): retain native facts when generation echo is invalid
abruption Oct 9, 2026
b75b43d
fix(handoff): validate retained receipt and wait metadata
abruption Oct 9, 2026
7598fcb
feat(delivery): frame untrusted peer messages at native boundaries
abruption Oct 9, 2026
5dc9c4a
fix(delivery): budget final framed Windows Codex arguments
abruption Oct 9, 2026
c10bb92
fix(handoff): bound private input and preserve native evidence
abruption Oct 9, 2026
d6c3d1c
fix(handoff): share native Darwin clock across receipt processes
abruption Oct 9, 2026
3c1287e
test(handoff): preserve remaining budgets across clock domains
abruption Oct 9, 2026
d9d1e15
feat(handoff): fence correlation-only Codex queue attempts
abruption Oct 9, 2026
7a2b7f4
fix(codex): refuse unverified home storage overrides
abruption Oct 9, 2026
ab7be7c
test(codex): isolate cross-platform environment key fixtures
abruption Oct 9, 2026
de9bccd
feat(handoff): bound stdin and retain original Codex context
abruption Oct 9, 2026
149f083
fix(handoff): require matching native queue evidence
abruption Oct 9, 2026
a061851
fix(handoff): preserve proven native facts and original target context
abruption Oct 9, 2026
7ca53e9
feat(ssh): bind optional host-key pins to the dispatch connection
abruption Oct 9, 2026
7e5c2bc
test(docs): intercept examples using a POSIX function identifier
abruption Oct 9, 2026
3279680
docs(ssh): verify localized host-key command continuation
abruption Oct 9, 2026
02d84c5
test(handoff): accept deadline closure during slow frame writes
abruption Oct 9, 2026
f320f08
refactor(handoff): share bounded owned-process and private SSH plumbing
abruption Oct 9, 2026
e808df9
feat(handoff): fence bounded source-streamed SSH native submissions
abruption Oct 9, 2026
7f9f8c4
fix(handoff): preserve local message limits for self routes
abruption Oct 9, 2026
cd508ca
fix(generation): preserve original stale target metadata
abruption Oct 9, 2026
80f0acc
test(generation): reject stale pins after owned process restart
abruption Oct 9, 2026
8ec6cf9
test(identity): cover unsupported Codex pins across send modes
abruption Oct 9, 2026
88c661f
chore(handoff): integrate reviewed prerequisites before final merge
abruption Oct 9, 2026
a12a37b
Merge remote-tracking branch 'origin/main' into abruption/v110-handof…
abruption Oct 9, 2026
7f38b63
test(handoff): align positive receipt fixture with cleanup reserve
abruption Oct 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ installation paths.

## Development and history

- [Handoff runtime candidate](handoff-runtime.md) describes unreleased private receipts, supported routes and remaining acceptance gates.
- [v1 compatibility contract](compatibility-v1.md) classifies stable, versioned, migrated, internal and experimental surfaces.
- [Agent transport architecture](architecture/agent-transports.md) and the [relay development plan](relay-development-plan.md) explain implementation boundaries.
- [v1.0.4 notes](releases/v1.0.4.md) describe the current maintenance release dated 2026-10-07; [v1.0.3 notes](releases/v1.0.3.md) preserve the prior security release history; [validation records](validation/69-rc.md) preserve scoped evidence and limitations.
Expand Down
59 changes: 59 additions & 0 deletions docs/handoff-runtime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Handoff runtime candidate

Dry-run validates the original prepared target, not a newly resolved replacement. Claude keeps the original PID/generation check; Codex rechecks and compares the original home/writer context. Changed evidence refuses without native submission and never rewrites the prepared binding.

This is the unreleased implementation candidate for [#181](https://github.com/abruption/session-peer/issues/181), not a feature in published Python v1.0.4. The [frozen Handoff v1 design](contracts/handoff-v1.md) and its synthetic fixture are unchanged. This candidate does not complete every design acceptance gate.

## Supported candidate route

Same-machine POSIX Claude inbox submission can use an explicitly initialized, user-owned ledger and a separate private receipt collector. The connected inbox PID, original process birth and registry endpoint must match the selected generation. Same-machine POSIX Codex supports correlation-only queue submission with no receipt capability and null target generation. Required delivered/acknowledged waits refuse before its queue invocation; best-effort ACK/observation requests report unsupported while retaining actual queued facts. No model/session is started. Single-destination POSIX source-streamed SSH supports correlation-only Claude/Codex submission as described below. Codex body-free injection observation, Windows bounded pipes, paired devices and SSH receipt bootstrap remain unsupported. Ordinary opt-out local/SSH behavior and the Codex wake version gate are unchanged.

```sh
session-peer handoff init --json
session-peer handoff prepare --to worker --message-file ./private-message.txt --json
session-peer send --to worker --message-file ./private-message.txt --correlation-id PREPARED_UUID --request-ack --wait-for acknowledged --wait-timeout 30 --json
session-peer handoff status --correlation-id PREPARED_UUID --json
session-peer handoff wait --correlation-id PREPARED_UUID --wait-for acknowledged --wait-timeout 30 --json
session-peer ack --receipt - --json
session-peer handoff confirm --receipt - --json
```

The message file must be a private, owner-only regular UTF-8 file. Use a prepared ID returned by the second command, not the literal placeholder. ACK/confirmation JSON enters only through private stdin. A cooperating recipient chooses whether to submit the delegated receipt under its normal permissions. Receipt authority is not approval to run other tools, send messages, wake a model or read a transcript. No automatic response is inferred.

Candidate handoff sending accepts named/positional message text, a private message file or bounded POSIX raw stdin. Stdin has one original effect cutoff, strict UTF-8 and limits of four million raw bytes/one million codepoints; a stalled pipe or SIGINT refuses before creating an intent, without an invented epoch or submission result. Receipt/confirmation stdin is a separate bounded API. Opt-in multi-host sending remains an implementation gate. Ordinary opt-out stdin and multi-host sending are unchanged. Native effect input uses the shared untrusted-peer body frame, with every external body line quoted; final character limits include that frame and any receipt authority.

Codex preparation records the canonical thread/home plus bounded private original home/writer resolution. Reusing a prepared ID with changed resolution refuses rather than rebinding it. This is not an atomic native incarnation or consumption proof. The ordinary live-writer and explicit inactive-home rules remain mandatory, and the original resolution is rechecked immediately before the single queue invocation. Its version probe and queue share the reviewed storage guard/environment normalizer from #268; effective native configuration layers/daemon guarantees remain that issue's open investigation boundary. The owned POSIX child has bounded raw metadata stdout/stderr and uses the original deadline/cleanup reserve. A complete matching native queue receipt may retain queueId/submitted facts after timeout or abnormal exit; absent, wrong-target, partial, invalid UTF-8 or overflow output remains unknown even at exit 0 and never retries. Diagnostic stderr never supplies native evidence. No app-server/body/transcript API is called.

Before minting authority, the candidate checks its exact protected script/interpreter and probes that script's ACK command. Private effect input names that exact isolated Python receipt-only handler and ledger, not an unverified session-peer from PATH; the raw capability remains on stdin. Receipt/confirmation input has one five-second EOF deadline and a 4096-byte limit. If storage fails after native submission, the output retains independently validated target/character/dry-run facts, omits unprovable handoff/ACK and forbids retry. With no explicit wait, independently known native submission keeps success/exit 0 plus a fixed history diagnostic; an explicit wait that cannot be verified fails with exit 1/130 without inventing a wait result.

For Codex correlation-only mode there is no pending explicit wait: SIGINT after a complete valid queue receipt retains success/exit 0 and wait not_requested. Partial evidence under SIGINT remains unknown/exit 130. Interrupted version preflight refuses/exit 130 before queue. Diagnostic stderr overflow is separate from stdout overflow: bounded stderr never erases a captured complete valid stdout receipt, while protocol stdout overflow is always unknown. Failed native journal snapshots use a closed fixed-reason profile; corrupt positive/secret fields fail as ledger_corrupt without being copied into query results. Malformed public correlation IDs are usage errors (exit 2) before message input; private receipt validation remains an operation failure.

## Source-streamed SSH correlation-only route

Verified structured SSH-self addresses keep the local message limit after normalization, including prepared-ID reuse and dry-run. Actual SSH destinations still have the 90,000-character command-argument limit. The protected metadata child additionally bounds each encoded JSON input/output frame to 1 MiB, including UTF-8 encoding and JSON escapes; this can reject otherwise valid local-sized input before submission.

The sender uses one original per-destination budget for metadata, harmless target probing, durable intent, one native effect and cleanup. Its protected metadata child preserves From/Reply and structured SSH-self normalization without renewing detection timeouts. A consumed intent refuses before any new probe. Required remote delivered/acknowledged waits refuse before SSH submission; best-effort requests report unsupported and mint no capability, remote ledger or collector. The effect rechecks the original Claude generation or canonical Codex home/writer context. An explicit Codex generation request is unsupported, not silently removed. Read-only status/wait never invokes SSH submission.

The probe records an old remote boot-bound monotonic anchor before replying. Only after strict response validation does the sender compute both remaining durations from one original clock sample. The remote effect uses that old anchor plus the remaining durations, including exactly five seconds within the total for cleanup; changed boot, future/expired anchor or malformed context refuses before native effect. No UTC or cross-host clock origins are compared. SSH shutdown does not prove cancellation of remote submission or consumption. Home mapping is the trusted remote handler's actual expanduser/resolve result, including owned symlink aliases; it is not a sender-verified inode, atomic incarnation or universal native storage guarantee.

The actual command must enter the closed private handler before config/source/SSH children start. Hidden options are not authentication: the receiver quotes every external body line once. Strict native profiles and request/target/home/context proofs preserve complete submission evidence after disconnect, timeout, diagnostic overflow or interruption, but never salvage an overflowing protocol stdout prefix. Connection-verified SSH identity and safe reply metadata remain separate from native facts. Private proofs, body, clock anchors and native context are not reflected into public outputs. Owned fake-SSH/source-stream/SQLite/queue fixtures verify this candidate slice, not actual remote hosts, native model consumption or installed reverse-SSH receipt bootstrap.

## Evidence, privacy and timing

A complete inbox write is submission, not consumption or turn completion. Only an authenticated correlated receipt, or a separate explicit operator attestation with provable ordering, produces acknowledged. An early receipt during native write/drain is saved as bounded unclassified evidence. It is classified only after independently known submission and original-clock ordering; classification cannot invent a receipt or backdate an ACK. A committed ACK survives collector restart; unused authority expires conservatively on restart. Duplicates still require original token-hash proof, including after expiry. Manual confirmation never rewrites an old wait.

Ordinary Python Claude fields remain absent rather than synthesized as false/null. Known native facts survive wait failure. Pending wait interruption records stopped and returns complete structured output with exit 130; querying the stopped intent succeeds with exit 0. No status, wait, ACK or confirmation operation submits a native message. Missing/corrupt/unknown history has a separate handoffQuery error without a fabricated epoch. No automatic resend or fallback is allowed.

The per-destination budget starts before setup: 30 seconds by default, ASCII integers 1..60 only. Exactly five seconds are reserved within the total for cleanup. Observation/effect ends at total minus five seconds; timeout diagnostics describe that cutoff. Budgets at or below five seconds refuse before effect. OS filesystem/identity operations and fsync cannot be forcibly cancelled by this Python implementation; this is not a universal hard-real-time latency guarantee.

Raw receipt capabilities occur only in authorized native message input and private IPC/stdin, never sender/collector ordinary outputs, argv, URI, environment or own journal. The journal stores hashes. Native recipient queue/history may retain the delegated token, and same-user readers may acquire it: token_possession is not independent model identity. No recipient transcript is read.

## Storage and remaining gates

Clock investigation (2026-10-09): [CPython 3.9.25's Darwin clock](https://github.com/python/cpython/blob/0bbaf5de9744ae1acea3e2c9ad2257d1cc68e847/Python/pytime.c#L823) subtracts a process-local starting tick. The candidate therefore uses typed mach_absolute_time/mach_timebase_info nanoseconds on Darwin across sender, journal and collector, and native monotonic nanoseconds elsewhere. The inbox adapter receives only a translated remaining duration in its own clock domain, not persisted cross-process timestamps. Missing/invalid native clock proof refuses; earlier candidate records have a distinct clock-provider continuity namespace and are not silently reinterpreted. Tests compare real parent/child samples; macOS Python 3.9 acceptance is also checked by CI, not inferred from this Mac's Python 3.14 run.

The owner-only POSIX directory is ~/.local/share/session-peer/handoff. Atomic fsynced snapshots and a stable exclusive lock fence the first native attempt before effect; a crash after that fence remains unknown even if no write actually began. Reservation includes each intent's bounded future receipt/wait/native detail; the 10,000-intent and 32 MiB quotas include tombstones and reservations. Capacity never evicts effect fences. There are at most 64 distinct waits per intent.

Details compact to a target-bound tombstone after 30 days only under proven native boot/monotonic continuity; unavailable continuity conservatively retains detail and may exhaust quota sooner. Cross-boot retention needs further acceptance review. A known restored ledger must be quarantined with the explicit operator HandoffLedger.quarantine_restored() API before reuse; CLI restore/reconciliation tooling remains a gate. Archive/new initialization is an explicit operator action, never automatic repair. Unmarked rollback or cloned history cannot be detected reliably. A stale collector socket is not automatically removed or rebound. Collector lifetime is at most 24 hours; receipt TTL is 24 hours from the original durable intent commit and collector restart revokes unused authority. Lifecycle/expiry boundaries still require review against the complete design before release.

Verification uses owned local Unix inboxes, actual peer-PID/process-birth checks, private collector/producer subprocesses, owned SQLite/fake Codex queue children, bounded raw stdin, strict JSON, concurrent startup, crash fencing, late/duplicate/expired receipt and SIGINT fixtures. Fake queue/version metadata is not a real Codex session or universal native-version certification. Tests do not prove real Claude model consumption, a native Codex observer, remote bootstrap or Windows cleanup. #181 stays open until those remaining acceptance gates are resolved; the frozen design fixture is not runtime certification.
3 changes: 2 additions & 1 deletion docs/i18n-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"README.md": "bcaf0aeedb55ba1b07bdf2b6c42b37aeec7d844d82a3052c1a24b88f91ec7ba6",
"RELEASING.md": "bee4a16ddbdb9c1db30781b5310bce2808fafc1966e57083b7e4c6681225921d",
"SECURITY.md": "8f018f6f98002312e143609b9734aeb971ca5217e146597c06b57b5645031046",
"docs/README.md": "1b519e0ef378bae50938a24fba517c59ca5da8dcd79ff77cd956f736aa83082d",
"docs/README.md": "d08c0f55ae687a6bb29a951dddd7431b0e9d1b267169e7e774b6a06f819154ed",
"docs/agent-adapters.md": "bf185d3121c94829bb412071a25c4d943302e10a6b2e713111e653b8f8c3ea91",
"docs/ai-assistant-guide.md": "9624ec96b92fb688fae3be3c94cb3564c77985091be451fbf1f056a1af9b9d9c",
"docs/antigravity.md": "f2b5122a0ce6e6fb5bd4d8ed50ce2c8bd2dc9c98ec8ce328405cf7c2cd24229d",
Expand All @@ -20,6 +20,7 @@
"docs/contracts/handoff-v1.md": "46a953010c49214bd8f969412f26713384fbe09a0ba78ebf0fef75da40d0d889",
"docs/diagnostics.md": "0949e53612eeab2cd7c7bbda498e14d71e9cb1b1189aa7722030cb8702cd8481",
"docs/download-metrics.md": "ded60e7b84d14ce779f8d639516b4e390358a06ef76b677dc9c1f997fb626a9f",
"docs/handoff-runtime.md": "525e4fc4e34453d76fba35f77bdee854cbad6efe43541f3ae3307be69faba233",
"docs/mcp.md": "a64010cdd63f2cde6a0052450bed2d05faa0f19c93c76143cb0d585d36f8ff62",
"docs/multi-home-list.md": "f8d8e3adf727e4842ea7c4d45e8f6523b11d0513bf2509d4d1204645875e6489",
"docs/paired-devices.md": "44c70f99d3f8f8ff9f6f86d128913ada69f4cc4e794c724cfc214f7f8ea78f20",
Expand Down
1 change: 1 addition & 0 deletions docs/ja/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

## 開発と履歴

- [Handoff ランタイム候補](handoff-runtime.md)は、未公開の非公開受領確認実装、対応経路、残る受け入れ条件を説明します。
- [v1互換性契約](compatibility-v1.md)はstable、versioned、migrated、internal、experimentalなsurfaceを分類します。
- [エージェント転送アーキテクチャ](architecture/agent-transports.md)と[リレー開発計画](relay-development-plan.md)は実装境界を説明します。
- [v1.0.4ノート](releases/v1.0.4.md)は2026-10-07付の現在のメンテナンス版を説明し、[v1.0.3ノート](releases/v1.0.3.md)は以前のセキュリティ版の履歴を、[検証記録](validation/69-rc.md)は範囲付きの証拠と制限を保存します。
Expand Down
Loading
Loading