Architecture
How PearDock is structured: control plane, data plane, repository layout, and RPC model. All peer-to-peer, with no open ports required for remote Docker ops.
High-level
Control plane (Docker RPC) and optional data plane (Holesail port tunnels) stay separate:
flowchart LR
subgraph Client["Desktop client"]
UI[PearDock UI]
end
subgraph Server["peardock server"]
RPC[protomux-rpc handlers]
HS[HolesailServer tunnels]
D[dockerode]
end
UI -- "HyperDHT · Noise · protomux-rpc" --> RPC
RPC --> D
D --> DE[dockerd]
HS -. "hs:// per published port" .-> Port["127.0.0.1:hostPort"]
Remote[Remote user / peer] -- "Holesail client" --> HS
Planes
flowchart TB
subgraph Control["Control plane"]
C1[HyperDHT keypair]
C2[protomux-rpc methods]
C3[ACL · audit · stats pushes]
end
subgraph Data["Data plane · optional"]
D1[Holesail L4 proxy]
D2["hs:// capability URLs"]
end
Control --> Docker[Docker Engine API]
Data --> Ports[Host published ports]
| Plane | Technology | Purpose |
|---|---|---|
| Control | HyperDHT + protomux-rpc | Docker RPC: containers, deploy, logs, ACL, stats pushes |
| Data / tunnels | Holesail | L4 proxy of host:port ↔ remote peer via hs:// |
Why not replace RPC with Holesail?
Holesail tunnels bytes between sockets. PearDock needs structured methods, roles, audit, and pushes (stats, logs, terminal). Keep both.
Repository layout
shared/ Protocol + encodings + schema + crypto-auth (both sides)
server/
server.js Entry: HyperDHT listen
core/ Keys, peer registry, ACL, audit, vault
rpc/ PeerSession, handler registration
handlers/ Domain methods (containers, images, registry, vault, stacks, …)
services/ Docker, stats, events, Holesail, schedules, image-updates, registry-client
utils/ Validation, rate limit, logging, compose, GitOps
client/
connection.js Single HyperDHT + protomux-rpc link
manager.js Multi-server connections + persistence
api.js Typed RPC helpers
jobs.js Job tray + hybrid pull progress helpers
templateResolve.js Portainer template / stack resolve
app.js + libs/ Desktop UI (deploy, registry, add-container, …)
electron/ Electron shell + OTA + GUI bundle
assets/ Logos + favicons
peardock-branding/ Master brand package
RPC model
sequenceDiagram
participant C as Client
participant S as Server
participant D as dockerd
C->>S: handshake / ping
S-->>C: role · protocol version
C->>S: listContainers / deploy / …
S->>D: dockerode API
D-->>S: result
S-->>C: response
S-->>C: push:containers / push:allStats / …
Client → server methods (examples):
handshake,pinglistContainers,checkImageUpdates,deployContainer,recreateContainerpullImage/pushImage(optional vault credential),pruneImagesdeployStack, registry catalog/tags/manifest, vault CRUD, Swarm, tunnels, schedules…startTerminal,getContainerLogs
Server → client pushes:
push:containers,push:allStats,push:logspush:pullProgress,push:pushProgress,push:buildProgresspush:dockerEvent,push:terminalOutput
Defined in shared/protocol.js.
PROTOCOL_VERSION is negotiated on connect (currently 3 for HMAC auth).
Full catalog: RPC & API.
Security surfaces
- Noise transport (HyperDHT)
- Roles: viewer (default) / operator / admin + method ACL
- Elevate via
SERVER_SEEDproof or self-containedpd1.invites - Optional peer allowlist + revoke
- Rate limits per peer
- Audit log for privileged methods
- Registry vault AES-GCM (keyed from seed) + remote Registry HTTP API V2 browser
- Browse roots default-deny
Breaking changes from v1
| v1 (legacy) | v2 (current) |
|---|---|
| Hyperswarm topic = SERVER_KEY | HyperDHT listen on keypair from seed |
| Share topic hex with clients | Share public key with clients |
| Raw JSON on duplex streams | protomux-rpc methods + push channels |
| Monolithic server.js switch | Modular handlers under server/handlers/ |