Foundation:
- Vendor xitca-web (upstream commit 7fa07dae, see xitca-web/VENDORED.md);
pin all six xitca crates via [patch.crates-io] in nimanyatta/Cargo.toml
- Reconstruct the lost nigig-common crate as nigig-lite/crates/common
(21 unit tests, clippy-clean), recovered from the wire contract in
crates/nimanyatta-protocol plus the server's own call sites
REST API:
- Register the four unwired room routes that were documented but never
reachable: {room_id}/invite POST, /typing PUT, /read_receipt POST,
/redact/{event_id} POST (root cause of the rooms.rs 50% coverage ceiling)
- REST read receipts now return 501 Not Implemented (documented) instead of
a 500 — receipts are recorded via the WebSocket MarkRead path
- README API tables corrected to the real paths/methods; env table expanded
(PROXY_TRUSTED_IPS, WS_ALLOWED_ORIGINS, CORS_ALLOWED_ORIGINS, ENABLE_OTEL,
token durations) plus proxy-trust and WS-origin explainer sections
Security & correctness:
- WS_ALLOWED_ORIGINS is now fail-closed in production: an empty list is a
configuration error at startup ('*' remains an explicit opt-out)
- Fix the OTP attempt counter: the BEGIN/IF SurrealQL block was a parse
error, so every wrong OTP failed the query and the fail-closed handler
masked it as a first-try 429 OTP_MAX_ATTEMPTS_EXCEEDED. Now a single
atomic conditional UPDATE ... WHERE attempts < $max, the handler logs the
underlying error before failing closed, and a regression test covers
store -> fetch -> increment -> persist
- Gateway KNOWN_ISSUES #1/#2/#5 fixed (centralised idempotent
cleanup_connection on all close paths; LoggedOut sent before
remove_session); #3 verified already enforced via session eviction
- DeliveryReceipt receipts now carry by_user: Option<String> per the
protocol crate (was Option<UserId> at the call sites)
Test suites:
- Repair every load-test binary: added mains for the three bins whose bodies
were #[tokio::test] functions (test items are cfg(test)-gated and vanish
from normal builds), fixed protocol-shape drift in the rest —
cargo check --features load --bins is clean
- New route coverage tests: invite/join/409-reinvite/404-unknown, typing
(member + 403 non-member), redact (happy path/404/403), read_receipt 501,
custom-role denial, non-member send 403, pagination edges (limit,
direction, invalid from-token), sync filter paths (room filter,
timeline_limit, invalid since-token, empty filter semantics)
- cargo test --features b_server: 51 passed / 0 failed
Docs:
- PLAN.md Phase 8 section + post-Phase-8 roadmap (per-site channels, pinned
document library, document read receipts, mentions/search/broadcast/
moderation/retention, offline queue — documented as open scope gaps)
- KNOWN_ISSUES.md statuses updated with the fixes above
6.2 KiB
NiManyatta
Real-time chat server built on xitca-web, SurrealDB (in-memory), and a custom WebSocket gateway.
Quick start
# 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).
# REST API
http://127.0.0.1:8080/health
# WebSocket
ws://127.0.0.1:8080/ws?token=<paseto-access-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 unlessWS_ALLOWED_ORIGINSis 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=<access-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.