Skip to content

Revision Protocol and Merge

Status: Revision format 8 core implemented; application wire protocol 11 Scope: authoritative application sync semantics for the production MVP

The production MVP uses v2 record/resource sync as the authoritative protocol.

Opaque full-KDBX snapshots are not the MVP sync protocol. They are reserved for later use as:

  • recovery exports
  • full-state checkpoints
  • object-storage availability objects
  • compatibility fallback for future clients that cannot run record-level merge

This avoids having two competing sync models in the MVP and keeps current v2 tombstone, resource, and merge behavior central.

The v2 protocol synchronizes typed vault records and resources.

Required record categories:

  • groups
  • entries
  • typed fields
  • custom icons
  • binary attachments
  • resource descriptors
  • tombstones for deleted records

Every record has:

record_id
record_kind
version
state: live or deleted
content_hash
author_device
author_sequence
epoch
parents or causal base
signature or inclusion in signed manifest

version is a per-record logical version produced from the accepted causal base. author_sequence is a per-device monotonic sequence used for rollback detection and equivocation checks. The same record_id can have concurrent candidate records from different authors when their parent sets do not include each other.

Resource objects are addressed by payload hash. The resource record has a separate canonical content_hash that commits the payload identity plus kind, size, content hint, chunk layout, references, state, and version. If the same bytes are used by different logical features, the encrypted payload is stored once while each signed resource-record state remains fully authenticated.

A head is a signed summary of the latest known revision state for one writer or merged branch. The revision-format-7 implementation keeps one materialized head and creates a signed multi-parent merge revision whenever accepted heads are concurrent.

A manifest is a signed container that binds:

  • repo commitment
  • epoch
  • revision ID
  • author device
  • author sequence
  • parent heads
  • record descriptors
  • resource descriptors
  • tombstones
  • protocol version

Clients verify manifests before requesting missing payloads.

The implemented revision identifier is a domain-separated author event ID:

revision_id = BLAKE3("lattice-revision-id-v1" || vault_id || author_device || author_sequence)

The signed canonical revision body binds every other field, including parents, state hash, membership document hash, and changed descriptors. Reusing one author sequence with a different signed body therefore has the same revision ID and is rejected as equivocation. Record origins reference this event ID, which avoids a self-hash cycle between a full-state commitment and its origins.

Example shape:

{
"type": "lattice.vaultsync.manifest.v2",
"repo_commitment": "BLAKE3(...)",
"epoch": 12,
"revision_id": "domain-separated-author-event-id",
"author_device": "device-id",
"author_sequence": 481,
"parents": ["previous-revision-id"],
"records": [
{
"record_id": "entry:...",
"kind": "entry",
"version": 7,
"state": "live",
"content_hash": "BLAKE3(...)"
}
],
"signature": "author-signature-over-canonical-manifest"
}
  • application protocol version: 11
  • libp2p stream: /lattice/sync/7
  • online repo-alias context: online-v8
  • signed revision format: 8
  • encrypted sync-state format: 9
  • materialization-journal schema: 5
  • membership-state format: 8
  • membership-transition journal: 5
  • admin-succession authorization package: 2
  • pairing acceptance package: 5
  • fresh-key recovery package: 3

ManifestV2 carries the complete signed reachable revision DAG, current state summary, and the sender’s signed coverage of that head. RequestV2 names the exact head being fetched. PushV2 repeats that manifest and coverage, and every record is checked against its signed descriptor before resource writes or KDBX materialization. A record response must repeat the exact previously verified manifest and exactly the requested descriptors before any resource chunks are fetched. AckV2 must name the head that was sent and carries the receiver’s signed coverage of that accepted head.

Manifest validation reconstructs every revision state from its parents and signed changed-record delta. Normal revisions cannot omit a parent record; deletion therefore requires an explicit tombstone. Merge states are reconstructed deterministically from causal record origins before version ordering. Revision count, parent count, graph depth, expanded state work, resource size, and chunk count are bounded, and DAG traversal is iterative.

PushV2.historical_records may carry losing record bytes for conflict history. Each historical descriptor must not claim to be current state. The revision graph must prove that the descriptor was the latest value for its record on one parent of a signed merge, that another merge parent was concurrent and did not contain that value, and that the authoritative value came from the other branch or from the merge or one of its descendants. The merge must lead to the authoritative head. This rule preserves unresolved conflicts across membership cutovers while rejecting ordinary causal history in every epoch. Historical payloads can rebuild the same KeePass conflict inventory, but cannot alter the authoritative head.

Membership history is retained so historical revision signatures remain verifiable after restart and pairing. An admin membership transition includes a current head that descends from the new membership cutover. A stale remaining device installs that state authoritatively before advertising any local head; detached records are removed from the materialized vault and retained in the encrypted conflict quarantine. Author sequence floors from detached branches remain persisted.

Membership adoption is crash-consistent across its separate stores. A durable transition journal binds the exact previous and target membership generations to the required signed cutover. Restart recovery accepts only one of those two membership generations, rolls the previous generation forward when necessary, and gates local edits plus inbound data service until the target authority, rollback floors, and cutover are durable. Any unrelated generation fails closed. Local KDBX changes observed while that cutover is still unsatisfied are not signed under the old authority: their records and resource payloads are retained in encrypted conflict quarantine before the authoritative state is materialized. Changes made after the cutover is already durable are ordinary current-epoch edits.

Encrypted sync-state format 9 binds the authoritative body to the exact KDBX byte hash and length from which it was derived. Normal daemon startup reconciles an offline KDBX edit into a signed descendant and rewrites that binding. Recovery export and fresh-key approval fail closed while the KDBX differs from the bound source, so an unprocessed local edit cannot be omitted from a newly trusted baseline. Format 9 carries the expanded semantic record body, signed per-key custom-data deletion tombstones, and signed per-member tombstone-coverage acknowledgements. It is a pre-production hard cut: formats 2-8 are rejected before decryption rather than migrated into a lossy record projection. The materialization journal uses schema 5 and rejects older journal schemas.

Membership-state format 8 binds each administrator succession to an explicit Routine, migrated RoutineV1, or Recovery kind and to the device ID whose signature authorizes it. Format-7 routine records migrate with their legacy signing projection intact. Membership-transition journal format 5 carries the format-8 target; format-4 journals migrate through the exact format-7 target fingerprint. Because AdminSuccession is also carried by pairing, fresh-key recovery, and online membership-update payloads, this pre-production cut bumps their package discriminators or cryptographic wrap/AAD labels. Pending packages from the previous formats must be regenerated after every device is upgraded.

The protocol must never use last-write-wins as the core conflict rule.

Merge decisions:

  • A record whose signed origin causally descends from the other candidate wins, regardless of HLC ordering.
  • For truly concurrent origins, higher version supersedes lower version for the same record.
  • Equal version and equal state with equal content hash is identical.
  • The same origin event with different record content is equivocation and is rejected.
  • Equal-version, different-content records from concurrent valid origins use a deterministic content-commitment tie-break and preserve the loser as a conflict artifact.
  • A valid tombstone supersedes an older live record.
  • A live record at the same or lower version than a known tombstone must not resurrect the item.
  • Concurrent edits to different records may merge automatically.
  • Concurrent edits to the same logical field require conflict handling.

Deletion must be represented explicitly.

Rules:

  • Local deletion creates a tombstone with a new version.
  • Tombstones are advertised in heads/manifests.
  • Incoming tombstones remove or mark absent the corresponding KDBX item during materialization.
  • Full tombstone payloads are retained until compaction can prove the deletion is covered by every current member.
  • A tombstone is covered by a head only if the head causally descends from the tombstone revision or from a later revision that carries the deletion forward.
  • Every current member signs one acknowledgement naming a covered current-epoch head; acknowledgements are bound to the vault, repository commitment, membership epoch, and exact membership document.
  • A conservative time horizon alone is not enough to compact a tombstone.
  • Only the current admin may compact, and only after every current member’s acknowledged head causally covers the deletion and is an ancestor of the admin’s current head.
  • Compaction authors a normal signed descendant containing a canonical payload-free deletion fence. It never removes the record identity or deletion state.
  • Stale peers that advertise pre-tombstone live records must be answered with the tombstone or a descendant revision, not allowed to resurrect the item.

A record that references a resource is not complete unless the receiver can obtain and verify the resource descriptor and encrypted payload.

Rules:

  • Request computation must include missing resource records and resource chunks.
  • Push responses must transfer resource records when they are required by pushed entry, attachment, or icon records.
  • Receivers reject live records that reference unavailable required resources unless they keep the revision pending rather than accepted.
  • Resource payload hashes are verified before materialization.
  • Resource object identity is the payload hash, not attachment-vs-icon usage.

The KDBX writer must apply the verified v2 snapshot, not a partially verified transport state.

Materialization rules:

  • Create missing groups and entries before applying live remote records.
  • Update existing groups and entries when the incoming record is accepted.
  • Remove or mark absent records that have accepted tombstones.
  • Preserve supported KeePass metadata that is outside the current typed field scope when possible.
  • Refuse materialization if required resources are missing.

Unsupported local KDBX features should not force the daemon onto a v1 path. The production path is v2-only.

The protocol can detect conflicts while the vault is locked, but some conflict resolution requires user interaction or KDBX access.

MVP behavior:

  • If remote changes merge cleanly at the record level, apply them.
  • If the same record field has concurrent divergent changes, preserve both values using a deterministic conflict representation or require user resolution before writing.
  • If the conflict cannot be represented safely in KDBX, keep both revisions pending and surface the vault as needing resolution.
  • Give every retained losing candidate a stable conflict ID derived from its record ID and content commitment.
  • After resolution, create a signed resolution revision that directly parents the current head after verifying that head causally descends from every recorded conflicting head. Its signed state monotonically commits the resolved conflict ID.
  • Clear KeePass history or conflict-copy artifacts only through the journaled materialization transaction for that signed revision. Retain the encrypted losing payload in the retired-conflict archive until proof-based pruning can safely remove it.
  • Treat exact command replay, stale pre-resolution heads, restart recovery, and offline redelivery as idempotent. None may remove the signed resolution tombstone or recreate an active conflict.

The implementation must not silently choose a winner based only on wall-clock time.

Operators inspect and resolve conflicts with:

Terminal window
lattice vault conflicts list personal
lattice vault conflicts show personal <conflict-id>
lattice vault conflicts resolve personal <conflict-id> --resolution current
lattice vault conflicts resolve personal <conflict-id> --resolution conflicting

list and show print record descriptors, commitments, artifact kind, and causal heads without decrypted field values. current keeps the materialized winner. conflicting promotes the retained candidate with a fresh record version. The same commands use authenticated local IPC while the daemon is running and the single-instance mutation lock while it is stopped.

Resolved conflict payloads remain encrypted in retired_conflicts while their exact descriptor remains eligible as a signed losing concurrent merge branch. lattice vault secrets report|prune removes a payload only when that descriptor is no longer eligible from the authoritative signed revision graph. The dry-run plan binds the authoritative head and exact conflict ID/disposition set, so a new resolution, incoming revision, restart recovery, or stale redelivery invalidates the confirmation instead of racing deletion. Deletion is committed through the retired-secret pruning journal before the membership-derived revision authority changes. Recovery repeats the deletion idempotently and removes the journal only after encrypted sync state and a secret-free completion receipt are durable, so no restart window can re-import or publish under partially pruned authority and exact retries remain unambiguous.

Opaque snapshot sync may be added later, but it must not conflict with v2.

If added, snapshot objects are:

  • signed full-state checkpoints
  • encrypted with the sync envelope before any storage upload
  • linked to the v2 revision DAG as checkpoints
  • not used to bypass membership, tombstone, or anti-rollback rules

Snapshot acceptance rule:

A snapshot can accelerate recovery only if its signed checkpoint is consistent with accepted v2 heads and local rollback floors.

Snapshot mode may be useful for recovery and cold-start catch-up, but it is not the MVP online sync protocol.

Rejected MVP Alternative: Snapshot Network Contract

Section titled “Rejected MVP Alternative: Snapshot Network Contract”

One reviewed alternative was to keep v2 records/resources only as a local merge engine and expose only opaque encrypted KDBX snapshot chunks at every network boundary.

That is not the production MVP recommendation.

Reasons:

  • The current compatibility work is already centered on v2 records, resources, tombstones, attachments, custom icons, and typed fields.
  • A snapshot-only network contract would delay proof that v2 resource and tombstone behavior works across real peers.
  • Opaque snapshots make same-record conflicts harder to resolve while the vault is locked.
  • Full-snapshot transfer is expensive for attachment-heavy vaults.
  • Dual-stack migration would create two authoritative protocol paths unless it is designed as its own project.

If snapshot networking is reconsidered later, it must be introduced as a new protocol mode with explicit feature negotiation and one of these migration policies:

  • hard cutover: once a vault migrates to snapshot networking, legacy v2-network peers are blocked until upgraded
  • dual-stack: peers support both v2-network and snapshot-network modes until all devices migrate

That migration policy is a future product decision, not an MVP blocker.

  • Remote-created entries, groups, icons, attachments, and typed fields materialize locally.
  • Local deletions propagate as tombstones and do not resurrect from stale peers.
  • All-member causal coverage compacts full tombstones to canonical deletion fences that survive restart and stale replay.
  • Missing resources are requested and transferred with referring records.
  • Same-author equal-event content mismatches are rejected; cross-author concurrent equal-version edits become conflicts.
  • v2-compatible KDBX files with attachments and custom icons do not fall back to unsupported v1 behavior.
  • Concurrent changes produce explicit merge or resolution state, not last-write-wins.