Security

wayangi's security model in one page: what we do, what's signed, what's hashed, what we don't collect.

Download integrity

  • SHA-256 per binary — published in manifest.json and on the download page. Every binary listed has its hash visible inline.
  • ed25519 signature on the manifest — generated offline by cmd/wayangi-sign, signing key never on the hub. Detached signature served at manifest.json.sig.
  • Pubkey baked into every release binary via -ldflags="-X main.signingPubKey=...". The wayangi update command refuses to install unless the served manifest verifies against the baked-in key.
  • Cache-busting query params on every download URL keyed by the SHA prefix, so Cloudflare can't hand the agent a stale binary even briefly.

Authentication and session

  • Google OAuth via a hand-rolled Authorization Code flow against Google's endpoints — no third-party OAuth library.
  • Session tokens SHA-256 hashed in storage. Only the cookie holds the raw token; a DB-read attacker cannot replay sessions without a hash preimage.
  • OAuth state cookie: HttpOnly + Secure + SameSite=Lax + 10-minute TTL, compared with the state= query on callback.
  • Strict redirect validation on the post-login next URL: must start with /, parse cleanly, no scheme, no host. Closes the protocol-relative-redirect class (/\evil.com).
  • Email allowlist: domain-wide allowlist + per-email exceptions. Closed signup.

Device tokens

  • 64-character hex tokens. Generated by crypto/rand, 32 bytes of entropy.
  • SHA-256 hashed in the database; the raw token is shown to the operator exactly once at creation (or after explicit rotation).
  • One token per device. First successful bootstrap binds the token to the device's WireGuard pubkey; subsequent bootstraps from a different pubkey require --force-rebind.
  • Force-rebind notification: if a rebind happens, an email goes out to the device owner with the source IP and timestamp so a stolen-token rebind is visible.

Hub-side WireGuard

  • Each customer device's WG private key is generated on-device and never leaves the device. Persisted at /etc/wayangi/state.json (mode 0700, root-only).
  • The hub stores the device's WG public key only — sufficient to add it as a peer, insufficient to impersonate it.
  • Inbound-only egress policy: the hub firewall drops any new outbound flow whose source is the customer pool. Customer IPs can respond to inbound flows but cannot initiate outbound — neutralises the abuse-from-customer-IP vector at the source.
  • MSS clamp + IP forwarding installed automatically at hub startup — no out-of-band iptables required.

Audit log

The audit_log table records every state-mutating event: token rotation, device force-rebind, device delete, account login, payment activation. Each entry carries the actor (user or device), source IP, timestamp, and event details. Operator can review at /admin/audit and /admin/security (correlation view: token-rotate followed by force-rebind from a different IP within 5 min is flagged as the classic account-takeover pattern).

Rate limits

  • /api/v1/bootstrap: 20 burst per IP, refill 1 / 6 s.
  • /login: 10 / 30 s per IP.
  • POST /devices: 5 / 60 s per user ID.
  • POST /devices/{id}/rotate-token: 5 / 60 s per device.
  • /wg-relay (WebSocket fallback transport): 10 / 5 s per IP.
  • /check (public reachability probe): 5 / 60 s per IP, SSRF-restricted to the customer pool.

No telemetry

The agent reports its os/arch/hostname exactly once at bootstrap so the dashboard can show "Linux/arm64 on raspberrypi". After that it reports nothing else home. No usage analytics, no crash uploads, no automatic phone-home. wayangi update only contacts the hub when the user explicitly runs it.

Known gaps

wayangi is honest about what isn't done yet:

  • CSRF tokens on POST endpoints — protected by SameSite=Lax today; per-session CSRF token is on the roadmap.
  • Single-region hub — Jakarta only. Multi-region HA is on the roadmap but not shipped.
  • 30-day session TTL with no rotation — stolen cookie remains valid for a month, though the session token is hashed in storage.
  • No SOC 2 / ISO 27001 / HIPAA — wayangi is not certified for regulated industries.
  • No SAML / SSO — Google OAuth only today.

Reporting vulnerabilities

Email [email protected]. PGP not required. We respond within 72 hours. Coordinated disclosure preferred; please give us 30 days before public release.

/.well-known/security.txt is published per RFC 9116.

Operating entity & data residency

DALANG PTE. LTD. (Singapore UEN 202622331M), operating dalang.io infrastructure. Hub VM is physically located in Jakarta, Indonesia. Customer data (account info, devices, audit log) is stored in that VM's SQLite database. For GDPR data-subject requests, email [email protected].