Repository navigation
v1.3.0
The relay can be watched, the API answers in one call, and the repository has a wiki
The theme is observability. Everything here exists because an operator running this could not see enough of it.
The WEB relay reports on itself
The relay reported nothing. The proxy beside it has served /stats since before any of this; the relay — the internet-facing half, the one terminating every session and carrying every byte — answered four request paths and published none of it. The first sign of trouble was a user saying so.
Enable it with one key:
[web]
stats-bind-to = "127.0.0.1:8100"curl http://127.0.0.1:8100/statsSessions, streams, bytes in each direction, profile names, reload outcomes, and a breakdown of why requests were refused:
"refused": {
"cover_served": 1841,
"not_found": 3,
"bad_token": 0,
"session_limit": 0
}The breakdown is the point. At three in the morning, cover_served climbing (clients presenting capabilities this relay does not recognise — usually a hostname or secret-set mismatch after a reload) and session_limit climbing (capacity) are completely different problems, and a single refusal count cannot tell them apart. Reload outcomes are counted apart from successes for the same reason: you have to be able to tell "my SIGHUP did nothing" from "my SIGHUP was never delivered".
It is a second socket, deliberately, and it must be loopback. The relay refuses to start otherwise. Its public origin is published to the internet by a front proxy and holds one invariant: anything not carrying a credential the relay minted is answered by the cover site, on every path. A real endpoint there would break that — a 401, a JSON body, even a distinctive response time says this hostname is not the website it claims to be, and token-gating does not help because the gate itself is the fingerprint. And this endpoint answers, with no credential at all, how many sessions are live and how many bytes have moved: exactly the traffic analysis the transport exists to deny.
Off by default.
GET /secrets answers the whole question
/secrets described a user's configuration and said nothing about what that user was doing, so a panel rendering one person's detail page had to fetch the whole of /stats and filter it. It now carries connections, last_seen, connected_ips and last_ip — the same numbers /stats reports, from the same counters, with the addresses gated on track-client-ips exactly as /stats gates them.
connections and last_seen are not gated: they are not addresses, and withholding them would break every panel that shows an online marker without protecting anyone.
The two endpoints are built by different code that happens to read the same counters, and nothing forced them to agree. There is now a test that they do.
A wiki
Twelve pages, written against the code rather than from memory: installation, a five-minute quick start, a complete configuration reference, the three connection types, a full WEB proxy walkthrough with a working Caddyfile, the management API, limits and quotas, monitoring, the security model, troubleshooting by symptom, and an FAQ.
It answers things that were previously only discoverable by reading example.config.toml — that replay protection is a stable Bloom filter checked on both doors, that keepalives and timeouts are fully configurable under [network], that IPv6 has four prefer-ip modes.
The source lives in docs/wiki/ so corrections can arrive as pull requests, which a wiki cannot accept.
The repository is contributable now
Issue forms rather than templates, so a report arrives with the version, the entry path and a config that has already been asked to have its secrets removed. Blank issues are off, and the chooser sends questions to Discussions — now enabled — and vulnerabilities to a private advisory. A pull-request checklist specific to this codebase's actual traps, CONTRIBUTING.md, and Contributor Covenant 2.1.
The browser harness finally runs
The bridge page has a test that runs it in a real browser engine. It is the only test that can catch a page correct by every static reading that does nothing inside a client's WebView. Nothing ran it — no workflow installed Node or a browser, so it skipped silently on every CI run, and a skip on a dashboard is indistinguishable from a pass. Through v1.1.0 and v1.1.1 it protected nothing, and the mistyped RFC 6455 accept constant it exists to catch shipped twice before a real client found it.
There is now a job that installs the harness and runs it, and the test turns its skips into failures when that job asks it to. It passed on its first real run.
Also
mtg-proxy accessgained per-listener links in v1.2.0; the wiki documents them properly now.- The concurrency test went from 24 streams to 200 across 8 users, and asserts the goroutine count returns to its baseline — a correct counter says nothing about whether the goroutines behind it went away.
- Playwright pinned to 1.62.1.
Upgrading
Additive. No config key renamed or removed, no release asset renamed, default accept-modes still ["ee"], and both new surfaces default to off — so a deployment that changes nothing behaves exactly as it did on v1.2.1. features on /stats is unchanged from v1.2.0.
To start watching a relay, add stats-bind-to under [web] and restart it. That key is structural: a reload will refuse it, because moving a bound socket needs a restart and a reload that accepted a new address would report success while the counters answered at the old one.
Full Changelog: v1.2.1...v1.3.0