x86-64 assembly decoding the Bluesky firehose
CBOR → CAR → DAG-CBOR → post text. No runtime. No abstractions. Just registers and the protocol.
Part of the Fortransky ecosystem — an AT Protocol client written in Fortran.
The AT Protocol relay (com.atproto.sync.subscribeRepos) speaks binary
WebSocket frames. Each frame contains three nested binary formats before you
reach a post:
Raw WebSocket frame
└─ CBOR envelope {op, t, seq, repo, rev, ops, blocks}
└─ CARv1 block store
└─ DAG-CBOR record {$type, text, createdAt, ...}
Assemblersky decodes all of it in x86-64 NASM assembly — four functions, four stages, no allocator, no garbage collector, no language runtime:
asb_decode_envelope() CBOR envelope → seq, repo, rev, ops slice, blocks
asb_find_create_post_op() ops array → collection, rkey, CID
asb_car_find_block() CARv1 → record block bytes
asb_extract_post_record() DAG-CBOR → $type, text, createdAt
A thin Rust harness wraps the assembly via a C ABI and emits normalized NDJSON — the same schema as Fortransky's Rust firehose bridge, so both decoders are interchangeable.
assemblersky-harness (Rust CLI)
└─ ffi.rs Rust FFI bindings → C ABI
└─ assemblersky_exports.c C glue
└─ cbor_scan.asm asb_decode_envelope
└─ car_scan.asm asb_car_find_block
└─ post_extract.asm asb_find_create_post_op + asb_extract_post_record
└─ util.asm asb_mem_eq
The assembly is strict about the x86-64 SysV calling convention. Getting this wrong is the main class of bug in assembly code that calls other assembly:
- Callee-save registers (
rbx,r12–r15,rbp) — pushed on entry, popped on exit for every function - Caller-save registers (
rsi,rdi,rcx,rdx,r8,r9) — reloaded from callee-save registers before every call that needs them; cannot be assumed to survive across acall rsi(end pointer) — must be reloaded before every helper call since nearly all helpers use it as a bounds limit- Output pointer arguments (
r8,r9)** — saved to the stack on entry since_decode_varintand similar helpers clobber both
The bring-up debugging session that fixed these is documented in the git history.
- NASM (
sudo apt install nasm/sudo pacman -S nasm) - Rust toolchain >= 1.78 (
rustupor distro package) - C compiler (
gccorclang, via thecccrate)
makeOr manually:
cd rust-harness
ASB_ROOT=$(pwd)/.. cargo build --releasemake testExpected output:
{
"kind": "commit",
"op": "create",
"collection": "app.bsky.feed.post",
"seq": 26653242501,
"repo": "did:plc:fortranskyfixture000000000000",
"rev": "3lmfixture-rev",
"rkey": "3lmfixturepost",
"record": {
"$type": "app.bsky.feed.post",
"text": "Synthetic raw relay commit fixture: hello from Fortransky.",
"created_at": "2026-03-19T00:00:00.000Z"
}
}mkdir -p ../fortransky/bridge/assemblersky/bin
cp rust-harness/target/release/assemblersky-harness \
../fortransky/bridge/assemblersky/bin/assemblersky_cliFortransky's relay_raw_tail.py auto-detects assemblersky_cli and prefers
it over the Rust firehose bridge. Detection order:
FORTRANSKY_RELAY_DECODERenv varFORTRANSKY_ASSEMBLERSKY_DECODERenv varbridge/assemblersky/bin/assemblersky_cli(bundled)assemblersky_clionPATH- Rust
firehose_bridge_clifallback
Check detection: ./scripts/check_assemblersky.sh
#commitframes onlycreateops onlyapp.bsky.feed.postcollection only- Definite-length CBOR subset (indefinite-length items fail gracefully)
- Linux x86-64 only (SysV ABI)
The CID in commit ops is captured as raw bytes — no semantic CID decode. The CAR block scanner uses a heuristic varint walk to determine CID length.
asm/
cbor_scan.asm asb_decode_envelope — CBOR envelope + body map parser
car_scan.asm asb_car_find_block — CARv1 varint scanner + CID match
post_extract.asm asb_find_create_post_op + asb_extract_post_record
util.asm asb_mem_eq — bounded memory compare
include/
assemblersky.h C ABI header — struct definitions + function prototypes
cshim/
assemblersky_exports.c C-visible glue (no logic)
rust-harness/
src/main.rs CLI entry point — calls all 4 stages, emits NDJSON
src/ffi.rs Rust FFI bindings
src/normalize.rs NormalizedEvent + NormalizedRecord output structs
build.rs Compiles NASM + C shim via cc crate
Cargo.toml
tests/
fixtures/relay_commit_frame.bin Synthetic raw #commit relay frame
expected/relay_commit_frame.json Expected normalized output
Fortransky (Fortran TUI Bluesky client)
└─ relay_raw_tail.py
└─ assemblersky-harness ← this project
└─ x86-64 NASM assembly
Fortransky: https://github.com/FormerLab/fortransky Former Lab: https://bsky.app/profile/formerlab.bsky.social
