PharosVPN
§03 · components

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

Go · single static binary (CGO_ENABLED=0) · SQLite by default, optional pure-Go Postgres · gRPC over mTLS · embedded SvelteKit 2 / Svelte 5 admin UI · SSH-based node agent onboarding.

github.com/PharosVPN/coxswain ↗

§ 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 coxswain gave 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. coxswain reaches 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

Go · gRPC server over mTLS · manages awg-quick@awg0 and xray.service.

github.com/PharosVPN/node ↗

§ 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 inside coxswain) or remote reverse tunnel (coxswain dials out to a public relay). Identical trust either way.
  • Sees only ciphertext. Profile bundles cross relay end-to-end encrypted; a compromised remote relay host cannot read user profiles.

Stack

Go · transparent gRPC proxy · reverse-tunnel transport (multiplexed) · mTLS.

github.com/PharosVPN/relay ↗

§ 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).

Stack

Shared Go core engine · native shells per platform (Swift / Kotlin / desktop) · installable router plugins (OpenWRT LuCI, OPNsense).

github.com/PharosVPN/caravel ↗