Security model
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. 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 reconnectcapabilities— active grants (by jti)spentJtis— deleted or exhausted grantsrevoked— 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
| Asset | Sensitivity |
|---|---|
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 |
| Audit log | Medium: forensic integrity |
| Container data / env secrets | High: via inspect, logs, exec |
Adversaries
- 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.
Controls
| Control | Mechanism |
|---|---|
| Transport E2E | HyperDHT Noise |
| Capability ACL | viewer / operator / admin + MethodRoles |
| 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 (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 |
Residual risks
- 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.
10. Operator hardening checklist
- 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