# RatDB wire protocol v1 Transport: TCP over loopback only in this implementation. Secure remote access uses an external SSH tunnel; there is no TLS negotiation. There is no greeting: the client sends authentication first. All responses, including protocol errors, use version 1. Every message is a 4-byte unsigned big-endian payload length followed by exactly that many UTF-8 JSON bytes. Zero-length frames are invalid. Authentication requests cap at 8192 bytes; all other frames cap at 1048576 bytes. The length is checked before body allocation. Partial TCP reads are assembled; partial/truncated frames, invalid JSON, missing/unknown typed request fields, and unknown operations are rejected. JSON objects inside document values retain normal serde_json semantics, including last-value-wins duplicate document keys; request envelope/typed field duplicates are rejected. JSON nesting has serde_json's default recursion bound. Requests have exactly `version` and `command`. Version must be integer 1. Command is a tagged object: ```json {"version":1,"command":{"op":"auth","username":"admin","password":""}} ``` Do not log auth request payloads. Success: ```json {"version":1,"ok":true,"data":{"authenticated":true},"error":null} ``` Authentication failure (generic for unknown user/wrong password): ```json {"version":1,"ok":false,"data":null,"error":{"code":"authentication","message":"authentication failed"}} ``` After authentication, the same connection accepts sequential requests and emits exactly one response per request. Clients should await each response before sending the next; no request IDs, multiplexing or unsolicited messages exist. Reauth returns an operation error without changing the session. Authentication failures, frame errors and wrong versions close the connection after a best-effort structured reply. Disconnects/timeouts may prevent delivery of that reply. | op | Additional fields | Success data | |---|---|---| | use | collection: string | selected collection | | collections | none | collections array, truncated flag | | put | id: string, document: JSON object | stored ID | | get | id: string | document or null | | delete | id: string | deleted ID (also for an absent key) | | find | field: string, value: JSON value | bounded scan result | | scan | none | bounded scan result | | stats | none | records, live_bytes, generation | | check | none | valid: true | | quit | none | bye: true, then connection closes | CRUD/scan/find require prior `use`. Collection selection is per connection and survives commands but not reconnection. Selecting a name changes session context. Use create to keep the name in listings while it is empty. Example: ```json {"version":1,"command":{"op":"use","collection":"cars"}} {"version":1,"command":{"op":"put","id":"car:001","document":{"make":"Ford"}}} {"version":1,"command":{"op":"get","id":"car:001"}} ``` Each line above represents a separate length-framed message; the protocol is not newline-delimited. Scan data contains `documents`, `examined`, `result_limit` (100), `examination_limit` (10000), `byte_limited`, and `possibly_truncated`. Results preserve key order and stop accumulating when the 512 KiB encoded-document budget is exhausted. Scan/find seek to the selected den. The examined count excludes work locating the first record and checking the range boundary. `possibly_truncated` is conservative, not a pagination cursor. Collections have a separate 1000-name/512 KiB budget. Check validates the entire active snapshot under the database mutex, including document keys, JSON object IDs, and catalog marker values and parents. Error codes: `bad_frame`, `version`, `authentication`, `server_busy`, `operation_failed`. Storage errors retain readable descriptions in `message`; operation errors keep the authenticated connection usable unless storage is poisoned, in which case reopen/restart is necessary. No server stack traces or passwords are returned. Put/delete success is sent only after the existing core commit completes all durability barriers. Losing the connection before receiving a response leaves commit status uncertain; clients must reconcile and must not assume failure. There are no multi-operation transactions. See SERVER.md for connection caps, deadlines, resource bounds, shutdown and credential policies. The typed `Command`, `Response` and `Session` plus `dispatch` are separable from TCP for future native IPC; authentication and access serialization remain the transport host's responsibility. ## Backward-compatible v1 namespace commands New typed commands: `navigate {name}`, `warrens`, `dens`, and `shinies`. `navigate` returns `{warren: string|null, den: string|null}`. At root, a plain name selects a warren; within a warren, it selects/replaces the den. `..` ascends one level, `/` resets to root, and `/name` selects a warren directly. Selections are per session. Failed navigation preserves prior context. CRUD/scan/find use the current warren and den. `warrens`, `dens`, and `shinies` return `{kind,names,truncated,limit:100,examination_limit:10000}`. Shinies returns only IDs. Dens requires a warren, shinies requires both selections. Names include explicit namespace markers and namespaces inferred from stored documents. Name listings have no continuation. Use page for ordered document traversal. Warren/den lists scan across the snapshot, so unrelated records can consume their examination budget. Shiny ID lists seek to the selected den and stop at its boundary. The old `use {collection}` command remains supported for old protocol clients: it explicitly selects that den in the `default` warren. The old `collections` operation lists default-warren dens as before. The updated shell sends `navigate`, and translates structured results into human messages. `show collections` in the updated shell aliases `show dens`. New clients need this server version for the additive commands; an older server rejects unknown operations. Stats/check still apply to the full instance. ## Application commands These additive v1 operations require a selected den. Older clients can continue using the original operations. | Command object | Successful data | |---|---| | `{"op":"insert","id":"c-1","document":{"name":"Jo"}}` | `{"inserted":"c-1"}` | | `{"op":"update","id":"c-1","fields":{"stage":"active"}}` | `{"updated":"c-1"}` | | `{"op":"page","limit":20,"filter":{"field":"stage","value":"active"}}` | `{"documents":[...],"next_after":null,"examined":1}` | Insert fails on a duplicate ID. Update fails for missing IDs, an empty patch, a non-object patch or an attempt to set `id`. It replaces supplied top-level fields in one commit. Other fields survive. `navigate` accepts `warren/den` or `/warren/den` as an absolute selection from any context. Components are validated before the session changes. Page accepts optional `after` (exclusive ID) and optional `filter` (top-level equality). Set `after` to the previous `next_after`; stop only on null. The limit is mandatory, from 1 through 100. `examined` counts candidate documents after seeking. A byte-limited record may be examined again on the next page. Each call reads at most 10,000 candidates, plus seek and range-boundary lookahead, and returns at most 512 KiB of serialized documents. Pages see committed state at request time, so records can change between requests. See API.md for cursor consistency. The shell accepts `use crm/contacts`, `insert c-1 {"name":"Jo"}`, `update c-1 {"stage":"active"}`, `page 20` and `page 20 "c-1"`. A printed continuation is directly executable. Shell `reconnect` opens a fresh authenticated session at the instance root. A failed request is never replayed automatically. For scripts, use `ratdb batch [ratdb://admin@127.0.0.1:7337]` with one shell command per line on stdin. Provide credentials via the existing `RATDB_PASSWORD` environment option and optionally `RATDB_USER` (default admin). Supply the password through a protected process environment. Successful authentication produces no output. Each command response is a JSON envelope on a single stdout line. A server error emits its envelope and stops with nonzero status. Parse, transport and authentication errors go to stderr and stop the batch. Earlier successful commands remain committed. EOF ends the session. Run the automated CRM example with `cargo test -p ratdb-cli --test workflows`. It creates contacts, companies, opportunities, notes and activities, updates a contact, filters, lists, deletes, and checks the results after server restart. ## Explicit namespace commands `{"op":"create","kind":"warren","name":"garage"}` persists the warren, even when empty. With a selected warren, `{"op":"create","kind":"den","name":"cars"}` also persists its parent. Repeated creates succeed and selection stays unchanged. Both return `{"created":""}`. Names follow the same component rules as navigation. `{"op":"drop_empty","kind":"den","name":"cars"}` removes an explicit empty den. Change kind to `warren` to remove a warren that has no documents or cataloged dens. A missing or populated target produces an operation error. The server checks emptiness and deletes while holding exclusive database access. Responses contain `dropped` and the resulting session `warren` and `den`. Dropping the selected namespace moves that session up a level. Other sessions retain their selections and may later insert documents there. The shell spells the operation `drop-empty` and requires both the kind and the name. Storage statistics include catalog records.