Plan to make freeq AV usable by moving its media transport off WebSocket.
freeq AV audio is staticky for every client — humans and bots alike. The
cause is the media transport, not TTS or encoding.
The SFU (freeq-server/src/av_sfu.rs, a moq_relay::Cluster) is reachable two
ways:
:8080 (the SFU binds web_addr's port;--web-addr 0.0.0.0:8080). The separate staging.freeq.at:4443 — do not confuse the two.:443 → /av/moq → :8080QUIC is unusable for browsers today because the QUIC listener presents a
self-signed localhost certificate. So every client falls back to
MoQ-over-WebSocket/TCP — and that path collapses under sustained real-time
media: the instant a client's audio encoder starts publishing, the connection
floods transport error: connection closed ~50×/sec and audio degrades to
static.
Evidence. The eliza agent over WebSocket logged thousands of
connection closed. The same bot over QUIC (--sfu-url
https://irc.freeq.at:8080/av/moq) logged zero.
All AV media over QUIC/WebTransport. WebSocket remains only as a last-resort
fallback (moq-native already races the two and keeps whichever connects).
An earlier test had a QUIC client and a WebSocket client fail to see each other,
but they were on different SFU deployments (the QUIC client hit
staging.freeq.at:4443; the WebSocket client hit production) — so it proves
nothing about within-cluster interop. Whether a QUIC client and a WebSocket
client interconnect through one production moq_relay::Cluster is unresolved;
they negotiate different moq-lite versions (QUIC v03, WebSocket v02). Phase 2
re-tests this against the production SFU. If they bridge, the migration can be
gradual; if not, clients must cut over together.
av-deploy.sh cert; no code)¶freeq-server runs as user chad; Let's Encrypt's privkey.pem is root-only.
Copy the cert to a chad-readable directory and keep it fresh on renewal.
/etc/letsencrypt/live/tech.blueyard.com/{fullchain,privkey}.pem/home/chad/freeq-certs/ (owner chad, mode 600)./etc/letsencrypt/renewal-hooks/deploy/freeq-av-cert.sh to re-copyfreeq-server on each renewal..pem files exist, are chad-owned, mode 600.freeq-server/src/av_sfu.rs)¶In run_quic_accept, replace server_config.tls.generate = ["localhost"] with
the real cert when it is configured:
match (std::env::var("FREEQ_AV_TLS_CERT"), std::env::var("FREEQ_AV_TLS_KEY")) {
(Ok(cert), Ok(key)) => {
server_config.tls.cert = vec![cert.into()];
server_config.tls.key = vec![key.into()];
}
// Dev fallback: self-signed, browsers can't use it.
_ => server_config.tls.generate = vec!["localhost".to_string()],
}
moq_native::ServerTlsConfig takes cert: Vec<PathBuf> + key: Vec<PathBuf>,
zipped pairwise (see moq-native tls.rs::load_certs).
Add to /home/chad/src/freeq/.env.secrets (the systemd EnvironmentFile):
FREEQ_AV_TLS_CERT=/home/chad/freeq-certs/fullchain.pem
FREEQ_AV_TLS_KEY=/home/chad/freeq-certs/privkey.pem
Deploy + restart. Verify: a native client connects to
https://irc.freeq.at:8080/av/moq with cert verification enabled and the
handshake succeeds — proving the cert is publicly trusted.
--sfu-url https://irc.freeq.at:8080/av/moqfreeq-sdk-ffi): point the MoQ URL at QUIC :8080 instead of deriving:443/av/moq.connection closed stays ~0 under publish load.freeq-app)¶Point the moq-watch / moq-publish components at https://irc.freeq.at:8080/....
Investigate first: how freeq-app configures the moq components' endpoint,
and confirm moq-watch/moq-publish negotiate WebTransport (HTTP/3) when handed an
https://host:8080 URL. Verify in browser DevTools: an HTTP/3 / WebTransport
session, not a ws:// one.
All three client types in one call. Confirm they see and hear each other (the
v02/v03 split was QUIC-vs-WebSocket; all-QUIC should be uniform — verify). If a
version split remains, pin the moq-lite version across clients.
Make QUIC the default endpoint for web/iOS/bot; keep WebSocket as the
moq-native-raced fallback. Acceptance:
connection closed stays ~0 under load.freeq-app sets the:8080 is UDP; ufw is inactive on the host, but some client networksav_sfu.rs: backward-compatible — unset the env vars → self-signed fallback.deploy/av-deploy.sh deploy.deploy/av-deploy.sh [cert|deploy|verify|all] — pushes the current branch, then
over SSH pulls/builds/restarts freeq-server on chad@tech.blueyard.com and
health-checks the SFU.