Moat is an authentication and scoped token minting service for Media over QUIC (MoQ) relays. It sits between your identity provider and the relay, issuing cryptographic access tokens that control who can publish or subscribe to which namespaces.
- Pluggable Identity Providers — Google OAuth2 out of the box, extensible
IdpVerifiertrait for others - Guest Access — anonymous login for public rooms, no IdP required
- Room Management — create rooms with namespace prefixes, manage members with admin/publisher/subscriber roles
- Scoped Token Minting — CAT4MoQT tokens (COSE_Sign1 + ES256) with per-namespace publish/subscribe permissions
- Privacy Pass — RFC 9578 Blind RSA and Partially Blind RSA token issuance
- Invite Links — time-bound, usage-limited invite codes for private rooms
- API Documentation — auto-generated OpenAPI/Swagger UI at
/docs/
Clients (browsers, apps, CLI)
│ │ │
login │ mint │ PP │
▼ ▼ ▼
┌──────────────────────────────────────────┐
│ Moat │
│ │
│ Auth ── Token ── Rooms ── Privacy Pass │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ IdP C4M PostgreSQL HTTP ──────────▶ PP-Issuer
│ Registry (ES256) (users, Client │ (Blind RSA,
│ [Google] COSE rooms, │ PBRS)
│ Sign1 members, │
│ invites) │
└───────────────────┬───────────────────────┘
│ C4M token
▼
MoQ Relay
(validates pub/sub access)
- Rust 1.75+
- PostgreSQL 14+
# ES256 key for C4M token signing
openssl ecparam -name prime256v1 -genkey -noout -out c4m-private.pem
# Session secret
export MOAT_SESSION_SECRET=$(openssl rand -hex 32)createdb moat
export MOAT_DATABASE_URL=postgres://localhost/moatcargo run -- \
--database-url "$MOAT_DATABASE_URL" \
--session-secret "$MOAT_SESSION_SECRET" \
--c4m-private-key ./c4m-private.pemMoat starts on http://localhost:3200. Visit /docs/ for the Swagger UI, or /app for the web dashboard.
cargo run -p pp-issuer -- --bind 127.0.0.1:3300Then point Moat at it with --pp-issuer-url http://127.0.0.1:3300 (or set MOAT_PP_ISSUER_URL).
All options can be set via CLI flags or environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
MOAT_DATABASE_URL |
Yes | — | PostgreSQL connection URL |
MOAT_SESSION_SECRET |
Yes | — | HMAC-SHA256 secret (min 16 chars). Generate with openssl rand -hex 32 |
MOAT_BIND |
No | 0.0.0.0:3200 |
HTTP listen address |
MOAT_C4M_PRIVATE_KEY |
No | — | Path to ES256 PEM. Token minting disabled if unset |
MOAT_C4M_ISSUER |
No | moat |
Issuer claim in minted tokens |
MOAT_C4M_AUDIENCE |
No | moq-relay |
Audience claim in minted tokens |
MOAT_TOKEN_LIFETIME |
No | 3600 |
Token lifetime in seconds |
MOAT_GOOGLE_CLIENT_ID |
No | — | Google OAuth2 client ID. Google login disabled if unset |
MOAT_CORS_ORIGINS |
No | $MOAT_BASE_URL |
Comma-separated allowed CORS origins |
MOAT_DB_MAX_CONNECTIONS |
No | 20 |
Maximum database connection pool size |
MOAT_BASE_URL |
No | https://chat.mocha-net.dev |
Base URL for invite links |
MOAT_PP_ISSUER_URL |
No | https://pat.issuer.mocha-net.dev |
Privacy Pass issuer URL |
MOAT_PP_SIGNING_KEY |
No | — | Ed25519 key for RFC 9421 attester signatures |
Full API documentation is available via:
- Swagger UI at
/docs/when the server is running - OpenAPI spec at
openapi.json(also served at/api-doc/openapi.json)
Endpoints cover authentication, token minting, room management, invite links, and Privacy Pass (RFC 9578).
| Level | Who can join | Use case |
|---|---|---|
| public | Anyone (guests + authenticated) | Open rooms, demos |
| authenticated | Logged-in users only | Team rooms |
| private | Invite or explicit add only | Restricted access |
# Debug build
cargo build
# Release build (both moat and pp-issuer)
cargo build --release
# See all CLI options
cargo run -- --help
cargo run -p pp-issuer -- --helpUnit tests run without any external dependencies:
cargo testIntegration tests require a running Moat server with a PostgreSQL database:
# 1. Start Moat (see Quick Start above)
# 2. Run the integration tests against it
MOAT_TEST_BASE_URL=http://localhost:3200 cargo test --test api_tests -- --ignoredMOAT_TEST_BASE_URL defaults to http://localhost:3200 if not set.
Benchmarks measure the crypto hot paths (session token create/verify, namespace canonicalization, invite code generation):
cargo benchResults are written to target/criterion/ with HTML reports.
The openapi.json file is generated from utoipa annotations in the source code:
cargo test export_openapi -- --ignoredThe scripts/ directory contains end-to-end demos:
| Script | Description |
|---|---|
scripts/demo-2-guests.sh [moat_url] [relay_url] |
Two-guest demo: creates a room, logs in Alice (publisher) and Bob (subscriber), mints scoped tokens, and prints relay commands |
scripts/run-moq-chat-e2e.sh |
Starts Moat + MoQ relay + moq-chat dev server for full end-to-end testing. Requires moq-rs and moq-web repos alongside this one |
For production, deploy behind a reverse proxy (nginx, Caddy) that handles:
- TLS termination
- Per-IP rate limiting
- Security headers (HSTS, X-Content-Type-Options, CSP)
Licensed under the BSD 2-Clause License.