Optional Object Storage Service
The object-storage service retains immutable epoch-encrypted objects and advisory signed head publications. It is separate from the relay/AutoNAT and mailbox services, cannot decrypt vault content, and never selects a canonical head for clients.
Configure
Section titled “Configure”Start from packaging/lattice-object-storage.toml, replace the example external
addresses, and validate the result:
lattice object-storage config-check --config ./lattice-object-storage.tomllattice object-storage peer-idThe server listens on TCP and QUIC port 4102 by default. Its identity,
capability key, metadata, immutable object files, usage records, and process
lock live under the dedicated lattice-object-storage data directory.
The private observability listener defaults to 127.0.0.1:9476:
/healthz: the metrics HTTP task is alive./readyz: both libp2p listeners reached readiness./metrics: OpenMetrics request, response, error, swarm, and bandwidth data.
Metrics contain no peer IDs, repository IDs, object IDs, capability IDs, or network addresses as labels. Production config validation requires a nonzero loopback metrics address.
Provision Capabilities
Section titled “Provision Capabilities”Repositories use an epoch-derived blinded storage ID. Issue least-privilege reader or publisher capabilities for that ID:
lattice object-storage issue <repo-id> reader \ --config ./lattice-object-storage.tomllattice object-storage issue <repo-id> publisher \ --config ./lattice-object-storage.tomlreader permits object reads and advisory-head listing. publisher permits
immutable puts, signed publication, and owner-scoped unpin. full grants both
sets. Capabilities default to 90 days, accept --valid-days 1..365, and can be
bound to one libp2p identity with --peer-id.
Issuance prints the bearer token on stdout and its publication-owner ID on stderr. Preserve that owner ID when rotating a publisher or unpin token:
lattice object-storage issue <repo-id> publisher \ --publication-owner <32-hex-owner-id> \ --config ./lattice-object-storage.tomlThe token is a bearer secret. Keep it out of logs, shell history, world-readable files, and service configuration.
Configure a Device
Section titled “Configure a Device”Pin one advertised direct TCP or QUIC address and the service peer ID:
lattice vault storage set personal \ /dns4/storage.example.net/udp/4102/quic-v1/p2p/<storage-peer-id>Install issued tokens through stdin so they are stored only as protected entries
in credential-cache.kdbx:
lattice object-storage issue <repo-id> reader \ --config ./lattice-object-storage.toml \ | lattice vault storage capability personallattice object-storage issue <repo-id> publisher \ --publication-owner <32-hex-owner-id> \ --config ./lattice-object-storage.toml \ | lattice vault storage capability personalThe token repository ID must match the vault’s current epoch-derived storage
ID. Peer-bound tokens must match the daemon identity. lattice vault storage status personal reports only the pinned endpoint and capability counts;
clear removes protected tokens before disabling or changing the endpoint.
Both the mailbox and object-storage services must be configured for offline
delivery. A device that publishes needs a mailbox deposit capability plus an
object-storage publisher or full capability. A receiving device needs a
mailbox poll/acknowledge capability plus an object-storage reader or full
capability.
Offline Delivery
Section titled “Offline Delivery”On each mailbox cycle, the daemon encrypts the current signed revision into an immutable object bundle, uploads its exact object closure, publishes the signed head, and only then includes the encrypted manifest object ID in the recipient’s signed mailbox hint. If publication fails, the daemon can still deposit a peer-address hint and reports the object-storage error in daemon status.
Prepared bundles use randomized encryption. The daemon therefore persists the exact prepared bytes under the vault’s private data directory before the first upload. Retries and restarts reuse those bytes and object IDs, so a periodic cycle cannot create a new publication for an unchanged revision. After a new revision is published, the daemon uses the publisher’s stable publication-owner identity to unpin the superseded publication. Keep that owner ID when rotating publisher capabilities. Across an epoch rotation, retain the old publisher capability until the daemon has had a successful cycle so it can unpin the old repository’s publication.
When a recipient sees an unknown revision, it first lists advisory heads from the pinned object service, finds the signed head bound to the mailbox manifest ID, downloads the exact signed closure, decrypts and validates every object, and passes the revision to the normal authoritative apply path. The mailbox message is acknowledged only after a successful apply. If no usable object bundle is available, peer addresses from the same hint remain the fallback.
Object storage never grants rollback authority. Already-known stale heads are acknowledged without applying them, and unseen stale or detached revisions are rejected by the same signed ancestry, membership cutover, and rollback-floor checks used for direct peer synchronization.
Run Rootless
Section titled “Run Rootless”install -Dm0755 target/release/lattice ~/.local/bin/latticeinstall -Dm0644 packaging/systemd/user/lattice-object-storage.service \ ~/.config/systemd/user/lattice-object-storage.serviceinstall -Dm0600 packaging/lattice-object-storage.toml \ ~/.config/lattice-object-storage/config.tomlsystemctl --user daemon-reloadsystemctl --user enable --now lattice-object-storage.serviceVerify the rootless service locally with
curl --fail http://127.0.0.1:9476/readyz.
Service Semantics
Section titled “Service Semantics”Objects are addressed by the hash of their exact encrypted encoding and are never overwritten. A publication pins one signed, AEAD-bound head pointer and its exact sorted object closure. Multiple concurrent publications remain visible until their publication owner unpins them; clients validate membership, decrypt objects, and resolve revision ancestry before applying anything.
Read, write, object-count, publication, pin, repository-byte, service-byte, and bandwidth limits are enforced per policy and capability. Unpinned objects are eligible for collection only after 30 days. Read usage survives restart in small per-token records without rewriting the repository metadata database.