Security & threat model
+Security model
- Audience: operators deploying PearDock in multi-operator or semi-trusted environments.
- Scope: HyperDHT P2P control plane + local Docker Engine socket.
+ How PearDock authenticates peers: keys in .env, default viewer,
+ admin seed proof, pd1. invites, roles on every RPC method, and revoke.
+ Also: threat model summary and hardening checklist.
1. Assets
+ + +1. Overview
++ PearDock is a self-hosted P2P Docker control plane. There is no central login. + Anyone who knows the server public key can attempt a HyperDHT dial. + That alone must not mean full Docker control — so the default role is viewer (read-only). +
+[Desktop client] --Noise/HyperDHT--> [peardock-server] --unix socket--> [dockerd]
+ |
+ +-- /opt/peardock/.env
+ +-- peardock-peers.json
+ +-- peardock-vault.json
+ +-- peardock-audit.log
+ Three legitimate ways in:
+| Paste in Add peer | Access |
|---|---|
Public key only (SERVER_PUBLIC_KEY) | Viewer (read-only) |
Public key + SERVER_SEED | Admin (seed never sent on the wire) |
Full pd1.… invite | Role in the invite — no seed sharing |
+ Canonical long-form doc in the source tree:
+ docs/SECURITY_AUTH.md.
+
2. Keys live in .env
+
+ After the first successful start, identity is written to
+ /opt/peardock/.env (mode 600). Check that file:
+
sudo grep -E '^(SERVER_PUBLIC_KEY|SERVER_SEED)=' /opt/peardock/.env
+# or: sudo cat /opt/peardock/.env
+ | Variable | Who | Notes |
|---|---|---|
SERVER_PUBLIC_KEY |
+ Safe to share for read-only | +How clients dial the server over HyperDHT | +
SERVER_SEED |
+ Admins only | +Server identity + HMAC MAC key + vault material. Never give to operators. | +
+ From the seed, PearDock derives a MAC key (HKDF). That key signs admin proofs and capability grants. + The seed itself is never transmitted on the wire. + Rotating the seed changes the public key; all clients must re-add the peer. +
++ Each desktop client also keeps a stable DHT keypair (peer id). + The server uses that peer id for registration, reconnect, allowlist, and revoke. +
+ +3. Roles
+Order: viewer < operator < admin.
+ Every RPC method has a minimum role (MethodRoles). Unknown methods default to
+ admin (fail closed). The UI may hide buttons; the server is authoritative.
+
| Role | Typical powers |
|---|---|
viewer |
+ List/inspect, logs, stats. No mutate. Cannot manage invites or ACL. | +
operator |
+ Day-to-day Docker mutate (start/stop, pull, stacks, many Swarm ops, terminals). Still cannot mint invites. | +
admin |
+ Everything + invite/revoke, vault, destructive prune paths, unrestricted CLI if enabled. | +
+ Invite management is admin-only:
+ invitePeer, listInvites, deleteInvite,
+ revokePeer, unrevokePeer, clearRevokedPeers.
+
4. Handshake elevation
+Baseline role (usually viewer) is resolved from env / admin keys / peer policy. Then handshake may elevate:
+-
+
- adminProof — seed ownership → admin +
- capability — HMAC grant from a
pd1.invite (or direct token)
+ - Otherwise stay at baseline +
+ If a capability fails but this client identity is already registered as operator/admin, + reconnect can use the registered role so restarts keep working. +
+
+ Optional: PEARDOCK_ADMIN_KEYS (fixed admin peer ids),
+ PEARDOCK_DEFAULT_ROLE, PEARDOCK_PEER_ALLOWLIST=1,
+ PEARDOCK_INSECURE_OPEN_ADMIN=1 (dev only — everyone is admin).
+
5. Admin seed proof
+Goal: prove “I know SERVER_SEED” without sending the seed over the network.
-
+
- In Add peer, paste public key +
SERVER_SEED(from.env).
+ - Client derives the same MAC key and builds an HMAC over
+
peardock-admin-v1 || nonce || peerId || serverPublicKey.
+ - Server verifies with its seed-derived key (constant-time compare). +
- Success → role admin. Failure →
ADMIN_PROOF_FAILED.
+
+ Best practice: only true admins hold the seed.
+ Onboard everyone else with invites. Prefer PEARDOCK_ADMIN_KEYS for fixed admin machines
+ if you do not want to paste the seed on every client.
+
6. pd1 invites & capabilities
++ Invites do not use Autopass or RocksDB. They are pure HMAC capability grants + packaged as a self-contained share string. +
+Capability token
+base64url(JSON payload) + "." + base64url(HMAC-SHA256(macKey, payload))
+ Payload includes role, jti (unique grant id), exp (or null = never), optional peer bind, and issued-at.
pd1 package
+pd1.<base64url JSON { publicKeyHex, capability, role, jti, ... }>
+ + Operators paste the full string in Add peer (never truncate). + The client decodes public key + capability and dials with the grant at handshake. +
+Mint (admin)
+-
+
- Connect as admin (key + seed). +
- Access → Create invite (role usually operator). +
- Default: never expires, unlimited uses (persistent). Optionally set TTL / max uses. +
- Copy the full
pd1.string and share it (not the seed).
+
Redeem (operator)
+-
+
- Paste full
pd1.…in Add peer.
+ - Client dials embedded public key and presents the capability. +
- Server verifies HMAC, then registers the client peer id with the grant role. +
7. Registration, reconnect, revoke
+State lives in peardock-peers.json (override with PEARDOCK_PEER_POLICY):
-
+
peers— registered elevated client identities for reconnect
+ capabilities— active grants (by jti)
+ spentJtis— deleted or exhausted grants
+ revoked— hard ban list of peer ids
+
+ After a successful elevate, reconnect can use the registered role even if that invite jti is later deleted. + To remove access for a lost laptop: Access → revoke that peer. + Unrevoke / clear revoked are admin recovery tools. +
+
+ Common error codes: CAPABILITY_INVALID, CAPABILITY_EXPIRED,
+ CAPABILITY_SPENT, ADMIN_PROOF_FAILED, PERMISSION_DENIED.
+ If a saved peer still holds a stale spent capability, paste a new full pd1 invite
+ or clear the cached grant.
+
8. Workflows
+Single admin
+-
+
- Install server; read keys from
/opt/peardock/.env.
+ - Add peer with public key +
SERVER_SEED→ admin.
+
Multi-operator (recommended)
+-
+
- Admin connects with seed proof. +
- Access → Create invite → share full
pd1.string only.
+ - Operator pastes invite → operator role; peer registered. +
- Lost device → revoke peer id. Leaked unused invite → delete invite, mint new one. +
Viewer guest
+Share only SERVER_PUBLIC_KEY. Observe-only.
9. Threat model (summary)
+Assets
SERVER_SEED | Critical: identity and vault key derivation |
| Docker socket access | Critical: full host container control |
| Registry passwords (vault) | High: encrypted at rest |
HMAC capability grants / pd1. invites | Medium: elevated access without seed (TTL / max uses optional) |
HMAC capability grants / pd1. invites | Medium: elevated access without seed |
| Audit log | Medium: forensic integrity |
| Container data / env secrets | High: via inspect, logs, exec |
2. Trust boundaries
-[Client] --Noise/HyperDHT--> [peardock server] --unix socket--> [dockerd]
- |
- +-- peardock-vault.json (AES-GCM)
- +-- peardock-peers.json
- +-- peardock-audit.log
- -
-
- Anyone with the server public key can attempt a DHT connection. -
- Default role is viewer (read-only) unless elevated via admin seed proof, HMAC capability (
pd1.invite),PEARDOCK_ADMIN_KEYS, peer policy, orPEARDOCK_INSECURE_OPEN_ADMIN=1.
- - Swarm is on by default (
ENABLE_SWARM=0to disable). Plugins are off unlessENABLE_PLUGINS=1.
- - Holesail tunnels are on by default. Each
hs://URL is a port capability.
- - Host FS browse is default-deny unless browse roots / open mode are set. -
3. Adversaries
+Adversaries
-
-
- Remote peer with public key only: gets viewer only (read-only lists/inspect/logs). -
- Stolen
pd1.invite / capability: limited by TTL / max uses + HMAC; revoke peer after redeem.
- - Compromised client: can use any role the peer holds until revoke. -
- Local host attacker with filesystem: can steal seed and vault if file perms are wrong — keep
/opt/peardock/.envmode 600.
- - Malicious container: out of scope for PearDock. Engine isolation applies. +
- Public key only: viewer (read-only). +
- Stolen pd1 invite: can elevate until delete/spend/expire; revoke peer after redeem. +
- Compromised client: holds that role until revoke. +
- Local filesystem on server: can steal seed if
.envperms are wrong.
+ - Malicious container: out of scope for PearDock; Engine isolation applies.
4. Controls (implemented)
+Controls
| Transport E2E | HyperDHT Noise |
| Capability ACL | viewer / operator / admin + MethodRoles |
| Default role | viewer; elevate via seed HMAC or pd1. invite |
| Default role | viewer; elevate via seed HMAC or pd1 invite |
| Admin proof | HMAC-SHA256 from SERVER_SEED (seed never on the wire) |
pd1. invites | Self-contained public key + capability share string (no vault DB) |
| Peer policy | Register, revoke, capability jti spend, optional allowlist |
| Audit | Append-only log for privileged methods |
| Rate limit | Per-peer limiter on RPC |
| Registry secrets | AES-256-GCM vault |
| Browse FS | Root allowlist / default deny |
| Tunnel targets | Loopback / allowlisted hosts only |
| pd1 invites | Self-contained public key + capability (no vault DB) |
| Peer policy | Register, revoke, jti spend, optional allowlist |
| Audit / rate limit | Privileged method log; per-peer limiter |
| Registry vault | AES-256-GCM |
| Browse FS / tunnels | Default-deny browse; loopback / allowlisted tunnel targets |
5. Residual risks
+Residual risks
-
-
- Default viewer is intentional for production. Use
PEARDOCK_INSECURE_OPEN_ADMIN=1only for single-operator dev.
- - Binary image/export streams are size-capped but still large; DoS via memory if many concurrent transfers. -
- JSON-over-RPC relies on handler validation (not full hyperschema end-to-end). -
- Swarm secrets/configs once enabled are highly privileged. +
- Default viewer is intentional. Use
PEARDOCK_INSECURE_OPEN_ADMIN=1only for single-operator dev.
+ - Persistent invites are powerful; tighten maxUses/TTL when sharing widely. +
- Large binary transfers can stress memory under many concurrent jobs. +
- Swarm secrets/configs, once enabled, are highly privileged.
6. Operator hardening checklist
+10. Operator hardening checklist
-
-
- After install, confirm keys in
/opt/peardock/.env(SERVER_PUBLIC_KEY,SERVER_SEED); back up seed offline; never commit.env
- - Connect as admin with public key +
SERVER_SEEDin the client (seed is session-only)
- - Share operators via Access →
pd1.invite (never shareSERVER_SEED)
- - Confirm public-key-only peers are viewer (read-only) -
- Set
PEARDOCK_ADMIN_KEYS=<your client public key hex>for fixed admin machines (optional)
+ - After install, confirm keys in
/opt/peardock/.env; back up seed offline; never commit.env
+ - Connect as admin with public key +
SERVER_SEED(seed is session-local)
+ - Share operators via Access → full
pd1.invite (never shareSERVER_SEED)
+ - Confirm public-key-only peers are viewer +
- Optional:
PEARDOCK_ADMIN_KEYSfor fixed admin machines - Enable
PEARDOCK_PEER_ALLOWLIST=1after registering operators - Do not set
PEARDOCK_INSECURE_OPEN_ADMINin production - Set
PEARDOCK_BROWSE_ROOTSonly if host path pickers are needed - Enable
PEARDOCK_AUDIT=1 - Treat
hs://tunnel URLs as secrets; setENABLE_HOLESAIL=0if unused - File mode
600on vault, peer policy, tunnels, audit,.env, identity
+ - Revoke lost client peer ids immediately
+ Install and multi-op steps: + Operator guide · + Quick start · + RPC & API +
+