Skip to content

API Server Architecture

Use this page when you need to understand how the API server maps network requests to AkkEngine. For startup and client examples, see API Server Usage.

The server code lives under akkara/akkserver/ and is licensed separately under AGPLv3. Check that boundary before packaging or redistributing server-enabled builds.

At engine startup, AkkEngine::open() checks components.apiEnabled. If enabled, it loads or uses the API server backend, creates an aggregate server for the configured transports, and starts it after the storage engine components are ready.

The server lifetime follows the engine lifetime. It starts during AkkEngine::open() and shuts down when close() runs or the engine is destroyed.

BackendRole
HTTPHuman-friendly smoke tests, scripts, and simple service boundaries.
TCPCompact AK5 binary protocol for high-throughput clients.
gRPCTyped RPC surface when Protobuf/gRPC support is available.

Each transport maps requests to the same underlying AkkEngine operations. The API server does not add schema awareness: keys and values remain raw bytes.

HTTP response bodies use little-endian binary encoding unless the endpoint explicitly returns text.

EndpointBody
/v1/exists[exists:u8], where 1 means present and 0 means absent.
/v1/count[count:u64le].
/v1/scan[count:u32le][truncated:u8]{row}*.
/v1/history[count:u32le][truncated:u8]{entry}*.
/v1/batchGet[count:u32le]{[status:u8][value_len:u32le][value bytes]}*.
/v1/statsCompact little-endian EngineStats snapshot.

Scan rows use:

[key_len:u16le][value_len:u32le][key bytes][value bytes]

History entries use:

[seq:u64le][source_node_id:u64le][timestamp_ns:u64le][flags:u32le][value_len:u32le][value bytes]

truncated is 1 when the server stopped because the configured or requested limit was reached. Increase limit or perform a follow-up range request when you need more rows.

Streaming bodies begin with a 5-byte prelude:

StreamPrelude
scanAKKS\x01
historyAKKH\x01

After the prelude, the de-chunked body is a sequence of frames:

[frame_type:u8][payload_len:u32le][payload bytes]

Frame type 1 is an item. For scan, the item payload is the same row shape used by non-streaming scan. For history, the item payload is the same entry shape used by non-streaming history, without the outer count and truncated fields.

Frame type 2 terminates the stream:

[emitted_count:u32le][truncated:u8]

HTTP streaming changes only how the server sends the result. It does not change the engine-side snapshot semantics of the operation.

The TCP transport uses AK5 binary frames. Request frames start with a 16-byte header:

char[4] magic = "AK5Q"
u8 version = 2
u8 opcode
u32 request_id
u16 key_len
u32 val_len

Response frames start with a 13-byte header:

char[4] magic = "AK5S"
u8 status
u32 request_id
u32 val_len

Each TCP request is:

[request header][key bytes][value bytes][crc32c:u32le]

The request CRC32C covers key bytes + value bytes. Each response is:

[response header][value bytes][crc32c:u32le]

The response CRC32C covers only value bytes.

OpcodeOperation
0x01GET
0x02PUT
0x03REMOVE
0x04GET_AT
0x05BATCH_PUT
0x06BATCH_GET
0x07PING
0x08EXISTS
0x09COUNT
0x0ASCAN
0x0BHISTORY
0x0CROLLBACK_TO
0x0DROLLBACK_KEY
0x0EFORCE_SYNC
0x0FFORCE_FLUSH
0x10STATS
0x11SCAN_STREAM
0x12HISTORY_STREAM

Status values are:

StatusMeaning
0x00OK
0x01Not found
0xFFError

request_id is echoed in the response. Use it to correlate responses when a client pipelines multiple requests on one connection.

OperationRequest keyRequest value
GET, PUT, REMOVE, EXISTS, HISTORYtarget keyoperation payload or empty
GET_AT, ROLLBACK_KEYtarget key[seq:u64le]
ROLLBACK_TOempty[seq:u64le]
COUNTstartKeyendKey
SCANstartKey[limit:u32le][endKey bytes], where limit = 0 means unbounded
BATCH_PUT, BATCH_GETemptybatch payload
FORCE_SYNC, FORCE_FLUSH, STATS, PINGemptyempty

TCP scan responses use [count:u32le][truncated:u8]{row}*. TCP history responses use [count:u32le]{entry}*; unlike HTTP history, the non-streaming TCP history payload does not include a truncated byte.

SCAN_STREAM and HISTORY_STREAM send multiple AK5S responses with the same request_id. Each response value is one stream frame:

[frame_type:u8][payload_len:u32le][payload bytes]

Frame type 1 is an item. Frame type 2 is terminal and carries [emitted_count:u32le][truncated:u8].

The gRPC transport exposes unary calls for the same engine operations, plus server-streaming calls for scans and history. The service name is akkaradb.grpcapi.v1.AkkaraDB.

Unary calls cover Ping, Put, Get, Remove, Exists, Count, Scan, GetAt, History, RollbackTo, RollbackKey, BatchPut, BatchGet, ForceSync, ForceFlush, and Stats. Streaming calls cover ScanStream and HistoryStream.

Availability is build-dependent. When Protobuf/gRPC support is not present, the gRPC backend is not available as a real server transport. gRPC maps api.grpcPort, api.grpcWorkerThreads, api.grpcCompletionQueues, api.grpcMinPollers, api.grpcMaxPollers, api.grpcMaxConcurrentStreams, api.grpcResourceQuotaBytes, api.grpcMaxBatchItems, api.grpcMaxScanItems, and api.grpcMaxHistoryEntries.

When api.transportMode == TLS, gRPC uses api.tls.certPath, api.tls.keyPath, and api.tls.caPath. Client certificates are required when api.tls.verifyPeer is true and a CA path is configured.

OptionApplies toNotes
api.httpMaxBatchItemsHTTP batch endpointsRejects oversized batch bodies.
api.httpMaxScanItemsHTTP scanCaps rows returned or streamed per request.
api.httpMaxHistoryEntriesHTTP historyCaps entries returned or streamed per request.
api.httpMaxContentLengthHTTP request bodyProtects memory use for POST bodies.
api.tcpWorkerThreadsTCP0 uses automatic behavior.
api.tcpAcceptQueueLimitTCPCaps accepted sockets waiting for a worker.
api.tcpAcceptQueueTimeoutMsTCPDrops queued sockets that wait too long; 0 disables timeout.
api.tcpPipelineBatchLimitTCPControls how many pipelined requests are processed in a batch.
api.tcpMaxBatchItemsTCP batch endpointsRejects oversized batch payloads.
api.tcpMaxPendingResponseBytesTCP/gRPCBackpressure limit; also feeds gRPC send/receive message size.
api.tcpReadTimeoutMsTCPIdle or partial-frame read timeout; 0 disables timeout.
api.tcpWriteTimeoutMsTCPResponse write timeout; 0 disables timeout.

Start with defaults for local development. Tune limits only after measuring request sizes, concurrency, and response backpressure.