Commit Graph
6 Commits
Author SHA1 Message Date
mrambossekandClaude Fable 5 6afcb131ef admin: terminate TLS in the binary, with a certificate that reloads itself
server-test / test (push) Successful in 33s
Direct rather than behind Caddy or nginx. This binary already serves TLS for the
control plane, so it is reuse rather than new machinery; one process with one
config file is most of what makes this thing pleasant to run; and a proxy on the
box would invite someone to eventually front the control plane too, which would
break SPKI pinning because clients pin that certificate's key.

The hard part of TLS is not termination, it is renewal - so the certificate is
re-read when the files change. No reload hook to write, and none to quietly stop
working months later and be noticed only after the certificate has expired. A
torn write (renewal tools write cert and key separately) keeps the previous
certificate rather than taking the listener down.

Not applied to the control plane, on purpose: clients pin that key, so replacing
it should cost an operator a moment's thought and a restart, not happen because
a file changed. Two listeners, two different right answers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 17:51:58 +02:00
mrambossekandClaude Opus 5 4ae744aae5 server: self-test — sysctl audit + egress-MTU self-proof ("server proven good")
server-release / image (push) Successful in 15s
server-test / test (push) Successful in 27s
server-release / release (push) Successful in 28s
A measurement server must prove its own host isn't distorting results:
- sysctl audit (/proc/sys): flags accept_ra on a static host, ICMP
  redirects, ICMP rate-limiting of the server's own errors, and disabled
  TCP options — each a measurement-fidelity hazard, with the "why".
- egress-MTU self-proof: DF PMTUD probe (IP_MTU_DISCOVER + getsockopt
  IP_MTU, no root — Linux-only, stub elsewhere) to external anchors. If the
  server's own uplink is below 1500, client MTU tests measure THIS server,
  so we say so.
Exposed at GET /admin/selftest (full report) and as server_selftest
{mtu_ok, sysctl_ok} in the profile so clients can trust or skip MTU tests.
Recommended deploy/99-echolot-sysctl.conf + README section.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 20:44:38 +02:00
mrambossekandClaude Opus 5 35baf70cdb server: canary DNS — authoritative zone with frozen §6.1 reference records
server-release / image (push) Successful in 15s
server-test / test (push) Successful in 26s
server-release / release (push) Successful in 27s
Stdlib DNS responder (no external deps): parses single-question queries
with EDNS OPT (bufsize, DO, ECS), serves the spec's frozen reference
records (ttl-{5,60,3600,86400} A/AAAA/TXT, many-rr 8×A in order, big-txt
~1800B), and per-query <nonce>.<session>.<zone> answers in 192.0.2.0/24.
UDP truncation sets TC past 512 (or the EDNS bufsize); TCP never
truncates — the EDNS-bufsize / TCP-fallback test. Every query is logged
(qname, resolver, transport, EDNS, ECS, case) and surfaced per session
prefix in GET /v1/sessions/{id}/observations as dns_canary. Profile gains
canary_zone + the canary-dns capability when configured.

Wire format validated against an independent client (correct rcodes,
answer counts, TC behavior, full EDNS response); unit tests cover
references, truncation-vs-EDNS, logging, NXDOMAIN.

Versioning: patch-first convention recorded in CLAUDE.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 20:08:30 +02:00
mrambossekandClaude Opus 5 43e1ba778a server: multi-address listeners, env-file config, self-update timer + checksums
server-test / test (push) Successful in 24s
server-release / image (push) Successful in 5s
server-release / release (push) Successful in 26s
- Comma-separated ECHOLOT_{CONTROL,UDP,TCP}_LISTEN; one listener/socket per
  address. Explicit binds matter on multi-IP hosts (a wildcard would also
  claim the SSH-only management address) and per-address UDP sockets are
  the substrate stun-5780 needs.
- systemd unit reads /etc/echolot-server.env (seeded once, never
  overwritten); --install-systemd with --self-update-api also installs a
  daily randomized update timer that try-restarts the service.
- selfupdate: SHA256SUMS verification is now mandatory before the atomic
  replace (integrity, not authenticity — signing still TODO).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 19:40:36 +02:00
mrambossekandClaude Opus 5 cae3c00990 server: document deployment requirements — no proxy, no 80/443, and why
server-test / test (push) Successful in 24s
Ports table, host-IP/second-IP/canary-zone requirements, and the two
protocol properties (SPKI pinning, observed-source fidelity) that a
reverse proxy would break. Coexists with traefik by never touching its
ports.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 16:54:52 +02:00
mrambossekandClaude Opus 5 8a80026d49 server: Go skeleton — control plane, UDP data plane, Docker + systemd modes
Pure stdlib. Implements the spec's core: enrollment (single-use tokens),
profile (SPKI pin, only real capabilities advertised), sessions with the
§2.4 HKDF-SHA256 key schedule; UDP data plane with the 32-byte ELT1
header, 4-byte HMAC gate, 1024-wide anti-replay window, ECHO_RESP with
observation block, TIMESYNC, and the §3.4 anti-amplification cap. Wire
format has tests (roundtrip + silent-drop cases); enroll→profile→session
smoke-tested live.

Modes: container (autodetect /.dockerenv|/run/.containerenv|cgroup, or
--docker/ECHOLOT_DOCKER=1; config via ECHOLOT_* env; distroless image;
network_mode host required — Docker NAT would falsify observed sources)
and native (--install-systemd/--uninstall-systemd with a hardened unit,
opt-in --self-update from Gitea releases; refused in containers).

CI: tests on any server/ push; server-v* tags build+push the image to the
Gitea registry and attach linux amd64/arm64 binaries + SHA256SUMS to a
release — the artifact self-update consumes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 13:09:08 +02:00