Core concepts
Soft sandbox
mailnix decides relay-vs-capture from three signals, never from the recipient domain. The legacy @sandbox.mailnix.ch rule is gone.
Three signals
- Project status. Anonymous projects always capture, regardless of recipient.
- Destination mode. A destination with
mode = 'sandbox'captures;'live'relays. New destinations default to sandbox; the explicitdestination_promote_to_livemutation flips them. Existing destinations were backfilled toliveby Migration 081. - No-route policy. When no destination matches, the project's
no_route_policydecides:capture(default for new projects),reject(default for existing), orrelay_default(currently behaves like reject).
The outcome field
Every trace.Send response (REST + MCP) carries an outcome field:
relayed: sent to a real provider.captured.anonymous_projectcaptured.sandbox_destinationcaptured.no_route
Each captured row records:
capture_reason: one of the three above.destination_id: which destination would have handled the relay (null for anonymous + no_route).intended_to/intended_from: recipient + sender as requested.addresses_seen: every email extracted from headers + body, GIN-indexed, powering cross-recipient search.
Anonymous projects can send anywhere
Anonymous projects send to any recipient. The capture happens because the project is anonymous, not because the recipient sits on a special domain. The MCP email_send_test tool description spells this out for AI clients.
Capture cap
Each project has a monthly_capture_cap (free tier = 5,000). Sends past the cap return HTTP 402 with error.code = "quota_exhausted_captures". The counter rolls inline at the month boundary; no cron required.
Full reference
The complete contract lives in the public reference at SOFT_SANDBOX.md.