Expand description
Multi-device sync core for CAR — the oplog + deterministic fold (slice B1
of docs/proposals/multi-device-sync.md).
The proposal’s frame: sync events, not files. Every state-changing
operation is appended to a content-addressed, append-only, replica-tagged
oplog::OpRecord log; sync is “send me the ops I don’t have”; and each
device fold::folds the full op-set into materialized state
deterministically — commutative, associative, and idempotent over the
op-set (CRDT properties), so two laptops writing simultaneously converge
the moment they exchange ops.
What this slice ships (pure, library-only — no network, no daemon wiring):
oplog—oplog::OpRecordexactly as the proposal specs it (op_idcontent-derived,hlc {wall_ms, counter, device_id},scope: Personal | Shared{org}, the eight-variantsurfaceenum, surface-specificpayload), plus the per-deviceseq/prevhash-chain linkage that makes a device’s log order-verifiable (oplog::verify_log) and theoplog::DeviceLogwriter that stampsoplog::Hlcvalues from the hybrid clock (see the B3 note below).- [
fold] —fold::fold(ops) ->fold::SyncStateunder the proposal’s per-surface fold rules: grow-only union by stable ID for the log tier, LWW-register per record ordered by HLC for the registry tier, and ordered-observationfold::SyncState::replayfor the path-dependent routing tier (“sync the observations, not the result” — the EMA apply is injected, execution stays out of the crate).fold::state_hashis the divergence-detection invariant (“same frontier ⇒ same snapshot hash”), andfold::registry_as_lwwprojects a folded registry ontocar_state::crdt::LwwMapso the fold provably agrees with the shippedcrdt_mergeprimitives where the domains overlap. journal— durable JSONL persistence for the log in thecar-eventlogjournal idiom: append-only, torn-line tolerant on load, plus B4’sjournal::OplogJournal::truncate_to(atomic temp+rename rewrite under the existing advisory lock, stamping ajournal::TruncationMarkerthat fences the naiveload+resumepath into a runtime error — a truncated tail resumes only throughcheckpoint::resume_anchored).checkpoint(B4) —checkpoint::Checkpoint: a serialized fold at a frontier — per-device{seq, hlc, head}frontier entries, covered scopes, thefold::SyncStatesnapshot,fold::state_hashas the divergence invariant, and a whole-recordcheckpoint_hash(frontier + scopes + state) as the content address / file name — so “same file ⇒ same checkpoint” holds even when two frontiers fold to one deduped state, and a tampered frontier is rejected on load.checkpoint::verify_anchoredproves a truncated tail continues the checkpoint’s recorded chain heads (the checkpoint IS the anchored head);checkpoint::resume_anchoredresumes a device chain past a truncation without forking.fold::fold_ontois the consumption primitive:fold_onto(checkpoint.state, tail) == fold(full log).compact(B4) — per-surface retention (compact::RetentionPolicy::proposal_default: conversations last-N, runs 50/agent + 30 days, trajectories last-D, knowledge/skills/routing keep-all — every dropped id-bearing entry leaves a minimal tombstone stub sosupersedesreferences resolve even when they arrive after compaction, and event-stream trims are rejected), the monotone-onlycompact::AckTablefold-frontier bookkeeping (an ack asserts durably-folded state — MUST, binding on B3), andcompact::compact_and_truncateenforcing the crash-ordering invariant checkpoint durable FIRST, then truncate — acknowledged data is never lost, and compaction refuses to drop anything above ANY device’s acked frontier.
Determinism discipline (the proposal’s “free property” depends on it):
all folded state lives in BTreeMaps — no HashMap iteration order, no
wall-clock reads anywhere in the fold/retention path (the age rules’
reference instant defaults to compact::as_of_from_ops, pure over the
below-frontier ops).
B3 adds the missing middle — how ops actually travel:
oplog::HlcClock— the real hybrid logical clock:{wall_ms, counter}state with the standard send/receive rules (max of local wall and everything witnessed; counter ticks on ties), monotone under clock skew, regression, and same-millisecond bursts. It replaces B1’s pure-Lamport stamp source behind the SAME wire shape, exactly as promised —DeviceLog::newstill defaults to the degenerate logical (always-0 wall) mode, and wall readings are injectable (oplog::WallClock;oplog::system_clockis the one opt-in place system time exists in this crate).relay— therelay::Relaytrait (push/pull(since seq frontier) → {ops, latest_checkpoint_ptr}/ack/checkpoint_put/get/roster) with two reference implementations:relay::InMemoryRelayand the shared-directoryrelay::FsRelayloopback (the single-user two-Mac case). The relay admits only ops that continue a device’s relay-held chain (fork = runtime error), computes the stable frontier =min(acked)over non-evicted roster devices, marks a device silent past the horizonHrelay::DeviceStatus::Evicted(its ack no longer pins GC; reinstated on a caught-up ack), and GC-drops an op only when it is both at/below the stable frontier AND covered by a stored checkpoint — checkpoints dedup oncheckpoint_hash, the whole-record content address, neverstate_hash(the B4 contract).session—session::SyncSession, the device-side pump holding the B1/B4 contracts by construction: append journals (flushed) before an op is pushable (journal-durable before transmit); pulls are verified before folding; folds are journaled before the ack, whose value is derived from journal-held ops only (acking merely-received state is impossible). Retry-safe at every crash point (op_iddedup both ways). Cold bootstrap / straggler re-entry issession::SyncSession::bootstrap/session::SyncSession::rebase:checkpoint_get+pull(since = checkpoint frontier)+checkpoint::resume_anchored— neverDeviceLog::resume— with locally-held uncovered ops (a returning straggler’s unpushed writes) carried across the rebase and pushed after.
B5 adds execution lease + fencing — single-leader execution layered on top of the leaderless replication above:
lease— thelease::LeaseCoordinatortrait: a linearizable compare-and-swap register per agent (exactly one holder at a time; a new acquire after TTL-expiry or release bumps the monotoneepoch= the fencing token). It is deliberately separate fromrelay::Relay— an eventually-consistent relay structurally cannot host a lease (no consensus).lease::InMemoryLeaseCoordinatoris the honest in-process reference (Arc<Mutex>CAS is genuinely linearizable in one process); a distributed backend is B6. The lease register holds only non-sensitive metadata, so it never breaches the E2E guarantee on the actual agent data (the proposal’s data/control-plane split).- Fencing as a fold property, over two views — the leased
oplog::Surface::Intentsurface (fold::FoldTier::Leased) carries theepoch, and the fold yields (a)fold::SyncState::committed_run, the fence-independent, keep-all idempotency ORACLE (survives epoch bumps AND compaction — the correct “did this run already execute?” lookup), and (b)fold::SyncState::intent, the “who holds now” view where pending intents are per-agent fenced (a stale zombie’s pending loses deterministically, order-independently, without a wall-clock race — fencing beats HLC) while committed/failed are terminal-immune. Idempotency keys on the B7car_proto::deterministic_run_id. This converges the ledger and provides the durable oracle; it is NOT exactly-once execution — that is B6’s dispatch fence (a linearizable “still epoch N?” plus the oracle read, before the external effect). Seeleaseandsession::SyncSession::record_intent(terminal-guarded).
B2 adds transcript resume — the conversation surface as an ordered, role-threaded projection of the oplog:
conversation—fold::SyncState::transcriptfolds theoplog::Surface::Conversationentries for oneconversation_idinto a causally-orderedVec<conversation::Turn>(the crate’s canonical(hlc, op_id)order — two devices talking to the same agent concurrently interleave deterministically), andfold::SyncState::resume_messagesreturns the repaired, provider-validcar_inference_types::Messagesequence car-inference’s multi-turn path replays to continue the conversation — the verbatim conversation-resume APIdocs/solutions/conversation-persistence-removed-in-0.25.mdsays does not exist today. A conversation turn is an event stream keyed byop_id(op identity IS turn identity — the kernel-review correction: content keying silently dropped two genuine same-timestamp turns), so a resent op dedups but two distinct authorings never collapse; it differs from routing only in being an independent multiset (no path-dependent replay), so it toleratesLastNretention. Because HLC order is deterministic but says nothing about concurrent turns,resume_messagesruns a repair (coalesce adjacent same-role turns, drop orphan/dangling tool exchanges) so theMessagesequence is never provider-invalid — the “runtime validates” thesis applied to the projection. The 0.25 compaction-vs-store incoherence cannot recur: the oplog is the one source of truth and the transcript is a projection of the same folded state B4’s checkpoint serializes. Built on the sharedcar-inference-typescrate, so aMessageshape change is a compile error here, not a runtime break in B6.
Later slices: rerouting today’s file write paths through the oplog and the
daemon/memgine adoption of transcript resume (B6), the sync.* WS/FFI
surface + E2E encryption + checkpoint/op signing + per-scope streams + the
distributed lease coordinator (B6 — the network backend speaks the
relay::Relay and lease::LeaseCoordinator contracts).
Re-exports§
pub use checkpoint::resume_anchored;pub use checkpoint::verify_anchored;pub use checkpoint::AnchorError;pub use checkpoint::Checkpoint;pub use checkpoint::CheckpointError;pub use checkpoint::FrontierEntry;pub use compact::apply_retention;pub use compact::as_of_from_ops;pub use compact::compact_and_truncate;pub use compact::is_tombstone;pub use compact::plan_compaction;pub use compact::AckTable;pub use compact::CompactError;pub use compact::CompactionOutcome;pub use compact::CompactionPlan;pub use compact::RetentionPolicy;pub use compact::RetentionReport;pub use compact::RetentionRule;pub use compact::RUNS_MAX_AGE_MS;pub use compact::RUNS_MAX_PER_AGENT;pub use conversation::Role;pub use conversation::Turn;pub use conversation::DEFAULT_CONVERSATION;pub use crypto::derive_ed25519_identity;pub use crypto::derive_key;pub use crypto::derive_x25519_identity;pub use crypto::ed25519_verifying;pub use crypto::encryption_audience;pub use crypto::generate_org_key;pub use crypto::parse_ed25519_verifying;pub use crypto::parse_x25519_pub;pub use crypto::require_canonical_org;pub use crypto::unwrap_org_key;pub use crypto::wrap_org_key;pub use crypto::x25519_public;pub use crypto::CryptoError;pub use crypto::DerivedKeyProvider;pub use crypto::Envelope;pub use crypto::KdfProfile;pub use crypto::LocalKeyCipher;pub use crypto::PayloadCipher;pub use crypto::StretchedMaster;pub use crypto::SyncKeyProvider;pub use crypto::WrappedOrgKey;pub use crypto::ALG_CHACHA20POLY1305;pub use crypto::ALG_ORG_KEY_WRAP;pub use fence::check_dispatch;pub use fence::FenceDecision;pub use fold::fold;pub use fold::fold_at;pub use fold::fold_onto;pub use fold::hlc_version;pub use fold::registry_as_lww;pub use fold::state_hash;pub use fold::FoldTier;pub use fold::FoldedRecord;pub use fold::IntentAgent;pub use fold::SyncState;pub use journal::OplogJournal;pub use journal::TruncationMarker;pub use lease::InMemoryLeaseCoordinator;pub use lease::Intent;pub use lease::IntentStatus;pub use lease::Lease;pub use lease::LeaseCoordinator;pub use lease::LeaseError;pub use net_relay::LeaseWire;pub use net_relay::LoopbackTransport;pub use net_relay::NetworkLeaseCoordinator;pub use net_relay::NetworkOrgKeyDirectory;pub use net_relay::NetworkRelay;pub use net_relay::OrgKeyTransport;pub use net_relay::SyncTransport;pub use net_relay::TransportError;pub use oplog::canonical_json;pub use oplog::logical_clock;pub use oplog::system_clock;pub use oplog::verify_log;pub use oplog::ChainError;pub use oplog::DeviceLog;pub use oplog::Hlc;pub use oplog::HlcClock;pub use oplog::OpRecord;pub use oplog::Scope;pub use oplog::Surface;pub use oplog::WallClock;pub use org_key_directory::FsOrgKeyDirectory;pub use org_key_directory::InMemoryOrgKeyDirectory;pub use org_key_directory::MemberPublicKey;pub use org_key_directory::OrgKeyDirectory;pub use org_key_directory::OrgKeyDirectoryError;pub use org_key_directory::OrgKeyDirectoryState;pub use org_key_provider::OrgAwareKeyProvider;pub use org_key_resolver::resolve_org_root;pub use org_key_resolver::ResolvedOrgRoot;pub use org_rotation::provision_org_members;pub use org_rotation::resolve_all_org_roots;pub use org_rotation::rotate_org_key;pub use org_rotation::sign_rotation_floor;pub use org_rotation::verify_rotation_floor;pub use org_rotation::ProvisionReport;pub use org_rotation::RotationError;pub use org_rotation::RotationFloor;pub use partition::is_portable;pub use partition::policy_for;pub use partition::portable_domains;pub use partition::SurfacePolicy;pub use partition::SyncClass;pub use partition::SURFACE_POLICIES;pub use relay::checkpoint_frontier;pub use relay::frontier_of;pub use relay::AckOutcome;pub use relay::DeviceStatus;pub use relay::Frontier;pub use relay::FsRelay;pub use relay::GcReport;pub use relay::InMemoryRelay;pub use relay::PullResult;pub use relay::PushOutcome;pub use relay::Relay;pub use relay::RelayConfig;pub use relay::RelayError;pub use relay::RosterEntry;pub use session::PumpReport;pub use session::SessionError;pub use session::SyncSession;
Modules§
- checkpoint
- Checkpoints — a serialized fold at a frontier (slice B4 of
docs/proposals/multi-device-sync.md, §“Snapshots: bounding the oplog”). - compact
- Compaction + oplog GC (slice B4 of
docs/proposals/multi-device-sync.md, §“Deep dive: the checkpoint / compaction / GC protocol”). - conversation
- Transcript resume: the conversation surface as an ordered, role-threaded
projection of the oplog (slice B2 of
docs/proposals/multi-device-sync.md), hardened by kernel review. - crypto
- End-to-end payload encryption boundary (slice B6 of
docs/proposals/multi-device-sync.md, §“Transport: Parslee-hosted relay, E2E for personal scope”). - fence
- The executor dispatch fence (slice B6 of
docs/proposals/multi-device-sync.md, §“Layer 2 (safety): the idempotency / fencing gradient” + §“Deep dive: the execution-lease / fencing protocol”). - fold
- The deterministic fold:
fold(ops) → materialized state. - journal
- Durable JSONL persistence for the oplog — the
car-eventlogjournal idiom: append-only, one record per line, torn-line tolerant on load. - lease
- Execution lease + fencing (slice B5 of
docs/proposals/multi-device-sync.md, §“Deep dive: the execution-lease / fencing protocol”). - net_
relay - Network sync backend — CAR’s client for a remote relay + lease register
(the Parslee sync service), behind the same
Relay/LeaseCoordinatortraits the local [FsRelay] /InMemoryLeaseCoordinatoralready satisfy. - oplog
- The append-only, replica-tagged operation log.
- org_
key_ directory - Org-key directory — the publish/fetch surface for the client-side org-key
agreement (the follow-up named in
crypto.rsalongside the mergedwrap_org_key/unwrap_org_keyprimitives). - org_
key_ provider OrgAwareKeyProvider— theSyncKeyProviderthat encrypts org-scoped ops under the SHARED org keyK_org(mutually readable across an org’s members) while personal ops keep the per-user key. This is the activation core of the org shared brain: swapping it in is what makesScope::Shared { org }ops readable by every member instead of only their author.- org_
key_ resolver resolve_org_root— the out-of-band resolver that turns a member’s published wraps into their org master keyK_org, for feedingcrate::org_key_provider::OrgAwareKeyProvider. This runs OFF the hot path (at subsystem open), never insidecipher_for.- org_
rotation - Org-key ROTATION on member removal (slice 10) — the granter-side primitive that bumps the epoch, plus the authenticated freshness marker that decides which epoch is allowed to author the org’s present.
- partition
- What syncs across a user’s devices, what stays device-local, and how a synced policy is reconciled when the devices run different operating systems.
- relay
- Relay transport — the account-scoped op-stream devices push/pull against
(slice B3 of
docs/proposals/multi-device-sync.md, §“Transport” + §“Stragglers and the single coherence knob” + §“Sync protocol surface”). - session
- The device-side sync session — the pump that drives a
DeviceLog+OplogJournalpair against aRelay(slice B3 ofdocs/proposals/multi-device-sync.md).
Structs§
- OrgSigning
Key - ed25519 signing key which can be used to produce signatures.
- OrgVerifying
Key - An ed25519 public key.