PharosVPN
§03 · components

helm · buoy · beacon · 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.

§ helm · controller / management plane

helm

The ship's wheel — where you steer the fleet from.

helm 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 helm-initiated outbound. The controller never appears in public DNS.
  • Drives the fleet. Holds a long-lived mTLS/gRPC connection to each buoy: pushes config and peers, receives a live event stream.
  • Issues credentials. Holds the in-repo CA; mints node, relay, and per-user/device certificates.
  • Serves admins. Embedded SvelteKit admin UI on localhost, live-updating over WebSocket, multi-admin safe via optimistic concurrency.
  • Serves users. Account login + end-to-end-encrypted profile sync, reached by clients only through a beacon relay (embedded by default).

Stack

Go · SQLite (Goose migrations) · gRPC over mTLS · embedded SvelteKit 2 / Svelte 5 admin UI · SSH-based buoy agent onboarding.

github.com/PharosVPN/helm ↗

§ buoy · VPN node agent

buoy

A fixed, public marker anchored out in the water — ships rely on it.

buoy 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 buoy 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 helm 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. helm 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/buoy ↗

§ beacon · relay

beacon

A signal relayed onward — so the lighthouse can stay hidden.

beacon 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 (helm) stays behind NAT; beacon is the only public ingress for clients.

Role

  • Public ingress for clients only. Terminates client mTLS, forwards their gRPC streams to helm.
  • Stateless. No database. Every identity lookup is delegated to helm.
  • Sanitizing. Strips spoofable client metadata; injects exactly one trusted value — the verified device fingerprint.
  • Two transports to helm: embedded (in-process inside helm) or remote reverse tunnel (helm dials out to a public beacon). Identical trust either way.
  • Sees only ciphertext. Profile bundles cross beacon end-to-end encrypted; a compromised remote beacon host cannot read user profiles.

Stack

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

github.com/PharosVPN/beacon ↗

§ caravel · mobile client

caravel

The small, agile ship that crossed unknown oceans.

caravel is the PharosVPN mobile client — the app 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.

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.

Stack

Native — Kotlin (Android) + Swift (iOS) — over a shared core.

github.com/PharosVPN/caravel ↗