VeltrixDB Docs
v1.1.0 GitHub FAQ Quickstart

Wire Protocol

VeltrixDB speaks two protocols over the same TCP port: a human-readable text protocol for ad-hoc use with nc or telnet, and a compact binary protocol that every official SDK uses by default. The server auto-detects which one a connection is speaking from the first byte.

Text protocol

The text protocol is line-oriented — one command per line, a plain-text response per line — which makes it usable directly from nc, telnet, or any tool that can write to a TCP socket. It's the fastest way to sanity-check a running server.

CommandResponse
PUT key valueOK
GET keyvalue (or ERR if not found)
DEL keyOK
PINGPONG
INFOkeys=N writes=N reads=N ...
AUTH user passOK
QUITBYE

Text commands added in v1.1.0

v1.1.0 brings the text protocol to parity with operations that were previously binary-only (TTL writes, atomics, batch get) and adds the new list and set data types. The usage lines below are exactly what the server prints on a malformed invocation (ERR usage: ...):

CommandResponse
PUTEX <key> <ttl-seconds> <value>OK — a PUT with expiration (the text-protocol counterpart of the binary PUT's TTL field). TTL must be ≥ 1; ERR bad ttl otherwise.
INCR <key> [delta] / DECR <key> [delta]The new integer value, as a decimal line. delta defaults to 1; a missing key counts from zero.
SETNX <key> <value>1 (created) or 0 (key already exists).
CAS <key> <expected> <new>OK (swapped), MISMATCH (current value differed), or NOTFOUND (key absent).
MGET <key> [key ...]One <key> <value> line per found key (missing keys are silently omitted), terminated by END.
LPUSH <key> <value> / RPUSH <key> <value>The new list length.
LPOP <key> / RPOP <key>The popped value, or NIL when the list is empty.
LLEN <key>The element count.
LRANGE <key> <start> <stop>One element per line, then END. Redis semantics: stop inclusive, negative indices count from the end.
SADD <key> <member> / SREM <key> <member>1 when added/removed, 0 otherwise.
SISMEMBER <key> <member>1 or 0.
SMEMBERS <key>One member per line (unordered), then END.
SCARD <key>The member count.
Token-delimited arguments CAS parses <expected> as a single space-delimited token — use the binary protocol for values containing spaces. Semantics of every command (per-key TTL, atomic RMW guarantees, list/set encoding) are covered in the Data Model, which also documents the VSET/VSEARCH vector commands.

Worked example

Start a server and drive it with nc:

$ docker run -p 9000:9000 ghcr.io/veltrixdb/veltrixdb:latest

$ nc localhost 9000
PUT hello world
OK
GET hello
world
PUT counter 41
OK
GET counter
41
DEL hello
OK
GET hello
ERR
PING
PONG
INFO
keys=1 writes=3 reads=3 ...
QUIT
BYE

Or pipe a whole session in one shot, as shown in the project's own quick-start:

echo -e "PUT hello world\nGET hello\nPING" | nc localhost 9000

Binary protocol

The binary protocol is what every official SDK (Go, Python, Node.js, Java, Rust, C++) speaks by default, and it's what the server assumes once it sees a leading byte that matches a known opcode rather than ASCII command text — no handshake or negotiation is required, the server simply auto-detects the framing per connection.

Frame layout

Request:  [1B cmd][2B keyLen][4B valLen][key][value]
Response: [1B status][4B payloadLen][payload]

Opcodes

CategoryOpcodeCommand
Single-key0x01PUT
0x02GET
0x03DEL
0x04PING
0x05INFO
Batch0x06MPUT (vectorized multi-put)
0x07MGET (vectorized multi-get)
Atomic0x18CAS (compare-and-swap)
0x19INCR
0x1ADECR
0x1BSETNX

The atomic opcodes 0x180x1B use extended frames that carry the extra operands (expected value, delta, TTL) — as of v1.1.0 the same four operations are also available as text commands (CAS/INCR/DECR/SETNX, above) and in every official SDK:

CAS:   [1B 0x18][2B keyLen][4B expectedLen] + [4B newLen][4B ttl] + key + expected + new
INCR:  [1B 0x19][2B keyLen][4B 0]           + [8B delta int64] + [4B ttl] + key
DECR:  [1B 0x1A][2B keyLen][4B 0]           + [8B delta int64] + [4B ttl] + key
SETNX: [1B 0x1B][2B keyLen][4B valLen]      + [4B ttl] + key + value

Status codes

CodeStatusMeaning
0x00OKRequest succeeded; payload holds the result, if any.
0x01ERRRequest failed; payload holds an error message.
0x02NOT_FOUNDKey does not exist or is expired/tombstoned.
0x03EXISTSSETNX: the key already existed; nothing was written.
0x04MISMATCHCAS: the expected value did not match the current value; nothing was written.
0x05CONFLICTTXN: an optimistic version check failed — re-read and retry.
Prefer batches MPUT/MGET batches are roughly 50× faster than issuing the same number of individual PUT/GET calls, because a batch amortizes one round trip and one group-commit window across many keys instead of paying that cost per key. All official SDKs expose batch methods (for example, db.mput(...) / db.mget(...)) — reach for them whenever you're writing or reading more than a handful of keys at once.

Protocol auto-detection

Both protocols share the same TCP listener (-addr, default :9000). A single connection is either all-text or all-binary — the server determines this from the leading byte of the first request and does not switch mid-connection. You never need to configure which protocol to use; connecting with nc/telnet naturally speaks text, and every official SDK's connection pool speaks binary.