Nautilus Protocol
JSON-RPC 2.0 protocol for multi-language Nautilus clients.
Overview
This crate defines the stable wire format for communication between:
- Language-specific clients (JavaScript, Python, etc.)
- Nautilus engine (Rust binary running over stdin/stdout)
Public API
| Module | Key exports |
|---|---|
wire |
RpcRequest, RpcResponse, RpcError, RpcId, ok(), err() |
methods |
11 method-name constants (ENGINE_HANDSHAKE, QUERY_*, SCHEMA_VALIDATE), request param structs, response structs |
error |
ProtocolError enum (18 variants), stable error-code constants, Result<T> alias |
version |
PROTOCOL_VERSION constant, ProtocolVersion wrapper |
Design Notes
- Single consumer — only
nautilus-enginedepends on this crate today, but the API is designed for any language client that speaks JSON-RPC 2.0 over stdin/stdout. schema.validatetypes are defined but not yet implemented in the engine; they are reserved for a future release.- Value encoding (base64 for bytes, string for decimals, etc.) is specified in this README as the wire-format contract, but the Rust encoding/decoding logic lives in
nautilus-engine, not here.
Protocol Stack
- Transport: Line-delimited JSON over stdin/stdout
- Format: JSON-RPC 2.0
- Versioning: Protocol version included in every request
Protocol Version
Current version: 1
All client requests must include protocolVersion: 1 in their params. The engine will reject requests with unsupported versions.
JSON Encoding
The protocol uses a stable JSON encoding for Nautilus Value types to ensure cross-language compatibility.
Value Type Mappings
Note: These encoding rules define the stable wire format between clients and the engine. The actual encoding/decoding logic lives in the engine layer, not in this crate —
nautilus-protocolonly defines the request/response structures and theirSerialize/Deserializeimplementations.
| Nautilus Type | JSON Type | Example | Notes |
|---|---|---|---|
Null |
null |
null |
|
Bool |
boolean |
true, false |
|
I32 |
number |
42 |
32-bit signed integer |
I64 |
number |
9007199254740991 |
64-bit signed integer* |
F64 |
number |
3.14159 |
IEEE 754 double |
String |
string |
"hello" |
UTF-8 text |
Bytes |
string |
"SGVsbG8=" |
Base64 encoded |
Decimal |
string |
"123.45" |
String to avoid precision loss |
DateTime |
string |
"2026-02-18T10:30:00Z" |
RFC3339 / ISO 8601 |
Uuid |
string |
"550e8400-e29b-41d4-a716-446655440000" |
Hyphenated lowercase |
Json |
any | {"key": "value"} |
Pass-through JSON value |
Array |
array |
["a", "b", "c"] |
Homogeneous array |
Array2D |
array |
[["a", "b"], ["c", "d"]] |
2D array |
*Note on BigInt: JavaScript clients should be aware of Number.MAX_SAFE_INTEGER (2^53 - 1). For values outside this range, the engine may return strings in future protocol versions.
Example Value Encodings
Methods
engine.handshake
Initial handshake to validate protocol compatibility.
Request:
Response:
query.findMany
Execute a findMany query on a model.
Request:
Response:
query.create
Create a new record.
Request:
Response:
query.findFirst
Return the first record matching the given arguments (or null).
Request:
Response: Same shape as query.findMany (QueryResult).
query.findFirstOrThrow
Same as query.findFirst, but returns error code 3004 (Record not found)
if no matching record exists.
query.findUnique
Find a single record by a unique filter.
Request:
Response: Same shape as query.findMany (QueryResult).
query.findUniqueOrThrow
Same as query.findUnique, but returns error code 3004 (Record not found)
if no matching record exists.
query.createMany
Create multiple records in a single operation.
Request:
Response: MutationResult with count reflecting the number of inserted rows.
query.update
Update a record matching a filter.
Request:
Response: MutationResult with count and optional data (when
the dialect supports RETURNING).
query.delete
Delete a record matching a filter.
Request:
Response: MutationResult with count.
schema.validate (reserved — not yet implemented)
Validate a schema string without applying it.
Request:
Response:
Error Codes
Standard JSON-RPC Errors
| Code | Message | Description |
|---|---|---|
-32700 |
Parse error | Invalid JSON received |
-32600 |
Invalid Request | Invalid JSON-RPC request object |
-32601 |
Method not found | Unknown method name |
-32602 |
Invalid params | Invalid method parameters |
-32603 |
Internal error | Internal JSON-RPC error |
Nautilus Error Codes
| Range | Category | Example Codes |
|---|---|---|
1000-1999 |
Schema/Validation | 1000 Schema validation1001 Invalid model1002 Invalid field1003 Type mismatch |
2000-2999 |
Query Planning | 2000 Query planning2001 Invalid filter2002 Invalid orderBy2003 Unsupported operation |
3000-3999 |
Database Execution | 3000 Database execution3001 Connection failed3002 Constraint violation3003 Query timeout |
9000-9999 |
Internal Engine | 9000 Internal error9001 Unsupported protocol version9002 Invalid method9003 Invalid request params |
Error Response Example:
Versioning Policy
Backward Compatibility
- Minor changes (new optional fields, new methods): No version bump, clients ignore unknown fields
- Breaking changes (renamed/removed fields, changed semantics): Protocol version increment
Client Compatibility
Clients should:
- Send handshake with their protocol version
- Check engine's protocol version in response
- Abort if versions are incompatible
Migration Path
When protocol version 2 is released:
- Engine will support both v1 and v2 simultaneously (for a transition period)
- Clients send their version in every request
- Engine responds according to requested version
Transport
Line-Delimited JSON
Each request and response is a single JSON object on one line, terminated by \n:
{"jsonrpc":"2.0","id":1,"method":"engine.handshake","params":{"protocolVersion":1}}\n
{"jsonrpc":"2.0","id":1,"result":{"engineVersion":"0.1.0","protocolVersion":1}}\n
{"jsonrpc":"2.0","id":2,"method":"query.findMany","params":{"protocolVersion":1,"model":"User"}}\n
Multiplexing
The protocol supports concurrent in-flight requests:
- Each request has a unique
id - Responses may arrive out-of-order
- Clients match responses to requests by
id
Logging
The engine writes:
- stdout: JSON-RPC responses only (one per line)
- stderr: Debug logs, warnings, errors (not JSON)
Clients should:
- Read stdout for responses
- Optionally capture stderr for debugging
Usage Example
use *;
use json;
// Create a findMany request
let request = RpcRequest ;
// Serialize to JSON line
let line = format!;
// Write to engine stdin...
// Read response from engine stdout...
License
MIT or Apache-2.0