Files
peardock-website/learn/index.html
T
2026-07-11 19:14:52 -04:00

574 lines
26 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>How PearDock Works · P2P Docker control</title>
<meta name="description" content="Learn how PearDock manages Docker over peer-to-peer HyperDHT. No open ports, no VPN, no public Docker API. Identity, hole punching, control plane, Holesail tunnels, and security." />
<meta name="keywords" content="PearDock P2P, HyperDHT, hole punching, Holesail, Docker control plane" />
<meta name="author" content="HoneyPeer, LLC" />
<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" />
<meta name="googlebot" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" />
<meta name="theme-color" content="#2dd4bf" />
<meta name="color-scheme" content="dark light" />
<meta name="application-name" content="PearDock" />
<meta name="apple-mobile-web-app-title" content="PearDock" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="msapplication-TileColor" content="#2dd4bf" />
<meta name="msapplication-config" content="/assets/favicons/browserconfig.xml" />
<meta name="format-detection" content="telephone=no" />
<link rel="canonical" href="https://peardock.boats/learn/" />
<link rel="alternate" hreflang="en" href="https://peardock.boats/learn/" />
<link rel="alternate" hreflang="x-default" href="https://peardock.boats/learn/" />
<!-- Open Graph -->
<meta property="og:type" content="article" />
<meta property="og:site_name" content="PearDock" />
<meta property="og:locale" content="en_US" />
<meta property="og:url" content="https://peardock.boats/learn/" />
<meta property="og:title" content="How PearDock Works · P2P Docker control" />
<meta property="og:description" content="Learn how PearDock manages Docker over peer-to-peer HyperDHT. No open ports, no VPN, no public Docker API. Identity, hole punching, control plane, Holesail tunnels, and security." />
<meta property="og:image" content="https://peardock.boats/assets/brand/og-image.png" />
<meta property="og:image:secure_url" content="https://peardock.boats/assets/brand/og-image.png" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="PearDock: decentralized Docker management on HyperDHT" />
<!-- Twitter / X -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="How PearDock Works · P2P Docker control" />
<meta name="twitter:description" content="Learn how PearDock manages Docker over peer-to-peer HyperDHT. No open ports, no VPN, no public Docker API. Identity, hole punching, control plane, Holesail tunnels, and security." />
<meta name="twitter:image" content="https://peardock.boats/assets/brand/twitter-card.png" />
<meta name="twitter:image:alt" content="PearDock: decentralized Docker management" />
<!-- Icons & PWA -->
<link rel="icon" href="/assets/favicons/favicon.ico" sizes="any" />
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicons/favicon-16x16.png" />
<link rel="icon" type="image/png" sizes="48x48" href="/assets/favicons/favicon-48x48.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/assets/favicons/apple-touch-icon.png" />
<link rel="manifest" href="/assets/favicons/site.webmanifest" />
<link rel="mask-icon" href="/assets/logo/peardock-wordmark.svg" color="#2dd4bf" />
<!-- Article -->
<meta property="article:section" content="Learn" />
<meta property="article:author" content="HoneyPeer, LLC" />
<meta property="article:publisher" content="HoneyPeer, LLC" />
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebPage",
"name": "How PearDock Works \u00b7 P2P Docker control",
"description": "Learn how PearDock manages Docker over peer-to-peer HyperDHT. No open ports, no VPN, no public Docker API. Identity, hole punching, control plane, Holesail tunnels, and security.",
"url": "https://peardock.boats/learn/",
"isPartOf": {
"@type": "WebSite",
"name": "PearDock",
"url": "https://peardock.boats"
},
"publisher": {
"@type": "Organization",
"name": "HoneyPeer, LLC",
"url": "https://peardock.boats"
},
"inLanguage": "en-US",
"primaryImageOfPage": {
"@type": "ImageObject",
"url": "https://peardock.boats/assets/brand/og-image.png",
"width": 1200,
"height": 630
}
}
</script>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="/src/styles/global.css" />
<link rel="stylesheet" href="/src/styles/docs.css" />
<link rel="stylesheet" href="/src/styles/learn.css" />
</head>
<body>
<div class="bg-mesh" aria-hidden="true"></div>
<div data-nav data-prefix="../"></div>
<header class="learn-hero">
<div class="learn-hero-inner">
<div class="badge mb-4">learn · how it works</div>
<h1>Peer-to-peer Docker control</h1>
<p class="lead">
PearDock is decentralized Docker management on the Holepunch stack.
<strong>You never need to open ports, expose Docker, or worry about remote access again.</strong>
You only need keys, peers, and the Engine you already run.
</p>
<div class="learn-key">
<div>
<strong>Key concept</strong>
Your laptop and a remote Docker host talk directly over an encrypted P2P stream.
No inbound firewall rules. No public <code>dockerd</code>. No always-on VPN.
Discovery and NAT traversal use HyperDHT. Docker commands ride protomux-rpc on that stream.
</div>
</div>
</div>
</header>
<div class="container learn-page">
<ul class="learn-toc">
<li><a href="#why">Why P2P?</a></li>
<li><a href="#problem">Old model</a></li>
<li><a href="#identity">Identity</a></li>
<li><a href="#connect">Connecting</a></li>
<li><a href="#nat">NAT &amp; hole punching</a></li>
<li><a href="#control">Control plane</a></li>
<li><a href="#data">Data plane</a></li>
<li><a href="#security">Security</a></li>
<li><a href="#fleet">Fleet</a></li>
<li><a href="#flow">End-to-end</a></li>
<li><a href="#glossary">Glossary</a></li>
</ul>
<!-- WHY -->
<section class="learn-section" id="why">
<h2>Why peer-to-peer for Docker?</h2>
<p class="learn-lead">
Docker Engine is powerful locally. Remotely, people usually open a TLS API port, maintain a VPN, or enroll hosts in a hosted panel.
PearDock takes another path: treat the host as a peer you reach with a public key.
<strong>No open ports, no exposed Engine, no remote-access checklist that never ends.</strong>
</p>
<p>
You can manage a home lab behind CGNAT, a VPS without punching firewall holes, or a fleet of edge machines.
Peers need to complete HyperDHT hole punching (or a relay fallback), and you need the right key material.
</p>
</section>
<!-- PROBLEM -->
<section class="learn-section" id="problem">
<h2>The traditional remote-Docker model</h2>
<p class="learn-lead">Most “remote Docker” setups trade convenience for a large attack surface or a permanent middleman.</p>
<div class="learn-compare">
<div class="learn-card learn-card--old">
<h4>✕ Centralized / exposed</h4>
<ul>
<li>Expose <code>dockerd</code> TCP + TLS to the internet</li>
<li>Always-on VPN just to run <code>docker ps</code></li>
<li>Hosted panel with your hosts enrolled forever</li>
<li>Single vendor outage = no ops UI</li>
</ul>
</div>
<div class="learn-card learn-card--new">
<h4>✓ PearDock peer model</h4>
<ul>
<li>Never open inbound ports for Docker or the control UI</li>
<li>Clients dial the <strong>public key</strong> over HyperDHT</li>
<li>Noise-encrypted RPC end to end. No VPN required.</li>
<li>You keep Docker and state on your machines</li>
</ul>
</div>
</div>
<pre class="mermaid">
flowchart LR
subgraph Traditional["Traditional"]
A1[Laptop] --> VPN[VPN / open :2376 / SaaS]
VPN --> D1[dockerd]
end
subgraph PearDock["PearDock"]
A2[Desktop client] -- "HyperDHT + Noise + protomux-rpc" --> S2[peardock-server]
S2 --> D2[dockerd]
end
</pre>
</section>
<!-- IDENTITY -->
<section class="learn-section" id="identity">
<h2>Identity: seeds and public keys</h2>
<p class="learn-lead">
PearDock v2 identity is a HyperDHT keypair derived from a secret seed.
It is not a shared “topic string” like some older swarm designs.
</p>
<div class="table-wrap">
<table>
<thead>
<tr><th>Material</th><th>Who has it</th><th>Purpose</th></tr>
</thead>
<tbody>
<tr>
<td><code>SERVER_SEED</code></td>
<td>Server only (secret)</td>
<td>32-byte seed; generates the listening keypair. Never share.</td>
</tr>
<tr>
<td><code>SERVER_PUBLIC_KEY</code></td>
<td>Operators / clients</td>
<td>64 hex chars. Paste into the client to dial this host.</td>
</tr>
<tr>
<td>Client keys</td>
<td>Each desktop install</td>
<td>Peer identity for ACL, invites, and reconnect cache.</td>
</tr>
</tbody>
</table>
</div>
<p>
On first start the server prints the public key (also in the journal when using systemd).
Rotating the seed changes the public key. All clients must re-add the peer.
Back up <code>/opt/peardock/.env</code> (or your dev <code>.env</code>) like any other root secret.
</p>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body">
<strong>Not the same as a Holesail <code>hs://</code> URL.</strong>
The server public key is for PearDock <em>control</em> RPC.
Tunnel capabilities are separate secrets created per published port.
</div>
</div>
</section>
<!-- CONNECT -->
<section class="learn-section" id="connect">
<h2>How a client finds and connects to a server</h2>
<p class="learn-lead">
Under the hood this is classic Holepunch connectivity: announce, look up, hole-punch (or relay), then encrypt.
PearDocks application layer starts only after that secure stream exists.
</p>
<div class="learn-peers">
<div class="learn-peer">
<h4>Client</h4>
<p>Desktop app holds public key + optional invite</p>
</div>
<div class="learn-arrow"><span></span>lookup</div>
<div class="learn-peer learn-peer--hub">
<h4>HyperDHT</h4>
<p>Distributed peer discovery &amp; signaling</p>
</div>
<div class="learn-arrow"><span></span>announce</div>
<div class="learn-peer">
<h4>Server</h4>
<p>Listens on keypair next to Docker</p>
</div>
</div>
<div class="learn-steps">
<div class="learn-step">
<h4>Announce</h4>
<p>The server registers its presence on HyperDHT under its cryptographic identity so dialers can find a path.</p>
</div>
<div class="learn-step">
<h4>Lookup &amp; punch</h4>
<p>The client queries the DHT, then both sides attempt coordinated UDP probes so NAT mappings open (“hole punching”).</p>
</div>
<div class="learn-step">
<h4>Encrypt &amp; RPC</h4>
<p>A Noise-encrypted stream carries protomux-rpc methods and server→client pushes (stats, logs, events).</p>
</div>
</div>
<pre class="mermaid">
sequenceDiagram
participant C as Client
participant DHT as HyperDHT
participant S as peardock-server
participant D as dockerd
S->>DHT: announce identity
C->>DHT: lookup public key
DHT-->>C: candidate endpoints
C->>S: hole punch / connect
C->>S: Noise handshake
C->>S: protomux-rpc handshake
S-->>C: role · protocol version
C->>S: listContainers / deploy / …
S->>D: dockerode
D-->>S: result
S-->>C: response + optional pushes
</pre>
</section>
<!-- NAT -->
<section class="learn-section" id="nat">
<h2>NAT traversal &amp; hole punching</h2>
<p class="learn-lead">
Most devices sit behind Network Address Translation. Routers map private IPs to public ports and block unsolicited inbound traffic.
That is why “just open Docker on the internet” is a bad default, and why P2P needs help to dial home.
</p>
<h3>What hole punching does</h3>
<ol>
<li><strong>Both peers learn public endpoints</strong> via DHT-assisted signaling (not a single app-specific rendezvous you host).</li>
<li><strong>Simultaneous outbound probes</strong> create temporary NAT mappings toward each other.</li>
<li><strong>Direct path preferred</strong> when mappings cooperate (common on home NAT).</li>
<li><strong>Relay fallback</strong> may apply when both sides are hard symmetric NAT. Traffic stays encrypted. Relays do not get plaintext Docker commands.</li>
</ol>
<div class="learn-compare">
<div class="learn-card">
<h4>Easier NAT</h4>
<p>Open / consistent mappings → hole punching usually succeeds quickly. Typical home routers often work well.</p>
</div>
<div class="learn-card">
<h4>Harder NAT / CGNAT</h4>
<p>Symmetric NAT or carrier-grade NAT can block direct paths. The stack may need more probes or a relay. Still no need to expose dockerd publicly.</p>
</div>
</div>
<p>
PearDock benefits from the same Holepunch/libudx generation of tools that power Hyperswarm and Holesail.
You dont configure port forwards for the control channel. You share a key.
That is the whole remote-access story: no open ports, no exposed Engine, no perpetual VPN.
</p>
</section>
<!-- CONTROL PLANE -->
<section class="learn-section" id="control">
<h2>Control plane: protomux-rpc over HyperDHT</h2>
<p class="learn-lead">
Once the encrypted stream is up, PearDock speaks a structured RPC protocol, not ad-hoc JSON lines.
That is how the UI stays rich: typed methods, roles, and live pushes.
</p>
<div class="learn-stack">
<div class="learn-layer">
<div class="learn-layer-name">UI</div>
<p>Desktop client (Electron / Pear) for containers, stacks, Swarm, tunnels, terminals, and jobs.</p>
</div>
<div class="learn-layer">
<div class="learn-layer-name">protomux-rpc</div>
<p>Request/response methods + push channels. <code>PROTOCOL_VERSION</code> negotiated on connect.</p>
</div>
<div class="learn-layer">
<div class="learn-layer-name">HyperDHT</div>
<p>Peer discovery, hole punch coordination, Noise transport under the app protocol.</p>
</div>
<div class="learn-layer">
<div class="learn-layer-name">handlers</div>
<p>Server modules for containers, images, volumes, networks, stacks, Swarm, vault, tunnels…</p>
</div>
<div class="learn-layer">
<div class="learn-layer-name">dockerode</div>
<p>Talks to local <code>dockerd</code> (usually the unix socket; server user in the <code>docker</code> group).</p>
</div>
</div>
<h3>Client → server (examples)</h3>
<ul>
<li><code>handshake</code>, <code>ping</code>: session setup and liveness</li>
<li><code>listContainers</code>, <code>startContainer</code>, <code>stopContainer</code>, <code>recreateContainer</code></li>
<li><code>deployContainer</code>, <code>deployStack</code>, <code>syncStackFromGit</code></li>
<li><code>getContainerLogs</code>, <code>startTerminal</code>, <code>containerStats</code></li>
<li><code>systemPrune</code>, Swarm methods, vault, schedules, tunnels</li>
</ul>
<h3>Server → client pushes</h3>
<ul>
<li><code>push:containers</code>, <code>push:allStats</code>: live fleet view without spam polling</li>
<li><code>push:logs</code>, <code>push:terminalOutput</code>: streaming operator surfaces</li>
<li><code>push:dockerEvent</code> and pull/build progress: keep the UI honest when the Engine changes</li>
</ul>
<p>
Method ↔ role maps live in the shared protocol. Viewers can observe; operators mutate day-to-day;
admins get destructive and policy-level actions. See
<a href="/docs/api">RPC &amp; API</a> and <a href="/docs/security">Security</a>.
</p>
</section>
<!-- DATA PLANE -->
<section class="learn-section" id="data">
<h2>Data plane: Holesail tunnels (optional traffic path)</h2>
<p class="learn-lead">
Managing Docker is not the same as reaching a published web app or TCP service.
PearDock keeps a second plane for that: Holesail L4 tunnels.
Still <strong>no open host ports</strong>. Share an <code>hs://</code> key instead of a firewall rule.
</p>
<pre class="mermaid">
flowchart LR
subgraph Control["Control plane"]
UI[Client RPC]
SRV[Server handlers]
UI --> SRV
SRV --> DK[Docker Engine]
end
subgraph Data["Data plane"]
HC[Holesail client]
HS[HolesailServer]
HC -- "hs:// capability" --> HS
HS --> Port["host:published-port"]
end
Control -.->|"orthogonal"| Data
</pre>
<div class="table-wrap">
<table>
<thead>
<tr><th>Plane</th><th>Technology</th><th>Carries</th></tr>
</thead>
<tbody>
<tr>
<td><strong>Control</strong></td>
<td>HyperDHT + protomux-rpc</td>
<td>Docker ops, ACL, logs stream control, deploy, stats</td>
</tr>
<tr>
<td><strong>Data / tunnels</strong></td>
<td>Holesail</td>
<td>Raw TCP/UDP bytes to a published host port via <code>hs://</code></td>
</tr>
</tbody>
</table>
</div>
<p>
<strong>Why not use only Holesail?</strong>
Holesail moves bytes between sockets. It is not an RPC framework, role system, or audit log.
PearDock needs structured methods and live UI channels, so control stays on protomux-rpc.
</p>
<p>
Tunnels are <strong>on by default</strong> (<code>ENABLE_HOLESAIL=0</code> to disable).
Treat every <code>hs://</code> URL as a capability secret. Details:
<a href="/docs/holesail">Holesail guide</a>.
</p>
</section>
<!-- SECURITY -->
<section class="learn-section" id="security">
<h2>Security model (operator mental model)</h2>
<p class="learn-lead">P2P does not mean “open to the world.” It means “reachable by those who hold the right cryptographic handles.”</p>
<ul>
<li><strong>Transport encryption:</strong> Noise on the HyperDHT stream, not cleartext Docker API on :2375.</li>
<li><strong>Roles:</strong> viewer, operator, and admin gate methods server-side.</li>
<li><strong>Peer policy:</strong> optional allowlists, invites, and revoke for multi-operator fleets without sharing root SSH.</li>
<li><strong>Rate limits and audit:</strong> blunt force protection and accountability for privileged calls.</li>
<li><strong>Registry vault:</strong> credentials encrypted at rest (AES-GCM keyed from server material).</li>
<li><strong>Tunnel SSRF guard:</strong> default allowlist for tunnel targets (loopback and published binds).</li>
<li><strong>Browse roots:</strong> host filesystem browse is default-deny and constrained.</li>
</ul>
<p>
You still must secure Docker itself: who is in the <code>docker</code> group, what images you run, and how secrets enter containers.
PearDock is a control surface. It inherits the power of the Engine socket it uses.
</p>
<p>Deep dive: <a href="/docs/security">Threat model &amp; hardening</a> · <a href="/legal/security">Vulnerability reporting</a></p>
</section>
<!-- FLEET -->
<section class="learn-section" id="fleet">
<h2>Multi-peer fleet UX</h2>
<p class="learn-lead">
One desktop client can keep many servers. Each peer is a separate HyperDHT identity with its own role and health.
</p>
<ul>
<li>Disk-backed peer cache under the client config directory</li>
<li>Auto-reconnect (retry cadence when a peer drops)</li>
<li>Environment tags (prod / staging / lab) for mental models</li>
<li>Job tray and notifications scoped to operator workflows</li>
</ul>
<p>
There is no “log into PearDock cloud.” There is only: which public keys did you add, and which of those peers are online.
</p>
</section>
<!-- END TO END -->
<section class="learn-section" id="flow">
<h2>End-to-end: first session</h2>
<p class="learn-lead">From zero to a live containers list. This is the path most operators take.</p>
<pre class="mermaid">
flowchart TB
A[Install peardock-server on Docker host] --> B[Read public key from journal]
B --> C[Install desktop client]
C --> D[Add peer · paste public key]
D --> E[Handshake · role · protocol version]
E --> F[listContainers · stats pushes · manage]
F --> G{Need app traffic?}
G -->|Yes| H[Create Holesail tunnel · share hs://]
G -->|No| I[Stay on control plane only]
</pre>
<ol>
<li>Install the server (one-liner or binary) on a machine with Docker.</li>
<li>Copy the <strong>public key</strong> from logs / journal.</li>
<li>Install the client; add the peer; wait for health (latency, Docker ok, role).</li>
<li>Operate: deploy, logs, terminal, stacks, Swarm, prune, schedules…</li>
<li>Optionally tunnel a published port with Holesail for users outside the control path.</li>
</ol>
<p>
Install: <a href="/download">Download &amp; install</a> ·
Guide: <a href="/docs/quickstart">Quick start</a> ·
Binaries: <a href="/releases">Releases</a>
</p>
</section>
<!-- GLOSSARY -->
<section class="learn-section" id="glossary">
<h2>Glossary</h2>
<div class="learn-glossary">
<details>
<summary>HyperDHT</summary>
<p>Distributed hash table used for peer announcements, lookups, and hole-punch coordination. Peers find each other by cryptographic identity without PearDock hosting a central directory of your fleet.</p>
</details>
<details>
<summary>Noise / Secret Stream</summary>
<p>Encrypted, mutually authenticated transport under HyperDHT connections. Application data (RPC) is not sent in the clear across the peer path.</p>
</details>
<details>
<summary>protomux-rpc</summary>
<p>RPC framework PearDock uses for named methods and server-initiated push channels over a multiplexed stream.</p>
</details>
<details>
<summary>Hole punching</summary>
<p>Technique where two NATed peers send coordinated outbound packets so temporary firewall mappings allow a direct path. If both sides are hostile NAT, encrypted relays may assist.</p>
</details>
<details>
<summary>NAT / CGNAT</summary>
<p>Network Address Translation (and carrier-grade NAT) share public IPs and block unsolicited inbound connections. That is why “open port 2376” is painful and unsafe as a default.</p>
</details>
<details>
<summary>Public key vs SERVER_SEED</summary>
<p>The seed is secret server identity material. The public key is what clients dial. Sharing the seed is equivalent to handing over the server identity.</p>
</details>
<details>
<summary>Holesail / hs://</summary>
<p>Peer-to-peer L4 reverse proxy. An <code>hs://</code> URL is a capability to reach a specific tunnel endpoint. It is separate from PearDocks Docker control key.</p>
</details>
<details>
<summary>dockerode</summary>
<p>Node.js Docker client used by the PearDock server to talk to the local Docker Engine API (typically via the unix socket).</p>
</details>
<details>
<summary>Role (viewer / operator / admin)</summary>
<p>Server-enforced permission tier for RPC methods. UI may hide actions, but the server is the authority.</p>
</details>
<details>
<summary>Rolling release</summary>
<p>Continuously updated binary channel used by the installer and <a href="/releases">releases</a> page. Recommended for current peardock-server and peardock-client builds.</p>
</details>
</div>
</section>
<div class="learn-cta">
<h2>Ready to try it?</h2>
<p>Install a server next to Docker, paste the public key into the client, and operate without a central control plane.</p>
<div class="learn-cta-actions">
<a class="btn btn-primary" href="/download">Install PearDock</a>
<a class="btn btn-secondary" href="/docs/architecture">Architecture docs</a>
<a class="btn btn-ghost" href="/docs/">All documentation</a>
</div>
</div>
</div>
<div data-footer data-prefix="../"></div>
<script type="module" src="/src/js/site.js"></script>
</body>
</html>