Files
suaveplan/docs/network/offline-and-reintegration.md
2026-05-27 23:29:53 +01:00

7.2 KiB
Raw Blame History

Offline Mode and Reintegration

The mesh keeps running when the central server is unreachable. This document is the reference for how that works in detail.

The central plugin is network-plugin-server-bridge, governed by NET-31..NET-34 and NET-56.

Detection (NET-31)

Tier 0 hosts maintain a persistent WebSocket to the central server with bidirectional heartbeats. The server-bridge declares the server dead within 2× the heartbeat interval after the last successful heartbeat. Standard verification rules apply (re-ping through alternate paths; consensus among multiple Tier 0 hosts) to avoid declaring a dead server on a transient blip.

Autonomous operation (NET-32)

Once the server is declared dead, the mesh transitions to autonomous mode. The transitions are:

Subsystem Online → Offline
Token grace periods extended by configurable factor (e.g., expiresAt += 24h)
New mesh / session creation paused; new requests rejected
New peer admission requires a still-valid pre-issued CapabilityToken; raw onboarding paused
Audit-log finalisation local append continues; finalisation deferred
Audit-log replication to server buffered
Cross-mesh trust existing MeshTrustTokens remain valid; new ones cannot be issued
Topology, failover, elections unchanged — fully client-side
Drive read/write unchanged — every operation is signed by drive identity, verifiable later

The bus emits server:disconnected so plugins can react. Most plugins do nothing — they don't depend on the server. Plugins that do depend on the server (server-bridge, auth, audit-mesh) flip into offline mode.

What gets buffered (NET-32)

Three buffer streams, all in local storage (typically OpfsStorage in the browser, FileStorage on a server peer):

  1. Pending audit-log entries — every signed log entry that would have been finalised by the server.
  2. Pending token-issuance requests — operations the user attempted that need a server-signed token.
  3. Pending mesh state deltas — bitpacker-encoded registry/topology deltas that the server would have authoritatively reconciled.

All three streams are append-only and signed by the issuing peer. Storage is bounded; on overflow, the oldest non-CRITICAL entries are dropped first. CRITICAL entries are never dropped — if the buffer is full of CRITICAL, the system surfaces an error and refuses to accept more until the user clears space (or the server returns).

Reconnection (NET-33)

When the server-bridge re-establishes the WebSocket, the reconciliation pipeline runs:

1. Handshake
   Server-bridge sends its mesh-bridge identity and the highest seq the server has.
   Server replies with the seq it expects.

2. Replay buffered entries in seq order
   For each:
     a. Server verifies Ed25519 signature against the issuing peer's pubkey.
     b. Server checks for conflicts with other partitions' replays.
     c. On accept: append to authoritative log, advance seq.
     d. On conflict: invoke partition merge protocol (NET-80, NET-81).

3. Process pending token requests
   For each queued request:
     a. Re-validate authorization (the issuer may no longer be eligible).
     b. Issue server-signed token.
     c. Notify the requesting peer via bus.

4. Reconcile state
   Server broadcasts `state:reconciled { meshState, sessionStates, ... }`.
   Each plugin updates its local view to match.

5. Hosting transition (model-dependent)
   In SERVER_TO_CLIENT_FAILOVER, the acting Tier 0 demotes; server resumes Tier 0.
   In CLIENT_WITH_SERVER_BACKUP, the user remains Tier 0; the server returns to its
   participant role.

6. Bus emits `server:reconnected` and `mesh:reconciled`.
   Suspended operations (new mesh creation, etc.) resume.

The replay is idempotent: replaying an already-finalised entry is a no-op (the server tracks (peerId, seq) to dedupe). This means a flaky reconnect that drops mid-replay can simply retry from the last acknowledged seq.

Conflict resolution

Two partitions running autonomously can produce conflicting state. Examples:

  • Both elected a different acting Tier 0.
  • Both wrote different state to the same path.
  • Both admitted a peer the other doesn't know about.

The merge protocol (NET-81) reconciles using the partition QuorumResolver strategy (NET-80) — size, performance, age, or hybrid. Whichever partition wins becomes the authoritative source; the other partition's peers rejoin (NET-82).

For state-level conflicts that don't involve elections, network-plugin-state reconciliation runs (NET-42) with one of five strategies: latest-wins, remote-wins, local-wins, deep-merge, or custom. The strategy is configurable per state path via PathStrategyRegistry.

Audit-log compaction (NET-39)

After reintegration, the audit log can grow unbounded. Compaction folds old entries into signed checkpoints:

checkpoint {
  seqRange: [from, to],
  stateSnapshot: bitpacker-encoded snapshot at seq=to,
  hostSignature: Ed25519 over (seqRange ‖ stateHash),
}

Replicas use the latest checkpoint to skip the entire prefix when bringing up a fresh peer. The full history remains available for forensic queries; the on-the-wire replay path skips it.

Hosting-model interactions

Model What happens at server-down What happens at server-up
PURE_CLIENT No-op — there's no server to lose. No-op.
SERVER_BASED Mesh stops. Operations queue locally; users see "offline" UI. Replay queue drains; mesh resumes.
SERVER_TO_CLIENT_FAILOVER Election promotes Tier 1 → acting Tier 0. Mesh continues with full operations except new-token issuance. Acting Tier 0 demotes; server resumes. Buffered log replays.
CLIENT_WITH_SERVER_BACKUP User remains Tier 0; mesh continues normally. The server is "missing" but it wasn't authoritative anyway. Server rejoins as participant. Buffered log replays.

The mesh token (NET-29) carries the model, so the server-bridge knows which transitions to run.

Common pitfalls and how the design avoids them

Pitfall Avoidance
Replay stuck on a single bad entry Server processes entries individually; rejects-but-continues.
Buffer overflow loses history CRITICAL entries are never dropped. Surface error before silent loss.
Two acting Tier 0s in different partitions both finalising tokens Tokens issued in autonomous mode have a special bit set; on merge, one partition's tokens are voided per the quorum strategy and re-issued.
Clock drift causes apparent expiry mid-outage extendedExpiresAt is computed from the peer's clock, not the server's, so lack of server time doesn't cause spurious expiry.
Long outage ⇒ everyone's CapabilityToken expires Grace-period extension is automatic; if the outage exceeds the configured maximum, the mesh degrades to read-only.

Reading further