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:
| Kind | Value | Direction |
|---|---|---|
CompactActionFrame | 1 | Client -> gateway |
CoreAck | 2 | Gateway -> client |
GatewayReject | 3 | Gateway -> client |
AuthSidecar | 4 | Client -> gateway |
CompactFrameGroup | 10 | Client -> gateway |
CoreAckBatch | 11 | Gateway -> client |
ClientHeartbeat | 12 | Client -> gateway |
ServerHeartbeat | 13 | Gateway -> client |
ResendRequest | 14 | Client -> gateway |
SessionResume | 15 | Client -> gateway |
SessionResumeResult | 16 | Gateway -> client |
SequencedData | 17 | Gateway -> 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 type | Status |
|---|---|
application/x-senticore-order-entry-batch | Preferred |
application/x-senticore-bsl-batch | BSL compatibility |
application/x-senticore-mm-batch | Legacy compatibility |
application/x-senticore-binary | Compatibility |
application/octet-stream | Compatibility |
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.