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

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.