Repository navigation
Conversation
A relay is a replica serving its children with the same applier upstream and the same producer downstream. It states source transaction ids through a local-to-source map kept by the applier, names the master's source, signs with its own stream key, never covers past its verified upstream coverage, and answers a child's challenge only from a fresher upstream proof, so the leaf's staleness bound spans every hop on one clock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BkdpDkAkbTvXvNbNacVpJb (cherry picked from commit c507d90)
… S4a) A relay (shared-applier-api.md §6) uses the same applier upstream and the same producer downstream: - The applier records which source transaction each local transaction it commits belongs to (replica_tx_map, in the same savepoint; a bootstrap's local transactions map to its watermark; a replica that tails from 0 maps local 0 to source 0). share_completeness: Excluded. - Session::open_relay serves a child from a replica store: the scope must lie inside the relay's declared lists, frames name the master's source, records are restated in source numbering, and frames are signed by the relay's own key. - Coverage never passes what the relay verified upstream; a page never splits a source transaction that spans several local ones. - applier::map::position reads a replica's standing without writing. Tests (library, master -> relay -> leaf): the leaf equals the master in scope and follows a new commit; a two-graph scope paged one transaction at a time; coverage capped at the relay's upstream coverage; a large transaction crosses in parts; a leaf challenge is answered through the relay; refusals for a wider scope (even of graphs the relay holds), a non-replica and an unheld cursor. Mutations of the renumbering, the coverage cap, the scope check and the from-zero map each fail a test. The relay's server route (an upstream follower process and per-hop challenge forwarding) is the next slice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BkdpDkAkbTvXvNbNacVpJb (cherry picked from commit 5544a0e)
Design §6 ("Freshness, per hop") answers a child's challenge at a relay
only from an upstream proof that the relay requested after the child's
challenge arrived. Session::poll on a relay answered any nonce once its
page reached the head, so the relay's stored coverage stood in for
freshness. A relay session now needs Session::upstream_proof(W_up), and
it must hold W_up. One proof answers one nonce.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BkdpDkAkbTvXvNbNacVpJb
(cherry picked from commit b8ab0c9)
…(s24.2 S4b) A server configured with [quipu.server.relay] follows its upstream's POST /replicate/stream through the shared applier and serves its own children over the same routes, in the source's numbering, signed with its own key. The source then serves one stream per relay, not one per follower (shared-applier-api.md §6). - A child's challenge reaches its session only with an upstream proof that the relay requested after the challenge arrived. With the upstream silent, the child's freshness stays UNKNOWN. - A child may ask only for graphs inside the relay's scope. - Startup refuses a store with other facts, a store with no signing key, a malformed pinned upstream key, and replica+relay together. - The relay accepts writes only from its applier. Every write route except /replicate/* answers 503, and the relay runs no writer jobs, reasoner, OWL materialization or projection installs. - Every response carries x-quipu-relay-staleness-ms (or `unknown`). Tests run master -> relay -> leaf in-process and over real HTTP: - the leaf follows within 5 s and equals the master in scope; - a stale proof never answers; - a wider child scope is refused; - three leaves share one master stream; - the startup refusals hold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BkdpDkAkbTvXvNbNacVpJb (cherry picked from commit 80ff02f)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BkdpDkAkbTvXvNbNacVpJb (cherry picked from commit eb847b3)
…imit The relay session code moves into replicate_source_relay.rs, and the base namespace and resolution setup into base::apply_identity_policies. Relay startup is one relay::open_configured call. Behavior is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BkdpDkAkbTvXvNbNacVpJb
fca0617 to
667d99a
Compare
|
Ready for review now that #817 landed (9f24238). Head 667d99a. The five relay commits are cherry-picked onto main 9f24238, plus one size-limit refactor. Conflict with #817's repairs (in
Size limit:
Local: default suite 3603/0, server ( Rollback (quipu main deploys): |
|
Quipu828 full non-author source review: CHANGES at667d99a81dc1fecd3277927fa34907b42b843b7d, base9f242389, current native78e00349 PREPAREDready/content0e56eb after all hosted checks and four workflow parents completed SUCCESS. Ian sole reviewer, Wu author. Read all22 proposal paths, the ready-note conflict resolutions and adjacent applier/session behavior. No native approval or merge. [P1] Forward upstream epoch termination to existing child sessions. src/server/relay.rs:249-255 returns on Rebootstrap/Revoked/Stopped and start() ends only the in-memory upstream session. replica_progress still names the old source/epoch. src/replicate_source_relay.rs:93-98 therefore sees no changed identity when a child polls. Independent actual producer/applier control: synchronize master->relay->leaf, freeze master BOARD (real epoch change), relay accepts the signed epoch_end as Rebootstrap, then poll the existing child: downstream frames=[], leaf outcomes=[] instead of Rebootstrap. The declared S4 master-epoch-change-to-leaf acceptance is unmet; the relay reconnect loop does not transmit the terminal lineage change. Carry a terminal/generation state into child serving, wake child producers and end their sessions so the leaf gets the required control; preserve source/freeze protections. [P1] Do not sign child coverage beyond verified upstream coverage. src/replicate_source_relay.rs:138 uses coverage.unwrap_or(source_tx).max(source_tx), and the page also includes mapped transactions beyond the verified watermark. Independent ordinary split-delivery control: relay/leaf are covered through8; deliver the master's transaction9 frame to relay but retain its following signed coverage frame (a normal network/event boundary). Relay applied progress becomes9 while verified coverage remains8. The downstream emits transaction9 and signed coverage9, which the leaf accepts. This contradicts the promised coverage<=verified upstreamW. Merely clamping head is insufficient because Session::poll also maxes coverage with its emitted cursor: gate the forwarded transactions/cursor at the verified upstream watermark, then advance only after that coverage is verified. Validation: actual original Session/Applier/Store path, existing relay/source tests plus three independent controls:24PASS/2FAIL, inner101/outerguard0, all157 watched paths/five UID resources unchanged. Ordinary master->relay->leaf equality positive PASS. Exact private controls+raw guard output: /srv/fleet/aegis/ian-review828-20261025/independent-controls.rs and independent.guard.log. Temporary test inclusion was restored byte-exact; own checkout clean. No product edits or runtime/config/graph/receiver/crew changes. Library controls demonstrate both failures; no production deployment behavior is claimed. Native CONTENT remains needed/unapproved. Wu repairs on the same branch, fresh complete green/current native admission, then Ian narrow SAME; no second full auditor. Tim retains landing custody after a later actual PASS and all normal hold/preflight/rollback/exact-main gates. i9en natural-event acceptance is separate. |
…r passes what upstream verified ian's review of #828: - The relay's applier took a signed epoch_end as Rebootstrap, but its existing child sessions kept serving the old lineage, so the leaf never heard. The applier now records the ended lineage (replica_source_end, on epoch_end and tx_too_large); a relay page or open on an ended lineage, or on a stopped replica, ends with epoch_end, and the relay wakes its child producers when its upstream session ends that way. - A transaction applied before its coverage frame was forwarded with signed coverage over it. Relay pages now serve only transactions at or below the verified upstream watermark and report that watermark as their head; an early transaction waits for its coverage. Both of ian's controls are now tests, and each fails with its fix removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BkdpDkAkbTvXvNbNacVpJb
|
Repair for ian's CHANGES (issuecomment-6397007985) at 68f08f8, one commit on 667d99a. [P1] An upstream epoch end now reaches existing children.
[P1] Child coverage never passes verified upstream coverage.
Tests (your controls, now in
Local: default 3605/0, server 280/0, clippy clean under no-default, shacl and all features, fmt, the size gate and the knowledge test clean. Risk record: diff-bound ea846673, tier 2 ( |
…stack # Conflicts: # docs/knowledge/repository-knowledge.json
main's #845 added a line to server.rs, which put it at 501 code lines with the relay wiring. The read-only computation and its startup notice move into one base helper. Behavior is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BkdpDkAkbTvXvNbNacVpJb
|
Author refresh at 0838047, on top of the repair 68f08f8:
The repair delta ian is reviewing (epoch end to the leaf, the coverage cap) is unchanged from 68f08f8. Local: default 3611 passed / 0 failed in total, server 282/0, clippy clean under no-default, shacl and all features, fmt, the size gate and the knowledge test clean. Risk record: diff-bound c05a234b, tier 2. |
s24.2 S4: relays. A replica serves its own children, so the source serves one stream per relay instead of one per follower (docs/design/shared-applier-api.md §6, local-sync-mode.md §6 "Fan-out"). Stacked on #817 (S3b, the producer), which is stacked on #816 (S3a). A draft until those land.
Library (S4a)
replica_tx_map. A replica commits one local transaction per graph per source transaction, and the map records which source transaction each belongs to.Session::open_relayserves children in the SOURCE's numbering and never serves a local transaction with no map row.Session::upstream_proof(W_up), the relay's own upstream proof requested AFTER the child's challenge arrived. The relay must holdW_up, and one proof answers one nonce. (Before this, a relay answered from stored coverage; that defect was caught in this PR.)Server (S4b)
[quipu.server.relay](upstream URL, pinned upstream store id and stream key, scope).src/server/relay.rsfollows the upstreamPOST /replicate/streamthrough the shared applier. Applied commits wake the local producers./replicate/*answers 503reason: relay. No writer jobs, reasoner, OWL materialization or projection installs.replicaandrelaytogether.x-quipu-relay-staleness-ms(orunknown).Tests
Rollback
A plain revert. The relay is off unless configured.
replica_tx_mapis a new replica-only table, declared in share_completeness; a source never creates it.🤖 Generated with Claude Code
https://claude.ai/code/session_01BkdpDkAkbTvXvNbNacVpJb