Skip to main content

BSL Payload Framing

BSL Direct TCP uses client-managed TCP/TLS sessions and explicit message frames. BSL compact HTTP uses an HTTP POST body for compatibility. They share signed actions, risk, sequencing, matching, WAL, and reconciliation semantics.

Direct TCP frame​

After the 48-byte handshake, each client message starts with:

u32_le kind
u32_le payload_len
payload bytes

Current message kinds:

KindValueDirection
CompactActionFrame1Client -> gateway
CoreAck2Gateway -> client
GatewayReject3Gateway -> client
AuthSidecar4Client -> gateway
CompactFrameGroup10Client -> gateway
CoreAckBatch11Gateway -> client
ClientHeartbeat12Client -> gateway
ServerHeartbeat13Gateway -> client
ResendRequest14Client -> gateway
SessionResume15Client -> gateway
SessionResumeResult16Gateway -> client
SequencedData17Gateway -> client
EndOfSession18Gateway -> client

The single compact action frame is 192 bytes. Gateway responses use the same 8-byte frame header (u32_le kind, u32_le payload_len). The advertised v2 response envelope is SequencedData: the payload begins with the session sequence and inner response kind, then carries a CoreAck, GatewayReject, or CoreAckBatch body.

Direct TCP order-auth sequence​

Single-order BSL Direct TCP submissions must be framed in this order:

kind=4, len=N cold-encoded AuthSidecar
kind=1, len=192 CompactActionFrame

The gateway keeps one pending sidecar per session. The sidecar is consumed by the next CompactActionFrame; a second sidecar before a frame replaces the first. For groups, use:

kind=10, len=N cold-encoded CompactFrameGroup

Every group item must contain both the 192-byte frame and its matching sidecar. Loose AuthSidecar messages are not applied to group legs.

The cold AuthSidecar fields are:

auth_binding 32 bytes
signature secp256k1 signature bytes
signer_id 20-byte EVM signer address
payload_bytes cold-encoded SignedAction, mandatory on public BSL

auth_binding is derived from the exact signed action:

action_hash_v2 = actionSigningHashV2(chainId, verifyingContract, canonicalAction)
auth_binding = blake3(signature || nonce_le_u64 || action_hash_v2)

The compact frame must carry the same auth_binding, the same nonce, the same account identity, and the current compact mapping version. The sequencer re-decodes payload_bytes, recovers the signer, checks the authorized signer set, and verifies that the signed action and compact frame describe the same order/cancel/amend.

HTTP compatibility​

Accepted content types​

The server accepts:

Content typeStatus
application/x-senticore-order-entry-batchPreferred
application/x-senticore-bsl-batchBSL compatibility
application/x-senticore-mm-batchLegacy compatibility
application/x-senticore-binaryCompatibility
application/octet-streamCompatibility

Requests without a content type are treated as binary/byte submissions by the server-side decoder.

SDK encoding​

The TypeScript, Python, and Rust SDKs encode:

{"version":1,"actions":[...],"idempotencyKey":"client-batch-1"}

as UTF-8 bytes and submit it with one of the accepted binary content types. Do not send application/json on the compact binary routes. The server first tries the internal cold binary decoder, then falls back to this SDK JSON byte shape.

Body and action limits​

Read GET /api/v1/bsl/limits instead of hard-coding limits. The response reports max actions per batch, low-latency max actions, max binary body bytes, timestamp skew, rate windows, and backlog limits.

Existing clients can still use GET /api/v1/mm/limits; it is a compatibility route for the same BSL limit envelope.

Idempotency​

Use the HTTP Idempotency-Key header and the body idempotencyKey field. The server hashes the body and response mode. Retrying the same key with a different body is a client error.