The server pings a peer's tunnel address every 30 s and shows the median
of the last 5 minutes in the peer list (with a 1-hour sparkline) and a
24-hour chart on the peer page. Off by default; "active" pings only
while the device sends traffic, "always" keeps the tunnel up.
The uplink check only reported the IPv4 default route. Split it into
IPv4 and IPv6 uplink rows, and add a public address row for each, read
from the uplink interface (private addresses are shown as behind NAT).
In a terminal, install now asks for the domain, Let's Encrypt email,
endpoint, WireGuard port and admin password, shows a summary and changes
nothing until confirmed. Re-runs offer the current settings and say how
many devices need a new config when the endpoint or port changes.
Flags still work and skip their question; -y or no terminal skips all
questions. New -port flag sets the WireGuard UDP port. Every value is
checked before the system is touched.
Add peer and Issue new config offer the same choice as the web UI: show
the config now or send a one-time setup link (1 h, 24 h or 7 d, PIN on by
default). The link sheet shares the link through the iOS share sheet and
shows the PIN, expiry and a QR code of the link. The peer page gets a
setup link card with share, copy and revoke.
A config can now be handed over as a one-time link, valid for 1 h, 24 h or
7 days and protected by a PIN by default. Keys are made only when the link
is opened; the link works once and is revoked after 5 wrong PINs. Issuing a
new config offers the same choice, and the current config keeps working
until the link is used.
Remove the option to paste a client's public key, in the web UI, the API
and the iOS app.
- The stats sampler records sessions per peer: start, end, address and
traffic. A session ends when the peer goes quiet or is disabled; a new
one starts when the device changes networks. Stored in stats.json and
kept as long as the daily traffic history (max 1000 per peer).
- Country and network operator come from the free DB-IP Lite databases
(CC BY 4.0), downloaded monthly and looked up locally, so peer
addresses never leave the server. Settings → Data retention can switch
this off, which deletes the databases.
- API: GET /peers/{id}/sessions; peer stats include the current location;
settings include the database status.
- Web UI and iOS app: connection history card, location line, country
code in the peer list (web), switch in data retention.
Claude-Session: https://claude.ai/code/session_01RAnLbyQZ5ZTA7KqwXP98nw
iOS app (ios/, SwiftUI, iOS 17+) in the web UI's styling with the Kamon
logo. It covers everything the web interface does except password, API
tokens and backups: dashboard, peers with search and filter, peer detail
with traffic charts, add/edit peers, one-time config with QR code and
share sheet, server settings with apply bar, app settings, data
retention and log viewer.
- Pairing by QR code or pasted pairing code; token kept in the keychain;
self-signed certificates are pinned by SHA-256 fingerprint.
- Colour providers and logo drawing are nonisolated: SwiftUI's background
renderer calls them, and main-actor closures crashed there when the
camera scanner was open.
- App Store: version 1.0, export compliance, privacy manifest, app icon,
release.sh (archive and upload), listing text, review notes and 6.9"
screenshots in ios/AppStore.
Server:
- Full-access API tokens may use settings, logs and restart; password,
tokens, backup/restore and the admin username stay admin-only.
- Web pairing dialog gains "Copy pairing code".
- The development simulator reports health checks in Linux wording.
Claude-Session: https://claude.ai/code/session_01RAnLbyQZ5ZTA7KqwXP98nw
- Settings → Data retention: log file size, number of old log files,
hourly and daily traffic history. Stored as log and stats in
config.json, validated, and applied without a restart; lowering a
limit deletes older log files and history after confirmation.
- Traffic history is now pruned by time instead of by bucket count.
- Server page DNS provider list offers only Quad9 and Custom.
Claude-Session: https://claude.ai/code/session_01RAnLbyQZ5ZTA7KqwXP98nw
- Kamon logo (crest ring around the ghost) as favicon.svg, 180 px
apple-touch-icon.png and the brand lockup in sidebar and login page,
with the katakana reading ゴーストワイヤー
- /favicon.ico redirects to /favicon.svg
- Client configs no longer set an MTU; each device picks its own, as
pivpn does. Server MTU changes no longer require reissued configs.
- Fix "null" rendered on the dashboard, wrapping activity times, and show
log lines as readable text instead of raw JSON
Claude-Session: https://claude.ai/code/session_01RAnLbyQZ5ZTA7KqwXP98nw
Single Go binary that manages a WireGuard server based on pivpn's defaults:
- config.json as the single source of truth, reconciled to the kernel via
netlink, wgctrl and its own nftables table (NAT, forward, input)
- web interface (dashboard, peers, peer detail, add peer, server, settings)
and a JSON API for the future iOS app, with session and API-token auth
- client private keys are never stored; configs and QR codes shown once
- per-peer traffic statistics in stats.json, logs in GHOSTWIRE.jsonl
- HTTPS via Let's Encrypt, self-signed, certificate files or off
- self-managing: install, update (restores the old binary on failure),
uninstall and passwd subcommands; systemd unit generated by the binary
Tested end to end on Ubuntu 26.04 (kernel 7.0) at dev.redetzke.aero.
Claude-Session: https://claude.ai/code/session_01RAnLbyQZ5ZTA7KqwXP98nw