Identity, Membership, and Keys
Status: Self-reviewed draft
Scope: device authorization, key material, membership epochs, revocation, and rollback resistance
Identity Layers
Section titled “Identity Layers”The design uses separate identities for separate jobs.
| Identity | Purpose | Rotatable? | Trust meaning |
|---|---|---|---|
| libp2p Peer ID | transport connection identity | yes | this network endpoint controls a transport key |
| device signing key | application author identity | rarely | this paired device may sign vault revisions |
| device encryption key | wrapping messages or keys to one device | rarely | this paired device can receive encrypted material |
| repo secret | private vault-sync group secret | epoch-bound | possession grants access to blinded aliases and mailbox IDs |
| membership document key or threshold | authorizes membership changes | policy-bound | controls who can add/remove devices |
A libp2p Peer ID alone never authorizes vault fetches.
Repository Identifiers
Section titled “Repository Identifiers”The public network must not use the vault filename or human-readable vault name as an identifier.
Each vault sync group has:
repo_secret: random high-entropy secret shared only with membersrepo_commitment: BLAKE3("lattice-repo-commitment" || repo_secret)repo_alias: BLAKE3("lattice-repo-alias" || repo_secret || epoch || context)repo_commitment appears in signed membership and revision documents. repo_alias is used for network lookup and may rotate by epoch or service context.
Repo Secret Rotation
Section titled “Repo Secret Rotation”The repo secret is epoch-bound namespace material. It should rotate on hard revocation, suspected repo-secret compromise, or any event where future mailbox aliases must be unlinkable from prior aliases.
Rotation rules:
- A new membership epoch carries a new
repo_commitment. - The old and new commitments are linked only inside the signed membership transition document delivered to remaining members.
- Future repo aliases and mailbox IDs use the new repo secret.
- Old repo secrets may be retained to read already accepted history. The membership admin may also retain a bounded, local-only bridge from old epoch ranges to old repo secrets so an offline remaining member can authenticate provisionally and receive the current signed transition. Bridge secrets are never sent to members and are not used for current aliases or payload keys. The pre-production implementation retains at most the eight most recent rotation bridges; members older than that window must re-pair.
- A removed device must not receive the new repo secret or any future epoch secret wrapping.
Pairing Requirements
Section titled “Pairing Requirements”Pairing must transfer enough material for the new device to verify and participate without trusting infrastructure.
Minimum pairing payload:
repo_secret or wrapped repo secretcurrent membership documentcurrent sync epochcurrent epoch encryption material or a wrapped form of itexisting member device signing keysrevoked device keyscurrent accepted headsanti-rollback floor for membership epoch and author sequencesThe pairing channel must authenticate both sides. Acceptable product flows can include QR code, local network pairing with numeric confirmation, invite file plus out-of-band confirmation, or recovery package. The implementation plan may choose one first, but the protocol must not depend on an unauthenticated invite copied through a server.
Membership Document
Section titled “Membership Document”Each vault sync group has a signed membership document.
{ "type": "lattice.membership.v1", "repo_commitment": "BLAKE3(...)", "epoch": 12, "members": [ { "device_id": "hash-of-device-signing-key", "device_signing_key": "...", "device_encryption_key": "...", "transport_peer_ids": ["optional-current-peer-id"], "role": "writer" } ], "removed": ["old-device-id"], "policy": { "membership_authority": "single-admin-v1" }, "signature": "membership-authority-signature"}The default single-admin-v1 policy has exactly one membership Admin. The
implemented opt-in recovery-admin-v2:<writer-device-id> policy retains that
single primary Admin and designates one distinct live Writer as a constrained
secondary recovery authority. Activating the recovery policy requires a third
distinct live Writer that is eligible to become Admin. The recovery authority
remains an ordinary Writer for data revisions; only its additional membership
authority is recovery-specific. Threshold signatures and all-member consensus
remain future policies.
Device Key Replacement
Section titled “Device Key Replacement”Healthy or compromised device keys can be replaced without treating the device as a brand-new member, but the replacement must be a signed membership event.
Key replacement document requirements:
repo_commitmentold device_idnew device_idold device signing keynew device signing keynew device encryption keymembership epochreason: routine | suspected-compromise | recoverymembership authority signatureIf the old signing key is still trusted, the replacement should also include an old-key signature over the new key. If the old key is suspected compromised or unavailable, the membership authority signature is sufficient, but the old key must be listed as replaced or removed in the same epoch.
Implemented recovery profile:
- The current admin issues a normal signed invite. The replacement installation creates a signed request that binds the invite, VaultId, predecessor membership, old writer ID, fresh signing key, fresh encryption key, and request nonce.
- The request code hashes the complete signed invite/request transcript and must be confirmed before the admin changes membership.
- Approval advances exactly one epoch, tombstones the old non-admin writer, adds
the fresh writer with the same role, rotates both repository and epoch secrets,
and persists an authority-signed
recoveryreplacement record alongside the membership history. - The admin creates a signed
MembershipCutoverparented to the exact previous authoritative head. The encrypted package contains the resulting complete revision graph, rollback floors, records, and resource payloads. - Package encryption uses a one-time X25519 sender key and transcript-bound HKDF; the complete ciphertext and metadata are signed by the invite’s admin key. A second independently communicated code hashes the complete signed package.
- Admin approval and recipient import each have private durable markers. Startup
remains
recovery-requireduntil the exact transaction is completed. The recipient writes the KDBX, resources, encrypted state, initialization evidence, fresh membership, and replay receipt before removing the import marker. - A replacement-shaped remove-plus-add membership transition is invalid unless its signed old-to-new replacement record is present and permanently retained.
The single-admin-v1 authority cannot replace itself. Lattice therefore exports
the current admin signing/encryption identity and its validating membership
state as a vault-ID-bound, passphrase-encrypted offline backup. Restore is a
stopped-daemon operation, refuses pending transitions and healthy differing
membership state, verifies any encrypted membership rollback floors using the
separately provisioned KDBX credentials, and preserves corrupt replaced bytes
as a private preimage. The backup intentionally excludes KDBX credentials,
sync state, and resource payloads.
As an alternative prepared before loss, the current Admin may activate
recovery-admin-v2 through a journaled, Admin-signed policy-only membership
cutover. Activation, reassignment, and clearing require the running daemon and
cannot change members, roles, keys, tombstones, or repository commitment in the
same epoch. The policy cutover rotates the epoch secret.
The implemented secondary-admin recovery profile is deliberately narrow:
- The designated recovery Writer creates one offer for the exact predecessor membership generation. A conflicting second local offer is rejected.
- A different existing Writer accepts and signs the exact next membership
document. The recovery Writer then signs an explicit
Recoverysuccession proof bound to its authority ID, both documents, the acceptance, operation nonce, and exact previous authoritative head. - The target removes and tombstones only the old Admin, promotes only the accepting Writer, retains the recovery authority unchanged as a Writer, and preserves every other member, key, role, and prior tombstone.
- Repository and epoch secrets both rotate. The encrypted authorization is targeted to the new Admin; only that device receives the bounded old repository-secret bridge needed for accepted history.
- The new Admin signs a
MembershipCutoverdirectly descending from the authorized head. Stale packages fail before journal or membership mutation, and exact commit retries use the durable journal and receipt.
Primary loss is not cryptographically provable. Recovery proves that the configured recovery authority authorized removal and a distinct Writer accepted promotion; cooperating recovery and candidate Writers can therefore remove a still-live primary. Operators must compare both codes out of band, take the primary offline, and avoid edits on the recovery and candidate devices during the ceremony. Without either a usable secondary recovery authority or an offline identity backup, loss of the sole Admin remains unrecoverable.
Acceptance rules:
- Future revisions from the old device signing key are rejected after the replacement epoch.
- Future epoch material is wrapped only to the new device encryption key.
- The anti-rollback store records the replacement epoch so a server cannot reintroduce the old key binding.
- A key replacement that changes transport Peer IDs does not require treating those Peer IDs as application identities.
Epochs
Section titled “Epochs”An epoch changes when membership changes or when sync encryption material is rotated.
Epoch rules:
- Epoch numbers strictly increase.
- A device rejects membership documents older than its persisted epoch floor.
- A device may retain old epoch keys only long enough to read already accepted history.
- New revisions must use the current epoch.
- Removing a member creates a new epoch and new future encryption material.
Key Wrapping
Section titled “Key Wrapping”Sync encryption material must be random and high entropy. It must not be derived from the KeePass master password.
For each epoch:
epoch_secret: random high-entropy secretdevice_wrap: encryption of epoch_secret to each member device_encryption_keycontext keys: derived from epoch_secret with labeled KDF contextsRequired contexts:
- session request authorization
- stored resource envelope encryption
- mailbox envelope encryption
- object-storage envelope encryption
- blinded aliases and mailbox IDs
Protocol 6 uses HKDF-SHA256 with explicit labels for the session ID, initiator confirmation, responder confirmation, initiator-to-responder request MAC, and responder-to-initiator response MAC. Stored resource chunks use a separate HKDF-SHA256 context and versioned XChaCha20-Poly1305 envelope bound to the vault ID, epoch, plaintext resource hash, chunk index, chunk length, and plaintext chunk hash. Plaintext resource identity and deduplication remain unchanged.
Each device retains its own local history of older resource epoch secrets so pending and historical resources remain readable across membership cutovers. That history is local-only: join, recovery, and replacement recipients receive verified plaintext resources and write them under their current epoch instead of receiving historical keys. Password-derived legacy chunks and older epoch envelopes are migration inputs and are atomically rewritten under the current epoch on the next verified write. No key is dropped implicitly and resource-key history never blocks membership rotation or revocation. Local proof-based pruning is implemented to shorten the retention window. Mailbox hints use ephemeral X25519, HKDF-SHA256, and XChaCha20-Poly1305. Object bundles use epoch-derived HKDF-SHA256 keys and XChaCha20-Poly1305 with repository, vault, epoch, object-kind, and object-ID bindings. Blinded mailbox and repository IDs are derived through separate labeled contexts.
Retired Secret Pruning
Section titled “Retired Secret Pruning”lattice vault secrets report <name> reports every retained repository bridge,
resource epoch secret, and retired conflict payload with a redacted disposition
and reason. prune <name> --dry-run emits a 32-byte plan commitment; applying
the plan requires --confirm <plan-id>. The daemon and stopped-daemon paths
recompute the commitment immediately before mutation. It binds the exact
membership fingerprint, authoritative revision head, retained-secret
dispositions, resource-envelope epochs, and object-bundle cache epoch.
Repository bridge removal requires all of the following:
- the authoritative graph has a current membership cutover
- every current non-local member has authored an accepted current-epoch revision descending from that cutover and leading to the authoritative head
- no cached object bundle uses an epoch covered by the bridge
This is deliberately conservative. Helper members do not author vault revisions, so their presence blocks bridge pruning until a future protocol adds signed tombstone acknowledgements or equivalent causal proof. Time alone is never proof.
A resource epoch secret is removable only when no committed local resource chunk envelope uses that epoch, no cached object bundle uses it, and no retained repository secret still accepts that epoch’s namespace. Resource scanning fails closed on unknown files, incomplete transactions, malformed envelopes, and future envelope versions. Pruning changes only local secret retention; it does not advance membership epoch or alter the signed public membership document.
Apply is crash-safe across the separate membership and encrypted sync-state stores. A mode-0600, vault-bound local journal records the validated source and target membership fingerprints plus the exact pruning report before either store is changed. It contains no membership secret material, and its encoded buffers are zeroized after use. Recovery always rolls forward: it installs the target membership and revision authority, idempotently removes the selected retired conflict payloads, writes a bounded secret-free receipt, and only then deletes the journal. Daemon startup and offline access finish this sequence before normal import, authoring, or network publication. A bounded receipt history makes completed plans replayable after every commit boundary without retaining removed key material.
Device Revocation
Section titled “Device Revocation”Revocation cannot make a removed device forget data it already has.
Revocation does:
1. Create a new membership document with a higher epoch.2. Add the removed device ID to the removed list.3. Generate new future epoch encryption material.4. Stop wrapping future keys to the removed device.5. Rotate repo aliases and mailbox IDs.6. Reject future revisions signed by removed device keys.7. Produce a fresh post-revocation head from a remaining writer.Revocation does not:
- delete data already copied by the removed device
- prevent the removed device from publishing stale ciphertext
- prevent denial of service
An old alias match is only transition ingress. Before returning a membership document or vault identifier, the responder verifies the provisional signature against the current live-member set. A removed device therefore cannot use a retained bridge to obtain the new repo secret, epoch secret, member list, heads, or records. Unknown aliases and recognized aliases with invalid or removed signers receive the same generic pre-authentication response.
Anti-Rollback State
Section titled “Anti-Rollback State”Implementation status: protocol 11 persists the signed revision DAG, latest head, current cutover, record origins, membership epoch/document commitments, removed devices, member signing-key bindings, accepted heads, highest observed author sequence and epoch, and known object hashes in encrypted sync state and the crash-recovery materialization journal. Membership adoption also writes a separate transition journal before advancing the membership store, revision authority, rollback floors, or required cutover. Startup either rolls the membership store forward from the journal or resumes the target transition, and the vault remains gated until the signed cutover is durable. A current member can also restore lost encrypted revision/floor state from an admin-signed, member-targeted recovery package as described below.
Membership state format 8 stores an explicit succession kind and signing
authority ID in every administrator-succession proof. Format 7 routine proofs
migrate as RoutineV1 and continue verifying against their original signing
bytes. Format 6 migrates with an empty succession history while preserving its
vault-ID-bound initialization evidence, signed key-replacement record history,
and retained resource-epoch secret history. Format 5 migrates with its existing
evidence and replacement history intact and an empty resource-epoch history;
older formats retain their documented fail-closed migrations. Transition
journal format 5 carries format-8 targets and migrates format-4 journals through
their original format-7 fingerprints.
New memberships begin Pending, successful authenticated state persistence
advances them to Initialized, and migrated older memberships are
LegacyUnknown until a valid encrypted state is loaded. Only a matching
Pending membership may bootstrap without state; Initialized,
LegacyUnknown, and cross-vault evidence fail closed as recovery-required.
The sidecar marker is versioned and vault-bound but is not the sole evidence.
Before initialization, the daemon also verifies the configured vault ID against
the UUID of the KDBX opened for the initialization snapshot and rejects source
replacement races rather than persisting state for a different database.
Each device must persist anti-rollback state outside the KDBX file.
Required persisted fields:
repo_commitmentcurrent membership epoch floorcurrent membership document hashremoved device IDsknown member signing keyslatest accepted headsknown revision DAG summaryhighest author sequence per devicehighest accepted epoch per device, if tracked separatelyknown object/resource hashes accepted locallylast successful sync time and providers, advisory onlyThis state must be updated atomically with acceptance of a new revision or membership document. A crash must not leave the vault file advanced while rollback floors remain stale.
Lost Local State
Section titled “Lost Local State”Losing anti-rollback state weakens rollback detection. The product must treat this as a recovery event, not as ordinary sync.
Allowed recovery options:
- restore anti-rollback state from a trusted local backup
- re-pair from an existing trusted device
- require user confirmation before accepting a remote state as the new baseline
Disallowed behavior:
- silently accepting the newest server-advertised head as authoritative after local state loss
- resetting epoch floors because storage has no newer membership document
- treating deletion of both encrypted state and its sidecar marker as first initialization
Implemented recovery profile:
lattice vault recover exportreads only the trusted admin’s locally authenticated encrypted state and packages the exact signed head, revision graph, rollback floors, records, and resource payloads. The encrypted state must carry a format-5 KDBX source binding that still matches the admin’s current file; otherwise the daemon must first reconcile the local edit.- The package is signed by the current membership admin and encrypted to one current member’s application encryption key.
lattice vault recover importrequires the out-of-band confirmation code and an existing initialization marker. It rejects healthy state except for an exact completed retry, wrong vaults, stale membership, wrong recipients, incomplete floors, detached heads, and mismatched resources before writing. A running daemon accepts the import only for its inactiverecovery-requiredvault and activates that vault after durable commit.- A private import marker makes interrupted replacement rerunnable only with
the exact same package; daemon startup reports
recovery-requireduntil the transaction completes. - Before the first KDBX replacement, import preserves an immutable, package-specific copy of the local KDBX. Exact-package retries reuse that copy instead of rotating it away, retaining any otherwise-unclassifiable local edits for manual reconciliation.
- A private durable receipt records the accepted package and head. Reusing that package after a later state loss is rejected, and a newly issued package must descend from the last recovered head.
- KDBX materialization and resources commit before encrypted sync state, so an interrupted import cannot become an untracked active baseline. Any stale materialization journal is removed after the recovery marker is durable and before KDBX replacement, so it cannot replay over the recovered state.
The same-key profile requires intact local membership keys and the same current membership document on both devices. If those keys are lost, the fresh-key replacement profile above performs the separate signed recovery transition.
Session Binding
Section titled “Session Binding”During an online sync session, each peer signs a transcript that includes:
- both libp2p Peer IDs
- both nonces
- repo commitment or alias
- negotiated protocol versions
- membership epoch
- membership document hash
Both receivers verify that the peer signing key is a current member key before serving vault-specific heads, fetches, or membership state. This binds transport identity to vault membership for the session without making Peer ID rotation a membership event. The complete signed transcript and proof signature are hashed into the protocol-7 HKDF schedule with the current epoch_secret; direction-separated HMAC keys then authenticate exact request sequences and responses.
Success Criteria
Section titled “Success Criteria”- A non-member transport peer cannot fetch vault data.
- A revoked device cannot create accepted future revisions.
- A malicious server cannot roll a device back below its persisted epoch or author-sequence floors.
- A reinstall or lost-state event cannot silently trust server-advertised state.
- Membership changes rotate future access material and network aliases.
- Device signing and encryption keys can be replaced through a signed membership event without letting old bindings reappear.