deploy slice C+D+E (binary features): rustls Phase 1 + /metrics + ValkeyEventSink #25

Merged
alee merged 10 commits from deploy-c/binary-features into docs/deployment-topology-spec 2026-07-06 14:58:44 +00:00

10 Commits

Author SHA1 Message Date
2217c7bf7a feat(smoke): assert-lifecycle-event-lands-in-Valkey-stream hook (deploy-C §5.6, deploy-B smoke harness)
Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-06 03:17:04 -04:00
e6dc6fda14 feat(valkey_sink): ValkeyEventSink -- first Valkey consumer (deploy-C §5.6)
Implement a bounded-mpsc + background-task Valkey event sink:
- XADD lifecycle events to a capped stream (MAXLEN ~ 10000).
- Fire-and-forget emit(): one mpsc::try_send, count-and-drop on full.
- Arc<AtomicU64> drop counter surfaced in /metrics.
- main.rs selects ValkeyEventSink when RUTSTER_VALKEY_URL is set and
  falls back to TracingEventSink otherwise.
- config::valkey_url validates redis:// / rediss://.
- CI services:valkey: block on test and clippy jobs.

Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-06 03:04:10 -04:00
2ac33cc01b fix(deps): pin redis to 0.27 (MSRV 1.70, works with 1.85 toolchain)
redis 1.3.0 requires Rust 1.88, conflicting with the workspace MSRV of
1.85 (rust-toolchain.toml). redis 0.27.6 has MSRV 1.70, well within
1.85. The xadd_maxlen + StreamMaxlen::Approx API is identical between
0.27 and 1.3 — no kimi-worker T8 code changes needed beyond the version
pin. BSD-3-Clause license unchanged.

Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-06 02:29:55 -04:00
9c28b2328b chore(deps): add redis 1.3.0 (aio + streams + tokio-comp) workspace dep (deploy-C §5.6)
Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-06 02:26:54 -04:00
ab4fdb3b2f feat(metrics): hand-rolled Prometheus /metrics on own listener (deploy-C §5.5)
- Add crates/rutster/src/metrics.rs with metrics_router(),
  metrics_handler, and render_prometheus_text().
- Add config::metrics_bind() parser for RUTSTER_METRICS_BIND
  (default 127.0.0.1:9090).
- Spawn the /metrics listener in main.rs on its own tokio task,
  above the TLS/plaintext branch.
- Add integration tests covering the 503 path and the live media
  thread 200 path.
- Emit rutster_event_sink_drops_total only when the sink reports a
  concrete count; omitting the line avoids misleading zero-drops readings.

Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-06 02:16:42 -04:00
36bbe70b7b feat(media_thread): admission_rejects + event_sink_drops extend MediaStats (deploy-C §5.5)
Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-06 01:55:16 -04:00
61fcc730cc feat(main): TLS listener wiring + SIGHUP hot-reload (deploy-C §5.4)
main.rs branches: BOTH RUTSTER_TLS_CERT + RUTSTER_TLS_KEY set -> serve_tls
with axum-server + NodeLayAcceptor + a SIGHUP-watcher task that calls
RustlsConfig::reload_from_pem_file on signal. Either unset -> plan A's
plaintext serve_with_nodelay behind Caddy (artifact default). Partial
config (one set, one unset) panics at boot with a clear message.

Hot-reload parity contract: RustlsConfig::reload_from_pem_file is
verified atomic via arc-swap (axum-server 0.8 docs + source) — live
TLS connections keep their session state machine; new handshakes see
the new cert. The e2e reload test (serve_tls_hot_reload.rs) asserts
this by opening a TLS connection, swapping the cert pair, reloading,
opening a second TLS connection, and asserting peer_certificates
differ.

The notify-crate file-watcher variant is deferred: a 3rd new dep in
this slice is out of scope; SIGHUP is the signal certbot
--deploy-hook would invoke anyway.

Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-06 01:46:06 -04:00
55065c569d feat(serve_tls): NodeLayAcceptor + serve_tls_with_nodelay (deploy-C §5.4)
axum_server 0.8's Server builder exposes no tcp_nodelay method (verified
in source: src/axum_server/server.rs:32-360). Plan A made TCP_NODELAY
unconditional on the plaintext path; this slice extends parity to the TLS
path via a custom Accept<TcpStream, S> impl that calls set_nodelay(true)
BEFORE the inner RustlsAcceptor runs the TLS handshake. TLS does not undo
the option (kernel persists it on the fd across the bytes the TLS layer
writes); axum-server's own examples/rustls_session.rs documents this
wrapping pattern for a different reason.

serve_tls_with_nodelay is the production TLS serve path: axum_server::bind
+ NodeLayAcceptor + Handle::graceful_shutdown. main.rs switches to it
when RUTSTER_TLS_CERT + RUTSTER_TLS_KEY are both set (Task 4). Hot-reload
via RustlsConfig::reload_from_pem_file lands in Task 4's e2e reload test.

Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-06 01:26:19 -04:00
5d14215053 feat(config): tls_cert_path / tls_key_path env parsers (deploy-C §5.4)
Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-06 00:55:24 -04:00
66006400bf chore(deps): add axum-server 0.8 (tls-rustls) workspace dep (deploy-C §5.4)
Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-06 00:35:23 -04:00