diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..7b737dd --- /dev/null +++ b/docs/API.md @@ -0,0 +1,149 @@ +# Telemt Control API + +## Purpose +This document specifies the control-plane HTTP API used for: +- runtime statistics access, +- user management, +- safe configuration mutations. + +The data-plane (MTProto proxy traffic) is out of scope. + +## Design Principles +1. Keep data-plane isolated. +The API must not affect MTProto hot paths. + +2. Keep configuration authoritative. +`config.toml` is the single source of truth for managed entities. + +3. Make writes safe. +All config mutations are validated and persisted atomically. + +4. Be explicit about concurrency. +Mutating endpoints support optimistic concurrency through revision matching. + +5. Prefer fail-fast contract errors. +Input validation errors are returned with machine-readable error codes. + +## Runtime and Configuration +Control API runtime is configured under `[server.api]`. + +Parameters: +- `enabled: bool` +- `listen: "IP:PORT"` +- `whitelist: [CIDR, ...]` +- `auth_header: string` (exact match against `Authorization` header; empty disables header auth) +- `request_body_limit_bytes: usize` +- `read_only: bool` + +Backward compatibility: +- `server.admin_api` is accepted as an alias while `server.api` is canonical. + +Operational note: +- Changes in `server.api` require process restart to take effect. + +## Protocol Contract +- Transport: HTTP/1.1 +- Payload format: JSON (`application/json; charset=utf-8`) +- API prefix: `/v1` + +### Success Envelope +```json +{ + "ok": true, + "data": {}, + "revision": "sha256-of-config" +} +``` + +### Error Envelope +```json +{ + "ok": false, + "error": { + "code": "machine_code", + "message": "human-readable text" + }, + "request_id": 1 +} +``` + +### Revision / Concurrency Contract +- Mutating operations MAY include `If-Match: `. +- If provided and stale, API returns `409 revision_conflict`. +- Revision is a SHA-256 hash of current config file content. + +## Endpoints + +### Read endpoints +- `GET /v1/health` +- `GET /v1/stats/summary` +- `GET /v1/stats/users` +- `GET /v1/users` +- `GET /v1/users/{username}` + +### Mutating endpoints +- `POST /v1/users` +- `PATCH /v1/users/{username}` +- `POST /v1/users/{username}/rotate-secret` +- `DELETE /v1/users/{username}` + +## Entity Contract: User +Managed user fields: +- `username` +- `secret` (32 hex chars) +- `user_ad_tag` (32 hex chars, optional) +- `max_tcp_conns` (optional) +- `expiration_rfc3339` (optional) +- `data_quota_bytes` (optional) +- `max_unique_ips` (optional) + +Derived runtime fields (read-only in API responses): +- `current_connections` +- `active_unique_ips` +- `total_octets` + +## Validation Rules +- `username` must match `[A-Za-z0-9_.-]`, length `1..64`. +- `secret` must be exactly 32 hexadecimal characters. +- `user_ad_tag` must be exactly 32 hexadecimal characters. +- Request body size must not exceed `request_body_limit_bytes`. + +## Security Model +1. Network perimeter. +Access is limited by CIDR whitelist. + +2. Optional application header auth. +If `auth_header` is configured, `Authorization` must match exactly. + +3. Read-only mode. +If `read_only = true`, mutating endpoints are rejected with `403`. + +## Mutation Approach +1. Acquire mutation lock. +2. Load config from disk. +3. Validate optional `If-Match` revision. +4. Apply in-memory mutation. +5. Run config validation. +6. Persist via atomic write (`tmp + fsync + rename`). +7. Return updated revision. + +Runtime apply path: +- Existing config watcher picks up persisted changes and applies them through the standard hot-reload path. + +## Known Limitations +1. Built-in TLS/mTLS is not provided by this API server. +Use loopback bind plus reverse proxy for external exposure. + +2. No pagination/filtering for user list in current version. + +3. `PATCH` updates present fields only. +Field deletion semantics are not implemented as explicit nullable operations. + +4. Config comments and manual formatting are not preserved after mutation. +Config is serialized from structured state. + +5. API configuration itself (`server.api`) is not hot-applied. +Restart is required. + +6. Atomic file replacement can conflict with external editors/tools writing the same config concurrently. +Use revision checks to reduce race impact.