From 6364988453ce9f22dfd720a6e3fd2194664818c0 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Sun, 30 Aug 2026 16:16:44 +0000 Subject: [PATCH] Record the final Replicator capability contract --- devs.md | 2 +- ...eplicator_capabilities_01_core_contract.md | 30 ++++-- ...r_capabilities_02_p2p_service_lifecycle.md | 77 +++++++++------ ...plicator_capabilities_03_migration_plan.md | 99 +++++++++++++++---- docs/settings.md | 8 ++ updates.md | 15 +++ 6 files changed, 177 insertions(+), 54 deletions(-) diff --git a/devs.md b/devs.md index 2367f2a5..a8a3eb26 100644 --- a/devs.md +++ b/devs.md @@ -167,7 +167,7 @@ Legacy modules remain grouped by directory: - **Service Hub** (`src/modules/services/`): Central service registry using dependency injection - **Common Library** (`@vrtmrz/livesync-commonlib`): Platform-independent synchronisation logic, shared with the CLI, WebApp, WebPeer, and external tools -Commonlib owns one stable `LiveSyncP2PService`, its `P2PRoomSessionOwner`, and the replaceable Trystero room session. Host commands, event handlers, and views consume the focused transport, directory, admission, transfer, change-relay, configuration, and diagnostic views returned by the service feature. They must not retain the deprecated compatibility Replicator as an ordinary service locator, close Trystero-owned raw peers, or install another Trystero transport generation at the application root. The exact as-built ownership and shutdown boundaries are recorded in Commonlib's `docs/p2p-transport-lifecycle.md` design document. +Commonlib owns one stable `LiveSyncP2PService`, its `P2PRoomSessionOwner`, and the replaceable Trystero room session. Host commands, event handlers, and views consume the focused transport, connection-probe admission, directory, peer-admission, transfer, change-relay, configuration, and diagnostic views returned by the service feature. They must not retain the deprecated compatibility Replicator as an ordinary service locator, close Trystero-owned raw peers, or install another Trystero transport generation at the application root. The exact as-built ownership and shutdown boundaries are recorded in Commonlib's `docs/p2p-transport-lifecycle.md` design document. ### Conflict Merge Policy diff --git a/docs/adr/2026_08_replicator_capabilities_01_core_contract.md b/docs/adr/2026_08_replicator_capabilities_01_core_contract.md index fb75c078..0d9662db 100644 --- a/docs/adr/2026_08_replicator_capabilities_01_core_contract.md +++ b/docs/adr/2026_08_replicator_capabilities_01_core_contract.md @@ -393,6 +393,12 @@ same upper bound and expose only relevant permissions. An unattended path may use persisted or automatic acceptance policy, but cannot obtain local interaction authority implicitly. +The same authority bounds presentation. An unattended P2P path may retain an +informational diagnostic, but no-target, authentication, tweak-mismatch, and +overlapping-transfer settlements must not promote themselves to a Notice. +User-initiated paths retain their existing Notice-level presentation. This is +a caller-authority rule, not a new process-wide presentation framework. + Outcomes do not collapse failure into `void` or `boolean`: ```typescript @@ -506,6 +512,9 @@ decision produced by one publication to its replacement. An edited-settings trial is different: it owns an independent Replicator and connection, never borrows the active publication, and disposes both resources. +An owned Security Seed resource also forces a fresh provider read for its +settings snapshot. Reusing a process-cached synchronisation parameter would +turn an observation made for an earlier flow into current trial evidence. Application suspension is a reversible host pause, not an ownership transition. `ReplicatorService` orders the provider's transfer-stop request but @@ -530,12 +539,14 @@ replication batch loop consume the same effective signal. An already-started atomic database operation may settle before cancellation completes, but no new batch is started afterwards. -`getNewReplicator()` is not a general temporary-instance API. Setup and settings -flows request narrow probes, such as connection, preferred-tweak, or isolated -P2P signalling validation. A resource-returning factory returns an owned -resource with idempotent asynchronous `dispose()`. Trial settings are passed -to the probe itself and cannot silently read active settings. A probe cannot -replace the active Replicator or the P2P service. +`getNewReplicator()` is not a general temporary-instance API. CouchDB and +Object Storage Setup and settings flows request narrow connection or +preferred-tweak probes. A resource-returning factory returns an owned resource +with idempotent asynchronous `dispose()`. Trial settings are passed to the +probe itself and cannot silently read active settings. P2P Setup instead uses +the stable service's connection-probe admission described in Part 2. Only its +idle continuation constructs and disposes a short-lived raw signalling trial. +Neither form can replace the active Replicator or the P2P service. Streaming Fetch receives an owned Security Seed resource bound to its settings snapshot. The current compatibility implementation may construct an @@ -598,6 +609,13 @@ The public contract is named `CentralRemoteAdministration*` because every current action, observation, failure, and postcondition belongs to that central milestone protocol. Established CLI command names remain unchanged. +The P2P Setup signalling check is not the P2P entry in the provider-owned +connection-probe row. P2P has no central connection resource; its +`P2PConnectionProbeAdmission` is a focused view of the independently composed +P2P service. It compares requested relays with the binding held by the existing +room-session owner as specified in Part 2; it adds neither a process-global +lease nor a second owner. + The following concerns deliberately stay outside this capability matrix: | Concern | Current owner | diff --git a/docs/adr/2026_08_replicator_capabilities_02_p2p_service_lifecycle.md b/docs/adr/2026_08_replicator_capabilities_02_p2p_service_lifecycle.md index d9a174bf..4f321d08 100644 --- a/docs/adr/2026_08_replicator_capabilities_02_p2p_service_lifecycle.md +++ b/docs/adr/2026_08_replicator_capabilities_02_p2p_service_lifecycle.md @@ -88,27 +88,29 @@ temporary decisions, advertisements, clients, listeners, and feeds do not. The underlying WebRTC peer remains under Trystero's shared-peer ownership as specified by the accepted lifecycle record. -### Expose seven narrow contract views +### Expose narrow contract views The service does not expose a room session, raw host, or concrete Replicator to -ordinary consumers. It supplies these seven views over the same owner: +ordinary consumers. It supplies these views over the same owner: 1. `P2PTransportLifecycle` observes room state and accepts explicit, user-owned connect or disconnect requests. -2. `P2PPeerDirectory` supplies peer snapshots and peer arrival or departure. -3. `P2PPeerAdmission` evaluates incoming peers and administers temporary or +2. `P2PConnectionProbeAdmission` arbitrates a complete Setup signalling check + against the current relay binding without exposing or replacing the room. +3. `P2PPeerDirectory` supplies peer snapshots and peer arrival or departure. +4. `P2PPeerAdmission` evaluates incoming peers and administers temporary or persisted acceptance decisions. -4. `P2PTargetedTransfer` performs pull, requested push, and bidirectional +5. `P2PTargetedTransfer` performs pull, requested push, and bidirectional finite synchronisation against an explicit peer, and executes the persisted configured-target set without interactive peer selection. -5. `P2PChangeRelay` administers peer watch and local-change broadcast. -6. `P2PConfigurationExchange` performs peer configuration exchange under its +6. `P2PChangeRelay` administers peer watch and local-change broadcast. +7. `P2PConfigurationExchange` performs peer configuration exchange under its declared interaction authority. -7. `P2PDiagnostics` supplies status and RTC diagnostics without exposing raw +8. `P2PDiagnostics` supplies status and RTC diagnostics without exposing raw room or peer connections. -These are stable service-level contract views, not seven wrapper allocations or -independent state owners. One implementation may satisfy several views. +These are stable service-level contract views, not independent wrapper or state +owners. One implementation may satisfy several views. Advertisement and admission state remain under one peer-access owner, while pull, push, and bidirectional transfer remain under one transfer owner. A consumer which needs more than one view receives those views explicitly; it @@ -303,24 +305,38 @@ the trigger-aware readiness policy rather than bypassing readiness, pending-file settlement, clean-up, or version gates. Fetch and Rebuild retain their separately authorised bypasses. -### Keep probes isolated from the active service +### Arbitrate Setup signalling checks through the room owner -Setup and settings use a separately owned `P2PConnectionProbe`. It does not -borrow or replace the current room session, and its `dispose()` cannot close the -active service. Because the current implementation may use process-global -relay sockets, a probe must either allocate isolated transport resources or the -host must reject concurrent probing while the active service uses those -sockets. It must not pause or close active relay sockets as a side effect of a -short-lived check. +The current Setup check establishes only whether its signalling transport can +be opened. It does not validate peer discovery, room credentials against +another device, or a TURN or WebRTC data path. The host therefore receives a +`P2PConnectionProbeAdmission` view over the existing room-session owner instead +of constructing an uncoordinated second transport. -The probe owns the same kind of lifetime controller as a room session, and -`dispose()` aborts and settles its work before releasing its own room. This -makes probe retirement bounded, but it does not turn process-global Trystero -relay state into isolated state. Runtime acquisition therefore reuses or -observes a compatible active session where the probe requires no second room, -allows a separately owned logical room only when the effective relay binding is -compatible, and otherwise returns a blocked result. It does not silently retire -the active service to make an incompatible probe possible. +The admission receives the requested relay settings and a continuation which +owns one short-lived raw signalling trial. The room owner serialises the whole +decision on its existing lifecycle queue: + +- a serving room whose active relay set covers every requested relay returns + `observed-active` without entering the continuation; +- a serving room which does not cover the requested relay set returns the + stable `active-p2p-relay-binding-conflict` decision code without entering the + continuation; and +- an idle owner runs the continuation and does not settle admission until the + caller has disposed the raw Replicator and its temporary database. + +Relay admission uses the same split-and-trim projection as transport setup, +then compares de-duplicated sets. It does not infer URI equivalence which the +Trystero relay key does not implement. The continuation must not await another +lifecycle transition on the same service while it holds this serialisation +boundary. + +This view adds neither a second room owner nor a process-global relay lease. +It does not change raw `TrysteroReplicator.dispose()` semantics, pause or close +an active relay, or silently retire the active service to make an incompatible +trial possible. A future check which genuinely needs peer-, room-, TURN-, or +WebRTC-level evidence requires its own bounded contract rather than widening +this signalling-only result implicitly. ### Keep provider composition explicit @@ -332,8 +348,9 @@ and does not publish a second P2P lifecycle owner. ## Consequences -- P2P room, peer, watch, acceptance, transfer, configuration, and diagnostics - have one explicit owner and seven focused contracts. +- P2P room, signalling-check admission, peer, watch, acceptance, transfer, + configuration, and diagnostics have one explicit owner and focused + contracts. - Disposing an active adapter cannot close a policy-owned or adjunct room. - Explicit user disconnect has a clear veto boundary and cannot be undone by AutoStart or a finite operation. @@ -341,7 +358,9 @@ and does not publish a second P2P lifecycle owner. - Settings and local database replacement cannot publish mixed-session state. - Reconnects do not repeat completed baseline transfers merely because the room epoch changed. -- Setup probes can validate P2P without mutating the active transport. +- Setup can observe a compatible active signalling binding or run an idle + trial without mutating the active transport; an incompatible active binding + is reported explicitly. ## Non-goals diff --git a/docs/adr/2026_08_replicator_capabilities_03_migration_plan.md b/docs/adr/2026_08_replicator_capabilities_03_migration_plan.md index 072248a2..1e3f6c9b 100644 --- a/docs/adr/2026_08_replicator_capabilities_03_migration_plan.md +++ b/docs/adr/2026_08_replicator_capabilities_03_migration_plan.md @@ -217,8 +217,8 @@ sequence rather than a second description of the implemented state. Make active creation a private, exhaustive `ReplicatorService` operation with one configuration identity and replacement policy. Migrate every non-active caller before restricting `getNewReplicator()`: CouchDB connection and passphrase -checks, Object Storage connection and preferred-tweak trials, isolated P2P -Setup, CLI commands, and other host compositions. Prove that every probe leaves +checks, Object Storage connection and preferred-tweak trials, P2P Setup +signalling trials, CLI commands, and other host compositions. Prove that every probe leaves the active main Replicator and adjunct P2P transport unchanged. Migrate Streaming Fetch to the owned CouchDB initial-transfer dependencies @@ -236,10 +236,15 @@ decision has been made. The current host composition has migrated CouchDB and Object Storage connection checks, passphrase inspection, preferred-tweak reads, CLI remote status and -administration, isolated P2P Setup, and Streaming Fetch Security Seed access to -owned resources or focused services. Active replacement is serialised, context -acquisition waits for a queued replacement, late candidates are fenced, and -short-lived resources are disposed. +administration, P2P Setup, and Streaming Fetch Security Seed access away from +active construction and towards owned resources or focused services. Active +replacement is serialised, context acquisition waits for a queued replacement, +late candidates are fenced, and short-lived resources are disposed. + +Stage 5 made the P2P Setup trial separately owned, but did not yet arbitrate it +against the active relay binding held by the stable P2P service. The Stage 7 +review identified and completed that remaining owner boundary; it did not +reopen the active-construction contract. This position completes the Stage 5 construction and probe boundary. It is not itself a release decision: the active-publication and truthful-attempt work in @@ -291,8 +296,12 @@ consumer-migration proposal: supports its real download workflow, and exposes no central facility; - ordinary central preparation and Streaming Fetch use owned Security Seed resources which dispose their unpublished compatibility instances; -- CLI synchronisation diagnoses lock and clean rejection from the exact outcome - while preserving the existing success and failure return policy; and +- CLI synchronisation diagnoses lock and clean rejection from the exact outcome. + Established successful output and exit behaviour are preserved. Typed + remote-administration verification failures return non-zero by default; + `--compat-remote-admin-exit-zero` restores the former zero result only for + returned verification failures, while thrown mutation failures remain + non-zero; and - Journal unavailable sync-parameter reads cannot enter the create-and-upload branch required only by explicit absence. @@ -418,6 +427,49 @@ database contract, and add another abstraction only where the inventory shows a concrete lifetime or testability benefit. These recorded questions do not widen Stage 6 or make an anticipatory database abstraction part of this change. +### Contracted Stage 7 review position + +The structural review retains the contracted service boundaries. The active +path remains independent of `LiveSyncAbstractReplicator`; the partial +compatibility facade, focused maintenance consumers, and in-process database +reset remain bounded deferred work. `ReplicatorService` and +`ReplicationService` continue to delegate their complex state and sequencing +to the focused collaborators listed above, so another physical service split +would add indirection without removing a current responsibility or improving a +current test seam. + +The review did identify five bounded behavioural corrections inside existing +owners: + +- Security Seed resources force a fresh provider read for their settings + snapshot instead of accepting process-cached synchronisation parameters as + current evidence; +- local-database close, reset, failed-initialisation rollback, and + physical-database close clean-up share and await the existing + active-Replicator retirement for one physical database lifetime; +- disposing an unused Journal resource does not report that replication + closed, while a Replicator lifecycle message is emitted only when a real + active publication is retired and no longer mislabels unload as database + reset; +- unattended P2P no-target, authentication, tweak-mismatch, and + overlapping-transfer settlements retain informational diagnostics without + creating Notice-level presentation; and +- P2P Setup receives the stable service's `P2PConnectionProbeAdmission` view. + Compatible active relay bindings are observed, an active binding which does + not cover the requested relay set is blocked with a stable decision code, + and only an idle owner runs and awaits the caller-owned raw trial. + +The last correction uses `P2PRoomSessionOwner`'s existing lifecycle queue and +adds no room owner, global relay lease, reference count, or raw transport +disposal policy. Every maintained production opening of the P2P Setup dialogue +passes through one host-owned `SetupManager` seam, which injects the admission +view explicitly. The decision code remains separate from the LiveSync-owned +English presentation message. + +These corrections close ownership and presentation gaps found by the review; +they do not add another generic capability, remote resource, probe framework, +or provider role. The target matrices in Part 1 therefore remain unchanged. + ## Verification ### Commonlib unit and type-contract tests @@ -435,8 +487,9 @@ Cover: candidate settlement; - unchanged-identity retention, changed-identity replacement, idempotent reservation release, and close ordering; -- probes which cannot replace the active Replicator or P2P service and dispose - owned resources; +- probes which cannot replace the active Replicator or P2P service, including + P2P Setup observation and blocking without trial construction, and idle + admission which awaits complete disposal of the caller-owned trial; - the deliberately narrow active-transfer stop request, including work it does not claim to cancel; - CouchDB compatibility and transfer using the same owned OneShot connection; @@ -445,9 +498,13 @@ Cover: and Continuous reassessment on newly opened CouchDB connections; - Journal synchronisation-parameter reads distinguishing explicit absence from unavailability and never writing after the latter; -- cohesive central administration and its truthful mutation settlement; and +- cohesive central administration and its truthful mutation settlement; - the narrow non-owning P2P active adapter, including download without a - synthetic upload or central facility. + synthetic upload or central facility; +- local-database retirement sharing the active owner boundary across reset and + close paths; and +- caller-authority preservation for unattended P2P presentation and truthful + Journal and active-lifecycle closure diagnostics. ### Self-hosted LiveSync unit tests @@ -468,17 +525,21 @@ Cover: - P2P AutoStart cancellation across suspension, bounded advertisement waiting, accepted peers without unattended dialogues, remote-broadcast prerequisites, session-demand reference counts, and overlapping peer policies; -- the seven P2P service views sharing one room owner without exposing a raw - host, room, or concrete Replicator; +- the focused P2P service views sharing one room owner without exposing a raw + host, room, or concrete Replicator, including connection-probe admission; - session replacement fencing callbacks, clients, temporary decisions, advertisements, and database-bound feeds while retaining persisted decisions; - counterpart RPC authorisation and broadcast progress preserving no-dialogue authority; -- Setup and settings validation through probes; +- Setup and settings validation through owned resources or owner-arbitrated + P2P admission; - exact failed-context mismatch, unlock, and cleaned-remote recovery, including rejection after active replacement; - CLI lock diagnostics from the exact finite outcome rather than mutable fields on a later active Replicator; +- CLI process exit codes for successful administration, returned verification + failure with and without `--compat-remote-admin-exit-zero`, and thrown + mutation failure; - first-device and additional-device initialisation for each current provider; - P2P as the main remote and as an adjunct transport; and - CLI, WebApp, and WebPeer composition against the same contracts, including @@ -492,9 +553,11 @@ waiting for the periodic interval, ordinary CouchDB start-up, and P2P start-up without an unexpected selection dialogue or transport replacement. P2P validation also covers an accepted configured peer advertising after room open, a watched peer whose remote side broadcasts, and suspension before a delayed -AutoStart callback. Object Storage validation must also confirm that a -temporarily unavailable synchronisation-parameter read does not upload a new -Security Seed. +AutoStart callback. A focused P2P Setup check must also cover an active room +with the same relay set, an attempted additional relay which is blocked without +closing that room, and an idle trial which releases its short-lived resources. +Object Storage validation must also confirm that a temporarily unavailable +synchronisation-parameter read does not upload a new Security Seed. No temporary stage is a release candidate. Release readiness requires the target capability matrix, the contracted core and in-scope consumer migration, diff --git a/docs/settings.md b/docs/settings.md index 98864c55..7619ee74 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -443,6 +443,14 @@ Setting key: P2P_relays The Nostr-compatible WebSocket relay URL or URLs used for peer discovery and WebRTC connection negotiation. Multiple URLs can be separated by commas. A signalling relay does not store or transfer Vault contents. See [How peer-to-peer synchronisation works](p2p.md). +The P2P Setup connection test does not interrupt an active P2P room. When the +active relay set already covers the requested URLs, the test observes that +active signalling transport. If the test would add a relay while P2P is +active, it asks you to use the active relay settings or disconnect P2P first. +When P2P is idle, the test opens and disposes a short-lived signalling trial. +This check does not prove peer discovery, room credentials against another +device, or a TURN or WebRTC data path. + #### Group ID Setting key: P2P_roomID diff --git a/updates.md b/updates.md index 8490c1b4..c336574f 100644 --- a/updates.md +++ b/updates.md @@ -12,6 +12,21 @@ Earlier releases remain available in the 1.0 release history, the 1.0 preview hi ## Unreleased +### Synchronisation and storage + +#### Fixed + +- **Sync on Startup** now runs an immediate Object Storage synchronisation after start-up or resume, including migrated profiles which retain a Continuous setting that Object Storage cannot use. +- A temporarily unavailable Object Storage synchronisation-parameter read is no longer treated as a missing object and cannot regenerate the shared Security Seed. Flow-specific Security Seed checks also bypass an earlier process-cached result. +- Local database reset and plug-in unload now retire active replication through its owner before closing the database, without reporting a missing active Replicator or describing unload as a database reset. + +### Peer-to-peer synchronisation + +#### Fixed + +- The P2P Setup connection test no longer interrupts an active P2P room. It observes an active compatible relay binding, blocks a test which would add another relay until P2P is disconnected, and uses a short-lived trial only while P2P is idle. +- Unattended P2P synchronisation no longer raises Notice-level messages for missing configured targets, authentication rejection, configuration mismatch, or an overlapping transfer. User-initiated operations retain their existing feedback. + ### Command-line interface #### Fixed