Each group of a member's events track rides a stream of its own, and the relay reads each stream in a task of its own, so two inputs sent back to back reached the floor in either order. The floor counted them as they came: with a limit of one, it could forward a member's second input (seq 1) and refuse its first (seq 0), leaving subscribers with seq 1 and a gap at seq 0, the opposite of what a verifier counting in chain order decides. The floor now keeps each author's chain as it has received it (the first seq it lacks, the seqs above it, and the inputs waiting) and decides an input once it holds every record of the chain below it and no input below it waits. Floor::events_filter is async: an input ahead of a gap waits, with the rest of its stream, until the gap fills or the floor's hold runs out ([floor] hold_ms, 2 000 by default). One whose hold runs out is refused input_out_of_order, whichever object of its author's finds it so, and the chain is counted from the record after it. An input that arrives after a later input of its author was forwarded is refused input_out_of_order too. Floor::events_step is the synchronous step, tested with explicit instants; Floor::waiting counts the inputs held. Tests: tests/floor.rs sends a member's seq 1 first, waits until the relay holds it, then sends seq 0: seq 0 forwarded, seq 1 refused input_over_limit; and seq 1 alone, refused input_out_of_order no sooner than the hold, after which a late seq 0 is forwarded. Unit tests cover a chat between two votes, an object whose records are out of seq order, the chain counted from after a refused input, and an input whose stream is gone. The hardened test from Train D is kept; its config gains `..FloorConfig::default()`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
127 lines
5.3 KiB
TOML
127 lines
5.3 KiB
TOML
# joule-moq-relay: an example configuration. Every field is optional; these are the
|
|
# defaults unless a comment says otherwise. Nothing secret belongs here: keys are files
|
|
# the operator provides, and tokens are named by their SHA-256.
|
|
|
|
# Native QUIC (ALPN moqt-22) and WebTransport over HTTP/3 (ALPN h3), one UDP port.
|
|
quic_listen = "127.0.0.1:4443"
|
|
# The WebSocket fallback (plain HTTP upgrade; put TLS in front of it).
|
|
websocket_listen = "127.0.0.1:4480"
|
|
# The path of the WebTransport CONNECT and of the WebSocket upgrade.
|
|
path = "/moq"
|
|
# The New Session URI a shutdown GOAWAY names; empty reuses the current one.
|
|
goaway_uri = ""
|
|
|
|
[tls]
|
|
# cert_path = "/etc/joule-moq-relay/fullchain.pem"
|
|
# key_path = "/etc/joule-moq-relay/key.pem"
|
|
# Without files: a development certificate for these names, its SHA-256 printed.
|
|
dev_names = ["localhost", "127.0.0.1", "::1"]
|
|
|
|
[cache]
|
|
max_bytes = 268435456
|
|
|
|
[limits]
|
|
max_sessions = 4096
|
|
max_requests = 256
|
|
max_uni_streams = 1024
|
|
max_request_updates = 16
|
|
max_auth_token_cache_size = 16384
|
|
max_rendezvous_ms = 30000
|
|
upstream_timeout_ms = 5000
|
|
fill_timeout_ms = 2000
|
|
write_concurrency = 8
|
|
max_object_bytes = 16777216
|
|
unknown_alias_wait_ms = 1000
|
|
goaway_timeout_ms = 5000
|
|
# 0 for each of these: no limit. A session at max_session_ms is closed; one sending past
|
|
# max_ingress_kbps (averaged over ingress_burst_ms) is closed; the binary exits with status
|
|
# 70 past max_resident_mb. A rule with `unlimited = true` exempts its sessions from the first
|
|
# two.
|
|
max_session_ms = 0
|
|
max_ingress_kbps = 0
|
|
ingress_burst_ms = 4000
|
|
max_resident_mb = 0
|
|
websocket_buffer_bytes = 67108864
|
|
idle_timeout_ms = 30000
|
|
quic_send_window_bytes = 1048576
|
|
|
|
[receipts]
|
|
receipts_track_suffix = "_receipts"
|
|
|
|
# Check objects in this namespace against their receipts at the door (example; not a
|
|
# default). An empty trust list accepts any key whose signature verifies. A field "*"
|
|
# matches any field; `trust_field = 1` with `prefix = ["*", "*", "video"]` would accept, in
|
|
# each room, the key its namespace's second field names (WAI live-rooms §6.1).
|
|
[[receipts.door]]
|
|
prefix = ["example", "live"]
|
|
trust = []
|
|
hold_ms = 2000
|
|
|
|
[summary]
|
|
# key_path = "/etc/joule-moq-relay/summary-seed"
|
|
# out_path = "/var/log/joule-moq-relay/sessions.jsonl"
|
|
|
|
# The room grants tokens carry, for the per-hop session records of WAI rooms (an example;
|
|
# not a default): a session whose SETUP presents this token runs under this grant.
|
|
[[metering.grants]]
|
|
token_sha256 = "1df4b07d05b9b3b1115b4b6b22af319a2f99564a2fa2573a34896063c7513c10"
|
|
grant = "0000000000000000000000000000000000000000000000000000000000000001"
|
|
joules_ceiling_micro = 5000000
|
|
funds_ceiling = 500
|
|
currency = "EUR"
|
|
|
|
[auth]
|
|
required = false
|
|
|
|
# An example grant; the hash below is of the placeholder string "replace-me".
|
|
[[auth.grants]]
|
|
prefix = ["example"]
|
|
token_sha256 = "1df4b07d05b9b3b1115b4b6b22af319a2f99564a2fa2573a34896063c7513c10"
|
|
publish = true
|
|
subscribe = true
|
|
# true: the token is a room grant's record (WAI live-rooms §4.1), honoured only on a
|
|
# session whose SETUP proved possession of its principal's key (§10.5.2).
|
|
proof = false
|
|
# true: a session presenting this token is exempt from max_session_ms and max_ingress_kbps.
|
|
unlimited = false
|
|
# true: the token may seek back, fetching a track below its live edge (WAI live-rooms
|
|
# §10.6). A room grant from an authority below seeks back when its powers hold seek.
|
|
seek = false
|
|
|
|
# The DVR store (an example; not a default): a window of the tracks under each prefix, kept
|
|
# on disk with their receipts and served to FETCH below the live edge (WAI live-recording
|
|
# §8). Groups go oldest first, whole, past max_age_s (at most 86400, 24 hours) or past
|
|
# max_bytes for the window's tracks together. The store survives a restart: on start every
|
|
# object is checked against its receipt again, and one that does not verify is dropped,
|
|
# counted, and never served. `room_keys` are the room authorities and clocks whose decision
|
|
# events withhold what they deny or redact (WAI live-moderation §6.3). `takedown_keys` are the
|
|
# services whose signed takedowns the store honours (WAI takedown §10.1): it forgets what each
|
|
# covers and signs a purge under purges/ with the summary key.
|
|
[dvr]
|
|
path = "/var/lib/joule-moq-relay/dvr"
|
|
room_keys = []
|
|
takedown_keys = []
|
|
|
|
[[dvr.window]]
|
|
prefix = ["*"]
|
|
max_age_s = 900
|
|
max_bytes = 4294967296
|
|
|
|
# The floor of WAI rooms (WAI co-performance §6): the relay reads each room's turn schedules
|
|
# and handovers from the events tracks it forwards, and does not forward a media object a
|
|
# performer publishes out of turn under a schedule that refuses. A schedule is honoured from a
|
|
# room key (these, and the keys of `auth.authorities`) or from a principal whose session proved
|
|
# a grant with the direct power. Absent: the relay holds no floor. The relay counts each
|
|
# author's audience inputs in the order of its chain (co-performance §7.3): an input that arrives
|
|
# ahead of a gap in its author's chain waits up to `hold_ms` for the gap to fill, and is refused
|
|
# `input_out_of_order` if it has not.
|
|
[floor]
|
|
room_keys = []
|
|
hold_ms = 2000
|
|
|
|
# A room authority (an example; not a default): every WRG1 grant its key signs is honoured,
|
|
# beside its proof and within its window, with the rights live-rooms §10.2 gives it. `rooms`
|
|
# narrows it to these room ids; empty, every room it grants.
|
|
# [[auth.authorities]]
|
|
# key = "<the authority's Ed25519 key, hex>"
|
|
# rooms = []
|