# NiManyatta Real-time chat server built on xitca-web, SurrealDB (in-memory), and a custom WebSocket gateway. ## Quick start ```sh # Build and run the server cargo brct_srv # alias: cargo run --release --bin broadcast_server --features b_server ``` Server binds to `0.0.0.0:PORT` (default `8080`). ```sh # REST API http://127.0.0.1:8080/health # WebSocket ws://127.0.0.1:8080/ws?token= # Prometheus metrics http://127.0.0.1:8080/metrics ``` ## Environment variables | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `PASETO_KEY` | Yes (prod) | — | 32-byte base64 PASETO v4 symmetric key | | `DATABASE_PATH` | No | in-memory | Path to persistent SurrealDB data file | | `PORT` | No | `8080` | HTTP/WS listen port | | `ENVIRONMENT` | No | `development` | `production` enforces stricter config | | `TRUST_PROXY` | No | `false` | Trust `X-Forwarded-For` from upstream proxy | | `PROXY_TRUSTED_IPS` | No | — | Comma-separated peer IPs allowed to set `X-Forwarded-For` (see note below) | | `WS_ALLOWED_ORIGINS` | **Yes (prod)** | *(permissive)* | Comma-separated allowed `Origin` values for WebSocket upgrades (see note below) | | `CORS_ALLOWED_ORIGINS` | No | — | Comma-separated allowed CORS origins | | `SMS_PROVIDER` | No | `Console` | `Console` (prints OTP to stdout) or `Http` | | `SMS_API_URL` | No | — | External SMS provider endpoint | | `SMS_API_KEY` | No | — | SMS provider API key | | `OTEL_EXPORTER_OTLP_ENDPOINT` | No | `127.0.0.1:4317` | Set to `""` to disable OTLP | | `ENABLE_OTEL` | No | `false` | Set `true` to enable the OTLP exporter | | `ACCESS_TOKEN_DURATION_SECS` | No | `3600` | Access-token lifetime | | `REFRESH_TOKEN_DURATION_SECS` | No | `604800` | Refresh-token lifetime | ### Proxy & forwarded headers (`PROXY_TRUSTED_IPS`) `TRUST_PROXY=true` alone is **not** sufficient: the middleware only reads `X-Forwarded-For` when the *TCP peer IP* of the connection is listed in `PROXY_TRUSTED_IPS`. Requests from untrusted peers keep their raw peer address and any `X-Forwarded-For` header is ignored — a client cannot spoof its IP by sending the header directly. ``` # Example: reverse proxy on the same host and a dedicated LB subnet TRUST_PROXY=true PROXY_TRUSTED_IPS=127.0.0.1,::1,10.0.0.0/24 ``` ### WebSocket origin validation (`WS_ALLOWED_ORIGINS`) WebSocket upgrade requests are rejected with `403` when the `Origin` header is not in the allowlist. The allowlist supports `*` (any origin). - **Development** (default): an empty allowlist is permissive — every origin is accepted, so local tooling works with zero configuration. - **Production** (`ENVIRONMENT=production`): the config **fails closed** — startup is rejected unless `WS_ALLOWED_ORIGINS` is explicitly set. Set it to your web client origins (`https://app.example.com`), or deliberately to `*` if any origin is genuinely acceptable (e.g. an internal API). See `src/shared.rs` for the full `Config` definition. ## API ### Auth (public) | Method | Path | Body | |--------|------|------| | POST | `/api/v1/auth/register` | `{ "username", "password", "display_name"? }` | | POST | `/api/v1/auth/login` | `{ "username", "password" }` | | POST | `/api/v1/auth/refresh` | `{ "refresh_token" }` | | GET | `/api/v1/auth/login_types` | — | ### Auth (token required) | Method | Path | Body | |--------|------|------| | POST | `/api/v1/auth/logout` | — | ### Rooms (token required) | Method | Path | Body / Notes | |--------|------|--------------| | POST | `/api/v1/rooms/create` | `{name?, topic?, is_direct, invite: [user_id]}` | | POST | `/api/v1/rooms/{room_id}/join` | `{}` | | POST | `/api/v1/rooms/{room_id}/leave` | `{reason?}` | | POST | `/api/v1/rooms/{room_id}/invite` | `{user_id, reason?}` | | POST | `/api/v1/rooms/{room_id}/send` | `{content: MessageContent, reply_to?}` | | POST | `/api/v1/rooms/{room_id}/messages` | `{limit, direction, from?}` (RFC 3339 `from` token) | | PUT | `/api/v1/rooms/{room_id}/typing` | `{typing: bool}` | | POST | `/api/v1/rooms/{room_id}/read_receipt` | **501** — receipts are recorded via the WebSocket `MarkRead` path until the REST storage lands | | POST | `/api/v1/rooms/{room_id}/redact/{event_id}` | `{reason?}` (requires `delete_any_message`) | ### Roles (token required) | Method | Path | Description | |--------|------|-------------| | POST | `/api/v1/admin/server-role` | Set server role | | POST | `/api/v1/rooms/{room_id}/role` | Set room role | | GET | `/api/v1/rooms/{room_id}/roles` | List room custom roles | | POST | `/api/v1/rooms/{room_id}/roles/create` | Create custom role | | POST | `/api/v1/rooms/{room_id}/roles/{role_id}/update` | Update custom role | | POST | `/api/v1/rooms/{room_id}/roles/{role_id}/delete` | Delete custom role | | POST | `/api/v1/rooms/{room_id}/roles/assign` | Assign custom role | | POST | `/api/v1/rooms/{room_id}/roles/remove` | Remove custom role assignment | | GET | `/api/v1/rooms/{room_id}/permissions` | Get own resolved permissions | ### Sync (token required) | Method | Path | Description | |--------|------|-------------| | POST | `/api/v1/sync` | Full sync | ### SMS Auth | Method | Path | Description | |--------|------|-------------| | POST | `/api/v1/auth/sms/request` | `{phone_number}` — request OTP (Console provider logs it) | | POST | `/api/v1/auth/sms/verify` | `{phone_number, code}` — verify OTP + login | ## WebSocket Connect to `/ws?token=`. Unauthenticated connections receive a guest identity. Messages are JSON `ServerToClientMsg` / `ClientToServerMsg` per `nimanyatta-protocol`. ## Cargo aliases Defined in `.cargo/config.toml`: | Alias | Command | |-------|---------| | `brct_srv` | `cargo run --release --bin broadcast_server --features b_server` | | `brct_srv_linux` | Same + `uring` feature | | `build_srv` | `cargo build --bin broadcast_server --features b_server` | | `stress_tester` | `cargo run --release --bin chat_load_tester --features load` | | `consistency_tester` | `cargo run --release --bin chat_load_tester_consistency --features load` | | `real_tester` | `cargo run --release --bin chat_load_tester_realistic --features load` | ## Deployment See `deploy/` for Ubuntu VPS hardening and verification scripts. ## Known issues See `KNOWN_ISSUES.md`.