coxswain · node · relay · caravel
Four components.
One controller, dumb public nodes, a stateless relay, and a mobile client that reads its profiles from interchangeable sources. Each keeps to its role; the trust boundaries do the heavy lifting.
§ coxswain · controller / management plane
coxswain
coxswain — the one who steers the fleet.
coxswain is the PharosVPN controller. It is the source of
truth for the fleet, the admin Web UI, the certificate authority,
the account & profile-sync service, and the engine that drives
every VPN node.
Role
- Private, behind NAT — zero inbound ports. Every connection is coxswain-initiated outbound. The controller never appears in public DNS.
- Drives the fleet, and keeps it correct. Holds a long-lived mTLS/gRPC connection to each
node: pushes config and peers, receives a live event stream. A reconcile sweep auto-heals drift and stale data planes; provisioning a profile or device pushes automatically. - Issues credentials. Holds the in-repo CA; mints node, relay, and per-user/device certificates.
- Serves admins. Embedded admin UI on localhost — fleet, paths, profiles, live sessions, alerts, audit log, and API tokens — live-updating over WebSocket, multi-admin safe via optimistic concurrency.
- Token API + audit. A management API secured by scoped, expiring tokens (hashed at rest); every action recorded in a hash-chained, tamper-evident audit log.
- Monitoring & analytics. Persists session history with per-session byte counts; an in-process engine raises alerts (best-effort, experimental), with a gRPC stream for SIEM ingestion.
- Serves users. Account login + end-to-end-encrypted profile sync, reached by clients only through a
relay(embedded by default).
Stack
§ node · VPN node agent
node
A fixed, public marker anchored out in the water — ships rely on it.
node is the PharosVPN node agent. It runs on every
public VPN node, runs the data plane (AmneziaWG + XRay), and
applies only the configuration the controller pushes to it over
mTLS. It is deliberately dumb: a compromised node
cannot compromise the fleet.
Role
- Public IP. Terminates end-user tunnels on UDP 443 (AmneziaWG) and TCP 443 (XRay / VLESS+REALITY).
- Stateless except for what
coxswaingave it. All config is written to disk only after the controller pushes it over a validated mTLS connection. - Control port. Listens for the controller's mTLS/gRPC connection: status, metrics, config push, live peer add/remove, service restart — and streams live events back.
- SSH is install-only.
coxswainreaches a node over SSH solely to install and update the agent; every operational instruction is gRPC. - Cold-start resilient. Comes up from disk every boot; controller offline ⇒ existing tunnels keep working.
Stack
§ relay
relay
A signal relayed onward — so the lighthouse can stay hidden.
relay is the PharosVPN relay — a stateless, public,
mTLS-terminating proxy that lets end-user clients reach a
controller that has no public presence. The controller
(coxswain) stays behind NAT;
relay is the only public ingress for clients.
Role
- Public ingress for clients only. Terminates client mTLS, forwards their gRPC streams to
coxswain. - Stateless. No database. Every identity lookup is delegated to
coxswain. - Sanitizing. Strips spoofable client metadata; injects exactly one trusted value — the verified device fingerprint.
- Two transports to
coxswain: embedded (in-process insidecoxswain) or remote reverse tunnel (coxswaindials out to a public relay). Identical trust either way. - Sees only ciphertext. Profile bundles cross
relayend-to-end encrypted; a compromised remote relay host cannot read user profiles.
Stack
§ caravel · client engine
caravel
The small, agile ship that crossed unknown oceans.
caravel is the PharosVPN client engine — the part
end-users actually run. It establishes the VPN tunnel and
acquires its profiles from whichever source fits the user: a
synced account, a QR scan, a file, or enterprise MDM. A shared Go
core drives every platform.
Role
- The VPN client. Runs the actual tunnel — multi-node, multi-protocol (AmneziaWG + XRay/REALITY).
- Profile sources, not modes. A VPN engine reads a local profile store; profiles enter that store from interchangeable sources — account sync, QR, file import, MDM managed config, deep link. "Synced vs unsynced" is just which sources are enabled, not a different app.
- Posture-aware. Personal: account login + QR + file import, with an admin section if the logged-in account is an admin. Managed (MDM config present): account login and admin hidden, profiles locked. One app, one store listing.
- Offline-resilient. Connects from cached local profiles when the account service is unreachable.
Platforms
First public client releases (v0.1.0, pre-alpha): macOS (signed +
notarized DMG), Linux (AppImage, x86_64 + aarch64), and Android
(APK). iOS signs for device (TestFlight pending an App Group), and
there are installable plugins for OpenWRT (a LuCI .ipk)
and OPNsense (an os-pharosvpn plugin).