Commit Graph

47 Commits

Author SHA1 Message Date
Daniel Redetzke 47762790ef Dashboard: no buttons next to the heading
The Server config and Add peer buttons are gone from the Dashboard
heading. Server is in the sidebar, Add peer is on the Peers page, and
with no peers yet the Peers card still links to adding the first one.
2026-10-05 23:41:27 +03:00
Daniel Redetzke 3e8dba6072 Fixes from the audit: input checks, apply order, sign-in limits
- The server endpoint must be a plain host name or IP address. It is
  written into client configs as is, so a newline could add lines such
  as PreUp, which wg-quick runs as root on the client.
- Listen addresses and the session length (1–720 hours) are checked.
  Before web settings or a restore are saved, the server tries the new
  listen addresses and certificate files, so a value it cannot start
  with is refused instead of stopping the service at the next restart.
- Kernel applies run one at a time and read the config once it is
  their turn, so an older config can no longer be applied last.
- Pending passkey sign-ins are capped: 10 per address, 1000 in total.
- Behind a local proxy, the last X-Forwarded-For entry is the client;
  earlier ones come from the client and are ignored.
- With LAN access off, peers are also kept from the IPv6 networks on
  the uplink, not only from its private IPv4 networks.
- A change that leaves no user with a password is refused, and so is a
  backup without one or from a newer version.
2026-10-05 23:09:03 +03:00
Daniel Redetzke aa4ca20296 API: the "Last apply" check is now "Kernel in sync"
GET /api/v1/status names the check Kernel in sync, the same as the
health row in the web interface, which now reads the new name directly.
The detail is unchanged: "applied <time>" or the kernel's error.
2026-10-05 22:29:47 +03:00
Daniel Redetzke 1bfede250c Health: "Last apply" reads "Kernel in sync"
The health row that shows when the config was last written to the
kernel is now called Kernel in sync, with the time since then, or Out
of sync and the kernel's error when applying failed. The API keeps the
check's name, so clients are unaffected.
2026-10-05 22:28:16 +03:00
Daniel Redetzke 8edde9f5e7 Links: arrows for moving around, ↗ for outside pages
Links to another page (All peers, Log, Add the first one) are ink with
an arrow that nudges on hover, the back link gets ←, and links that
open an outside page get ↗ and "opens in a new tab" for screen
readers, on the setup page too. Peer names look the same everywhere,
and links in text get a pale underline. Buttons and the sidebar stay
as they are.
2026-10-05 21:28:26 +03:00
Daniel Redetzke 7c36223de0 Live page: upload gets a light area too
Download and upload are both lines over light, see-through areas, so
upload no longer looks less important when it is the larger one.
2026-10-05 20:35:23 +03:00
Daniel Redetzke 2b7b41859d Live page: curved lines and speeds with one decimal
The chart draws download and upload as smooth curves that never dip
below zero or overshoot a peak, and speeds always show one decimal
from kbit/s up so the figures keep their shape as they change.
2026-10-05 20:28:56 +03:00
Daniel Redetzke ccf7b50c59 Live page: figures update without counting 2026-10-05 14:48:30 +03:00
Daniel Redetzke a82314ee94 Live page: 10-second averages for the figures and the table
The big figures, the per-peer speeds and the busiest-first order
average the last 5 steps instead of swinging with every burst, and the
figures sit in fixed-width columns so they no longer push each other
around. The charts still show every step.
2026-10-05 14:44:18 +03:00
Daniel Redetzke d83582eea1 Live page: streamed updates and a sliding chart
The server pushes each new step over server-sent events
(GET /api/v1/live/stream) the moment it is sampled, so updates no
longer arrive in uneven pairs. The chart slides left steadily between
steps instead of jumping, and the big numbers count to their new
value. Both stay still with reduced motion.
2026-10-05 14:39:35 +03:00
Daniel Redetzke 5f321f2979 Live page: online peers only 2026-10-05 14:34:08 +03:00
Daniel Redetzke 6661424daf Live page: the speed of every peer right now
A new Live page shows current download and upload per peer, updated
every 2 seconds, with the last 2 minutes as a chart and a small chart
per peer. The server reads the WireGuard counters every 2 seconds and
keeps 2 minutes in memory; GET /api/v1/live serves them, with since=
for only the newer steps. The dev simulator now adds traffic in
proportion to the time between samples.
2026-10-05 14:28:29 +03:00
Daniel Redetzke 456ae6a41e Dashboard: a range switch redraws only the traffic chart
The range buttons light up at once and fetch only the chart's stats,
like on the peer page, instead of reloading the whole dashboard.
2026-10-05 14:18:18 +03:00
Daniel Redetzke c7b4ca692e Dashboard: 24 h, 7 day and 30 day range on the traffic chart 2026-10-05 14:12:44 +03:00
Daniel Redetzke 6733bf2f3a Peer page: traffic chart opens on 24 hours 2026-10-05 14:06:05 +03:00
Daniel Redetzke e9bd3090a8 Charts: clock times and dates along the bottom
Traffic and latency charts label the x-axis with clock times every
3 hours (6 on phones, the date at midnight), or dates for the 7- and
30-day ranges. The hover readout for hourly bars shows the clock
time range instead of "3 h ago".
2026-10-05 14:01:10 +03:00
Daniel Redetzke 50467535a8 Remove old config copies from updates
Settings -> Upkeep -> Backup & restore lists the config.json.bak-* files
that update leaves behind and removes one or all of them; they hold the
same secrets as a backup. Removing needs a signed-in user and is logged.
After a successful update only the newest 3 copies are kept, and a copy
that would overwrite an older one (version unknown, or the same version
twice) gets the time appended. The backup card now also names preshared
keys and authenticator app secrets.

The update notice uses the existing compareVersions instead of its own.
2026-10-05 12:34:22 +03:00
Daniel Redetzke 39dde98af5 Settings in groups; the log on its own page
Settings is grouped into Access (users, sign-in, iOS app and API tokens),
Web interface (address and HTTPS), Logs & history and Upkeep
(updates, backup). Session length moved to Sign-in and no longer asks for
a restart. Log level, log size and traffic history share one card; the
country lookup is its own switch. Each card says how it saves.

The log viewer moved to a new Log page in the sidebar, with a filter for
changes only, and the Dashboard's Log link opens it.
2026-10-05 12:11:32 +03:00
Daniel Redetzke 59c4fb5ff1 Update notice: a newer release shows in the web interface
Once a day the server asks Gitea or GitHub, as picked under Settings ->
Updates, for the latest release. A newer one shows as a pill in the
sidebar, a banner on the Dashboard and in the Updates card with its
release notes and the commands to update this server. Drafts and
pre-releases are ignored, nothing about the server is sent, and the check
can be switched off. POST /updates/check checks now.
2026-10-05 11:59:27 +03:00
Daniel Redetzke 15d921019a Health card: addresses beside a list of checks
The public addresses stack on the left and the other checks are rows in
one list on the right, each with its raw setting right after the status.
Below 1000px the addresses move above the list.
2026-10-05 10:10:51 +03:00
Daniel Redetzke ef636e8a58 Health card: public addresses up top, checks as tiles
The public IPv4 and IPv6 addresses, with their uplink, lead the card.
Every other check is a tile with a plain-word status, the raw setting
and, when it fails, what is wrong. The header counts passing or failing
checks.
2026-10-05 09:09:44 +03:00
Daniel Redetzke dc002b7440 Show peer names in plain ink instead of underlined links 2026-10-05 01:57:00 +03:00
Daniel Redetzke cf1e1f7f63 Show dialogs again when an extension moves them
Bitwarden moves elements around in <body>. A moved dialog stayed open but
fell out of the top layer to the bottom of the page, so a confirmation
seemed to vanish and its checkbox stayed ticked unsaved.
2026-10-05 01:45:32 +03:00
Daniel Redetzke 00429ae484 Delete stored security keys 2026-10-04 22:35:19 +03:00
Daniel Redetzke 1de98f785b Passkeys only: drop adding security keys 2026-10-04 22:13:31 +03:00
Daniel Redetzke 0bafc3d7bc Japanese hover label on the passkey button 2026-10-04 22:03:12 +03:00
Daniel Redetzke 9d54d2d5ad Two-step sign-in: authenticator app, security keys and passkeys 2026-10-04 21:55:53 +03:00
Daniel Redetzke 420f1688ac Drop the tagline from the sign-in page 2026-10-04 21:02:37 +03:00
Daniel Redetzke 7d875450c8 Sortable peers table, shorter connection history 2026-10-04 20:53:56 +03:00
Daniel Redetzke ce4f2bc36d More web interface pages 2026-10-04 20:47:12 +03:00
Daniel Redetzke 464788d944 Sign out from an icon in the account row 2026-10-04 20:21:00 +03:00
Daniel Redetzke 9394c9bc7a Web interface setting 2026-10-04 20:16:57 +03:00
Daniel Redetzke ab5fb131a3 Keep the sidebar in view on long pages
Beside the page, the sidebar now stays in place while the page scrolls,
so the account link and Sign out are always visible; it scrolls by
itself in very short windows. On a phone it still stacks above the page.
Also show the version with one 'v': release builds already start with it.
2026-10-04 16:36:30 +03:00
Daniel Redetzke 0444a0b2be My account page
The signed-in user's profile, password and own app tokens move from the
Settings card to their own page at #/account, ready for more user
functions. The sidebar footer links to it with the user's name. Users
can now set their own note; the page header shows when and from where
the session started, which the server now records. Settings keeps the
Users table, where your own row links to My account.
2026-10-04 16:07:21 +03:00
Daniel Redetzke 15b1983a0b Multiple users, all admins
The single admin account becomes a list of users; config.json moves to
version 2 and the old admin is migrated on first start. Every user is an
admin. Sessions are tied to a user and their password, so deleting a user
or resetting a password signs them out at once. API tokens belong to the
user who made them and go away with that user.

Admins add users with a temporary password and choose whether it must be
changed at first sign-in; until then the API refuses everything but the
password change. Settings gets My account and Users cards, and the token
table shows each token's owner. 'GHOSTWIRE passwd [username]' resets any
user's password. A failed update now also restores config.json, since the
new version may have upgraded it.
2026-10-04 15:51:14 +03:00
Daniel Redetzke 34fb329e46 Hannya mark and Shippori Mincho B1 wordmark
Replace the Kamon ghost with the horned Hannya mask in favicon.svg,
apple-touch-icon.png, the web UI and the setup page. The wordmark and
katakana use Shippori Mincho B1 ExtraBold, bundled as a subset (ASCII
and katakana, 21 KB) under the SIL OFL and served from the binary.
2026-10-04 15:06:18 +03:00
Daniel Redetzke 4866d92216 Peers: optional latency check with per-peer setting
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.
2026-10-04 14:28:44 +03:00
Daniel Redetzke ce9353a17d Show the Sign in button's label in katakana on hover 2026-10-04 02:36:17 +03:00
Daniel Redetzke 85cd0eebbd Sidebar logo links to the start page 2026-10-04 02:31:36 +03:00
Daniel Redetzke e47e984c1d Add one-time setup links as an alternative to the QR code
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.
2026-10-03 23:10:28 +03:00
Daniel Redetzke 404e6015e5 Redesign login page: dark single column with stacked logo 2026-10-03 20:08:47 +03:00
Daniel Redetzke 584f115560 Remove sign-in heading and lockout hint from login form 2026-10-03 20:03:32 +03:00
Daniel Redetzke 242377fdef Add connection history per peer with country and network lookup
- 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.
2026-10-03 19:21:10 +03:00
Daniel Redetzke 066d669f3a Add native iOS app with App Store preparation
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.
2026-10-03 18:34:41 +03:00
Daniel Redetzke 46cfd4b329 Make log and traffic retention configurable; limit DNS presets to Quad9
- 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.
2026-10-03 17:48:33 +03:00
Daniel Redetzke 4bbe75348e Add Kamon logo as favicon and brand mark; fix MTU and UI issues
- 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
2026-10-03 17:12:27 +03:00
Daniel Redetzke 155dd7e264 GHOSTWIRE 0.1.2: WireGuard server manager with web UI and API
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.
2026-10-03 16:54:37 +03:00