V0 / DEPLOYMENT NOTES
A domain. A VPS.
A very small private Internet.
The VPS is deployed with Cloudflare DNS and publicly trusted certificates. An isolated Linux client passed IKEv2, private DNS, HTTPS, and revocation checks. The browser walkthrough remains a simulation; a physical iPhone test is still required.
Cloudflare is a good fit for DNS—not the VPN itself.
Use Cloudflare for authoritative DNS and Let’s Encrypt DNS-01 validation. Native IKEv2 runs on a VPS with public UDP 500 and 4500. Workers and a standard Cloudflare Tunnel do not replace that endpoint.
01 / Public DNS stays small
| Record | Value | Cloudflare mode |
|---|---|---|
join.secret.town | VPS public IPv4 | DNS only for v0 |
vpn.secret.town | VPS public IPv4 | DNS only (required) |
*.coolkids.secret.town | No public A/AAAA/CNAME | Private DNS only |
Do not create a wildcard public record that makes private names resolve accidentally. Keep existing email and other DNS records intact when moving nameservers. If Cloudflare already hosts your zone, no nameserver change is needed.
02 / One Linux VPS
The kit targets a dedicated Ubuntu 24.04 VPS. Private listeners bind only to 10.77.0.1. A separate public Caddy listener serves the join page. The firewall requires both an inbound IPsec policy and an active device IP for every private request.
Only 10.77.0.1/32 is selected into the v0 tunnel. Ordinary Internet traffic stays on the device’s normal connection. No full-tunnel NAT or IP forwarding is needed when all services run on the gateway.
03 / Publicly trusted certificates
Use Let’s Encrypt DNS validation for join.secret.town, vpn.secret.town, and *.coolkids.secret.town. The kit uses Certbot’s Cloudflare plugin and an RSA certificate for the VPN endpoint. A wildcard certificate does not require a public wildcard address.
Cloudflare Origin CA certificates are not suitable: Safari and native IKEv2 must trust the endpoint directly. Keep the zone-scoped Cloudflare API token on the VPS, never in chat or browser code. Public certificate transparency will reveal certificate names; this is private access, not an invisible domain.
04 / One credential per device
The local CLI creates a one-use invitation. The API atomically consumes it, allocates a non-recycled private IP, and encrypts a random EAP credential at rest. A short-lived, one-use download delivers the native profile to Safari.
The root-only gateway worker applies configuration changes about every two seconds. Revocation removes the device from the firewall allow-set first, clears stale credentials, and terminates its IKE session. The revoke command reports success only after the worker confirms completion.
05 / The test that matters
- Disconnected: query a public resolver for
radio.coolkids.secret.town. Expect NXDOMAIN. - Install the personalized profile on an ordinary iPhone and connect in Settings.
- Open the same hostname in Safari. It must load over HTTPS with no certificate warning.
- Visit an ordinary website and compare the public egress IP with and without the VPN. It should stay the same on the same Wi-Fi network.
- Disconnect. Private service access must stop, even if the browser retains an old DNS answer.
- Reconnect, revoke that device from the CLI, and confirm its existing session and new connections stop while another device still works.
A DNS cache is not an access-control boundary.
A reload may briefly use a cached address or cached page. Test a fresh network request and wait for the private DNS TTL to expire before asserting NXDOMAIN. Use the firewall test independently.
What’s implemented—and what isn’t
- Implemented: public join flow, example profile, SQLite enrollment, encrypted device credentials, single-use invitations/downloads, native IKEv2 profile generation, revocation/reconciliation code, example services, and deployment configuration.
- Not yet verified: profile installation and native split DNS/routing on a physical iPhone, Wi-Fi/cellular transitions, and the two-device acceptance test. Passing a Linux protocol test does not prove iOS compatibility.
- Not included: an admin dashboard, user-created Internets, native apps, or v1 membership-aware routing. SSH is an optional next test, not enabled by default.
Start with README.md and deploy/README.md in the archive. No credentials are included.