Build e6c937427256 ยท Updated 2026-06-07
Grove uses a peer-to-peer trust model โ you explicitly choose who to connect with. There is no central authority.
| What | Algorithm | Key size |
|---|---|---|
| File chunks | ChaCha20-Poly1305 | 256-bit |
| Manifest metadata (filename, path) | ChaCha20-Poly1305 | 256-bit (per-recipient envelope) |
| Grant key wrapping | X25519 + HKDF-SHA256 | 256-bit |
| Chunk/content addressing | BLAKE2b | 256-bit |
| Cell identity | Ed25519 | 256-bit |
| Chat key exchange | X25519 | 256-bit |
| Chat messages (peer + portal) | ChaCha20-Poly1305 | 256-bit |
| Password hashing | PBKDF2-SHA256 | 100k iterations, random salt |
| Peer secret comparison | HMAC-SHA256 (timing-safe) | โ |
~/.grove/dashboard_authHttpOnly, SameSite=Lax, 30-day expiryauth_lockout)X-Real-IP authoritatively; Grove uses it for lockout and rate checks)X-Grove-Secret header on all peer API calls_derive_peer_secret). The global peer secret is a fallback for legacy callers only.X-Grove-Sender header identifies the calling cell's pubkey; the receiver derives the expected secret for that identity and rejects mismatches โ this prevents a peer from using a valid global secret to masquerade as a different identity.hmac.compare_digest (constant-time, prevents timing attacks).Prior to build d487e1d (2026-05-20), manifest metadata โ filename, source path, version note, delete state โ was stored in plaintext on every peer that held even one chunk for replication. Any replication-only peer could enumerate a user's entire library. This was a real leak against Grove's stated privacy goals.
The fix is documented in full at MANIFEST-METADATA-LEAK.md (canonical). Summary:
filename, source_path, version_note) live in an encrypted_metadata blob, encrypted with a per-manifest metadata key.metadata_envelope holds the metadata key wrapped with X25519 for each authorized recipient (creator + every grantee). Peers without a grant entry cannot decrypt the metadata..json (not filename-derived), so the filename is not revealed by the filesystem either.peer_can_access_manifest() in grove.py gates which peers receive manifests during sync. Metadata visibility is strictly per-grant.4f358be). The fleet holds no plaintext v0 manifests for own files.Identity in Grove is a keypair (Ed25519 for signing, X25519 for encryption). Three tiers:
| Tier | Who | What they can do |
|---|---|---|
| Peer | Any connected cell | Replicate encrypted chunks; no file/metadata access |
| Friend | Mutual explicit trust | Chat + file sharing (grants); metadata visible only for explicitly granted files |
| Observer | Portal account | Sees only files the cell owner has explicitly shared to that account |
peer_can_access_manifest./api/revoke-grant is only accepted if the request's granted_by field matches the X-Grove-Sender header. An attacker who compromises a peer secret cannot forge revocations from a different identity. See INVARIANTS.md / INVARIANT-AUDITS.md.| Data | Visible to replication-only peers? |
|---|---|
| File contents | No โ encrypted chunks, key never shared |
| File names | No โ v1 manifest metadata is encrypted; opaque on-disk names |
| Source paths | No โ encrypted in v1 manifest metadata |
| Chunk hashes | Yes โ needed for deduplication/replication |
| Your cell's pubkey | Yes โ identity |
| Your cell's name | Yes โ you choose what to advertise |
| Your IP address | Yes โ needed to connect |
| Storage usage (chunk count) | Yes โ chunk count visible |
| Chat messages | No โ end-to-end encrypted |
Friends with grants can decrypt filename/path for the specific files they were granted access to. They cannot enumerate your full library.
| Data | Visible? |
|---|---|
| Message contents | No โ E2E encrypted |
| Who's connected | Yes โ pubkeys + IPs |
| Message size / timing | Yes โ traffic metadata |
| Which cells communicate | Yes โ routing info |
The portal is an owner-gated window into a cell, accessible over the public WAN.
X-Grove-Public-Edge: 1 (overwriting any client-supplied value). check_dashboard_auth rejects owner session cookies and portal-admin /api/ access when this tag is present. The owner dashboard and admin APIs are therefore only reachable over Tailscale (or from localhost). This closed a real hole on the familynook node (2026-06-06, da49bd0).*portal_owner stamp matches) or files explicitly shared to it. _portal_visible_manifests() enforces this. This closed prior IDOR / roster-enumeration / XSS / reshare bugs (tasks #57-#60).portal_chat.key) before being written to the database (v1: format)./portal, /site, /invite, /relay, and explicitly public APIs are reachable over the public edge. Everything else requires tailnet or localhost.Grove's AI features (chat inference, image generation, peer AI proxying) include several security layers:
peer_model_policy config key (default: friends) gates which peers may use your cell's inference capacity โ friends = only mutual friends, none = no remote access.encrypt_chat_message). The relay cannot read inference traffic._image_prompt_moderation() before reaching the worker queue. Hard-reject token patterns block the request at submission time and log to ~/.grove/image-gen-rejections.log. This was added after a 2026-05-18 incident.Chunks belonging to a peer are only deleted from your node by one path: a signed tombstone that propagates via the regular manifest-sync loop. process_tombstones() verifies the Ed25519 signature against the leaving peer's pubkey before purging anything.
The live /api/peer-leaving notification is treated as a hint only โ it removes the peer from config and health, but never deletes data, even when authenticated. Reason: shared peer secrets can leak; Ed25519 signatures cannot be forged. An attacker who obtained a peer's secret could remove the peer from your peer list, but cannot trigger a data purge.
All private key files should be chmod 600 (owner read/write only):
~/.grove/node.key # Ed25519 private key โ NEVER share
~/.grove/node_x25519.key # X25519 private key โ NEVER share
~/.grove/.key # Master encryption key โ NEVER share
~/.grove/peer_secret # API auth secret โ NEVER share
~/.grove/dashboard_auth # Password hash โ keep private
~/.grove/portal_chat.key # Portal chat at-rest key โ keep private
If you lose .key, your encrypted chunks are unrecoverable. Back up keys regularly.
Grove has 14+ locked behavioral invariants covering replication counting, trust boundaries, grant gating, routing, and chunk management. They are the authoritative source of truth for expected security behavior:
This document does not duplicate them. When a code path appears to violate an invariant, consult those files first.
X-Grove-Public-Edge tag; tailnet-only gate)disable_auto_update: true)desired_factor โฅ 3 reduces single-peer loss risk