Files
peardock-website/docs/architecture.html
T
2026-07-11 19:01:46 -04:00

178 lines
6.9 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>Architecture · PearDock Docs</title>
<meta name="description" content="PearDock architecture: HyperDHT, protomux-rpc, handlers, Holesail data plane." />
<meta name="theme-color" content="#2dd4bf" />
<link rel="icon" href="/assets/favicons/favicon.ico" sizes="any" />
<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" />
</head>
<body>
<div class="bg-mesh" aria-hidden="true"></div>
<div data-nav data-prefix="../"></div>
<div class="docs-layout">
<aside class="docs-sidebar" data-docs-nav data-prefix="../"></aside>
<button type="button" class="docs-sidebar-toggle" aria-label="Docs menu"></button>
<article class="docs-main">
<h1>Architecture</h1>
<p class="docs-lead">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.</p>
<h2>High-level</h2>
<p>Control plane (Docker RPC) and optional data plane (Holesail port tunnels) stay separate:</p>
<pre class="mermaid">
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
</pre>
<h3>Planes</h3>
<pre class="mermaid">
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]
</pre>
<div class="table-wrap">
<table>
<thead>
<tr><th>Plane</th><th>Technology</th><th>Purpose</th></tr>
</thead>
<tbody>
<tr>
<td><strong>Control</strong></td>
<td>HyperDHT + protomux-rpc</td>
<td>Docker RPC: containers, deploy, logs, ACL, stats pushes</td>
</tr>
<tr>
<td><strong>Data / tunnels</strong></td>
<td>Holesail</td>
<td>L4 proxy of host:port ↔ remote peer via hs://</td>
</tr>
</tbody>
</table>
</div>
<div class="callout info">
<div class="callout-icon">i</div>
<div class="callout-body">
<strong>Why not replace RPC with Holesail?</strong>
Holesail tunnels bytes between sockets. PearDock needs structured methods, roles, audit, and pushes (stats, logs, terminal). Keep both.
</div>
</div>
<h2>Repository layout</h2>
<pre><code>shared/ Protocol constants + encodings (both sides)
server/
server.js Entry: HyperDHT listen
core/ Keys, peer registry, ACL, audit, vault
rpc/ PeerSession, handler registration
handlers/ Domain methods (containers, images, volumes, …)
services/ Docker client, stats, events, Holesail, schedules
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
app.js + libs/ Desktop UI
electron/ Electron shell + OTA + GUI bundle
assets/ Logos + favicons
build/icon.* Package icons
peardock-branding/ Master brand package</code></pre>
<h2>RPC model</h2>
<pre class="mermaid">
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 / …
</pre>
<p><strong>Client → server</strong> methods (examples):</p>
<ul>
<li><code>handshake</code>, <code>ping</code></li>
<li><code>listContainers</code>, <code>killContainer</code>, <code>containerTop</code>, <code>deployContainer</code>, <code>recreateContainer</code></li>
<li><code>pruneImages</code>, <code>getSystemDf</code>, <code>systemPrune</code></li>
<li><code>startTerminal</code>, <code>getContainerLogs</code></li>
<li><code>deployStack</code>, Swarm methods, vault, tunnels, schedules…</li>
</ul>
<p><strong>Server → client</strong> pushes:</p>
<ul>
<li><code>push:containers</code>, <code>push:allStats</code>, <code>push:logs</code></li>
<li><code>push:pullProgress</code>, <code>push:buildProgress</code></li>
<li><code>push:dockerEvent</code>, <code>push:terminalOutput</code></li>
</ul>
<p>Defined in <code>shared/protocol.js</code>. <code>PROTOCOL_VERSION</code> is negotiated on connect.</p>
<h2>Security surfaces</h2>
<ul>
<li>Noise transport (HyperDHT)</li>
<li>Roles: viewer / operator / admin + method ACL</li>
<li>Optional peer allowlist + invites</li>
<li>Rate limits per peer</li>
<li>Audit log for privileged methods</li>
<li>Registry vault AES-GCM (keyed from seed)</li>
<li>Browse roots default-deny</li>
</ul>
<p>See <a href="/docs/security">Security &amp; threat model</a>.</p>
<h2>Breaking changes from v1</h2>
<div class="table-wrap">
<table>
<thead>
<tr><th>v1 (legacy)</th><th>v2 (current)</th></tr>
</thead>
<tbody>
<tr><td>Hyperswarm topic = SERVER_KEY</td><td>HyperDHT listen on keypair from seed</td></tr>
<tr><td>Share topic hex with clients</td><td>Share <strong>public key</strong> with clients</td></tr>
<tr><td>Raw JSON on duplex streams</td><td>protomux-rpc methods + push channels</td></tr>
<tr><td>Monolithic server.js switch</td><td>Modular handlers under server/handlers/</td></tr>
</tbody>
</table>
</div>
<div class="docs-pager">
<a href="/docs/quickstart"><span>Previous</span><strong>← Quick start</strong></a>
<a class="next" href="/docs/operator"><span>Next</span><strong>Operator guide →</strong></a>
</div>
</article>
</div>
<div data-footer data-prefix="../"></div>
<script type="module" src="/src/js/site.js"></script>
</body>
</html>