inbuxa-relay: the relay binary, inbound in held and live mode #245

Merged
jcoffey-dev merged 1 commits from relay/binary into main 2026-10-30 03:47:45 +00:00
Owner

The relay side of phases 2 and 3 in docs/spec/features/relay.md: a new crate, crates/relay, building the inbuxa-relay binary. It uses no inbuxa server crate (RL-30).

What it does

  • join, run, status, reset. A container can join on its first run with INBUXA_RELAY_JOIN_TOKEN.
  • The link: TLS 1.3, a client certificate required and chained to the pinned downstream CA (RL-6). JMAP over HTTP/1.1 and WebSocket (RFC 8887) with push, plus uploads and downloads.
  • The JMAP methods: Relay/join, get and set; recipient sync, including Recipient/replace from an upload; Inbound get, query, changes, claim and set; RelayEvent.
  • Port 25, closed until the downstream has sent settings and recipients: real recipients only, no AUTH, per-IP limits, STARTTLS.
  • The spool: each message sealed to the downstream as it arrives and fsynced before 250; leases per connection; held mail expiry; 452 when nearly full.
  • Live mode: the reply after DATA waits for the downstream's verdict, and falls back to held after liveVerdictTimeout.

Tested: 13 tests, including an end-to-end run over real sockets: join and proof, a stranger CA refused at TLS, settings, recipients, unknown and foreign recipients refused, STARTTLS, a held message claimed, downloaded and unsealed, a live reject, and a live timeout. Plus the 20 in relay-proto. Clippy and rustfmt are clean, the fork checks pass, and Cargo.lock only gains the new package.

Footprint: a 5.4 MB release binary and 6.3 MB resident when idle, against budgets of 15 MB and 32 MB. This is the glibc build; the static musl build comes with the image (RL-31).

Not here yet: outbound (phase 4), DNSBL zones, Relay/upgrade, the image, and the whole server side. Until the server side lands, only the tests can drive the relay.

The relay side of phases 2 and 3 in `docs/spec/features/relay.md`: a new crate, `crates/relay`, building the `inbuxa-relay` binary. It uses no inbuxa server crate (RL-30). **What it does** - `join`, `run`, `status`, `reset`. A container can join on its first run with `INBUXA_RELAY_JOIN_TOKEN`. - **The link:** TLS 1.3, a client certificate required and chained to the pinned downstream CA (RL-6). JMAP over HTTP/1.1 and WebSocket (RFC 8887) with push, plus uploads and downloads. - **The JMAP methods:** `Relay/join`, `get` and `set`; recipient sync, including `Recipient/replace` from an upload; `Inbound` `get`, `query`, `changes`, `claim` and `set`; `RelayEvent`. - **Port 25**, closed until the downstream has sent settings and recipients: real recipients only, no AUTH, per-IP limits, STARTTLS. - **The spool:** each message sealed to the downstream as it arrives and fsynced before `250`; leases per connection; held mail expiry; `452` when nearly full. - **Live mode:** the reply after DATA waits for the downstream's verdict, and falls back to held after `liveVerdictTimeout`. **Tested:** 13 tests, including an end-to-end run over real sockets: join and proof, a stranger CA refused at TLS, settings, recipients, unknown and foreign recipients refused, STARTTLS, a held message claimed, downloaded and unsealed, a live reject, and a live timeout. Plus the 20 in relay-proto. Clippy and rustfmt are clean, the fork checks pass, and Cargo.lock only gains the new package. **Footprint:** a 5.4 MB release binary and 6.3 MB resident when idle, against budgets of 15 MB and 32 MB. This is the glibc build; the static musl build comes with the image (RL-31). **Not here yet:** outbound (phase 4), DNSBL zones, `Relay/upgrade`, the image, and the whole server side. Until the server side lands, only the tests can drive the relay.
jcoffey-dev added 1 commit 2026-10-30 03:33:09 +00:00
inbuxa-relay: the relay binary, inbound in held and live mode
ci / fork-checks (pull_request) Skipped
ci / build (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 11m6s
3eb5879bb3
The edge appliance from docs/spec/features/relay.md, inbound side:

- join, run, status and reset; INBUXA_RELAY_JOIN_TOKEN joins a
  container on its first run (RL-2, RL-8, RL-9, RL-10)
- the link: TLS 1.3 with a client certificate required and chained to
  the pinned downstream CA, else a TLS alert and nothing more (RL-5,
  RL-6); JMAP over HTTP/1.1 and RFC 8887 WebSocket with push; uploads
  and downloads
- the JMAP methods: Relay/join, get, set; RecipientDomain and Recipient
  sync with Recipient/replace; Inbound get, query, changes, claim, set;
  RelayEvent get, changes, set
- the door on port 25, closed until the first settings and recipient
  set: real recipients only (RL-1), no AUTH or relaying (RL-4), per-IP
  limits, early talkers dropped, STARTTLS, DSN parameters passed down
- the spool: sealed to the downstream as the message arrives and
  fsynced before 250 (RL-21), leases per connection (RL-15), held mail
  expiry, 452 when 90% full
- live mode: the SMTP reply waits for the downstream's verdict, and
  falls back to held after liveVerdictTimeout (RL-12, RL-13)

An end-to-end test drives it over real sockets as a downstream would.
Release build: 5.4 MB; 6.3 MB resident when idle.

Not here yet: outbound (phase 4), DNSBL zones, Relay/upgrade, the
static musl build and image (RL-31), and the server side.
jcoffey-dev merged commit 3e66a0269b into main 2026-10-30 03:47:45 +00:00
jcoffey-dev deleted branch relay/binary 2026-10-30 03:47:46 +00:00
Sign in to join this conversation.