···120120- **error handling**: all SDKs use infallible decode functions that never abort on failure — errors are counted and the frame is skipped
121121- **capture coupling**: the corpus capture tool uses zat's CBOR decoder for the commit-with-ops header peek. this is standard CBOR parsing (not zat's typed firehose decoder), but it does mean frames that zat's CBOR decoder rejects won't appear in the corpus
122122123123+## signature verification
124124+125125+a separate benchmark measures the full signature verification pipeline that relays perform on every incoming commit. this is CPU-bound ECDSA work that compounds with scale (~500-1000 verifies/sec on the live network, much higher during backfill).
126126+127127+### what it measures
128128+129129+per entry, both SDKs do identical work:
130130+1. CBOR decode the signed commit (has `sig` field)
131131+2. strip the `sig` field, re-encode as unsigned CBOR (deterministic DAG-CBOR)
132132+3. SHA-256 hash the unsigned bytes
133133+4. ECDSA verify the hash against the signature using the account's public key
134134+5. dispatch by curve type: P-256 or secp256k1
135135+136136+both enforce low-S normalization on both curves.
137137+138138+### two tiers
139139+140140+- **full pipeline**: CBOR decode → strip sig → re-encode → SHA-256 → ECDSA verify (what a relay actually does)
141141+- **crypto-only**: SHA-256 → ECDSA verify with pre-computed unsigned bytes (isolates crypto cost from CBOR overhead)
142142+143143+### results
144144+145145+_3,072 signed commits (all secp256k1), 5 measured passes, macOS arm64 (M3 Max)_
146146+147147+| SDK | variant | verifies/sec (median) | entries | P-256 | secp256k1 | errors |
148148+|-----|---------|--------:|-----:|-----:|-----:|-----:|
149149+| go ([indigo](https://github.com/bluesky-social/indigo)) | full pipeline | 15,109 | 3,072 | 0 | 3,072 | 0 |
150150+| go (indigo) | crypto-only | 15,012 | 3,072 | 0 | 3,072 | 0 |
151151+| zig ([zat](https://tangled.sh/@zzstoatzz.io/zat) + [k256](https://tangled.sh/@zzstoatzz.io/k256)) | full pipeline | 9,796 | 3,072 | 0 | 3,072 | 0 |
152152+| zig (zat + k256) | crypto-only | 9,716 | 3,072 | 0 | 3,072 | 0 |
153153+154154+Go leads sig verification by ~1.5x. indigo uses [decred/dcrd](https://github.com/decred/dcrd/tree/master/dcrec/secp256k1) — a highly optimized secp256k1 implementation with specialized 10×26-bit field arithmetic. zig uses [k256](https://tangled.sh/@zzstoatzz.io/k256) with GLV endomorphism, precomputed base point tables, and Jacobian point arithmetic, on top of zig stdlib's fiat-crypto field operations.
155155+156156+the crypto-only vs full-pipeline numbers being nearly identical confirms ECDSA is the bottleneck, not CBOR re-encoding overhead.
157157+158158+### why only zig + go
159159+160160+only zat and indigo have production-grade signature verification built in. the raw/jacquard/python implementations don't include commit signing — adding it would mean hand-rolling crypto, which isn't what those SDKs represent.
161161+162162+### sig-verify corpus format
163163+164164+```
165165+[u32 BE entry_count]
166166+per entry:
167167+ [u8 curve_type] // 0 = P-256, 1 = secp256k1
168168+ [u16 BE signed_len][signed_bytes...] // signed commit CBOR (with sig field)
169169+ [u16 BE pubkey_len][pubkey_bytes...] // compressed public key (33 bytes)
170170+```
171171+172172+captured by connecting to the firehose, extracting signed commit blocks from CAR data, and resolving each DID via PLC directory to get the signing key. entries that fail verification are dropped during capture.
173173+123174## corpus format
124175125176the fixture file (`fixtures/firehose-frames.bin`) uses a simple length-prefixed binary format:
···156207## usage
157208158209```sh
159159-just capture # capture ~10s of firehose traffic
160160-just bench # run all benchmarks
161161-just bench-zig # run a single language
210210+# decode benchmarks
211211+just capture # capture ~10s of firehose traffic
212212+just bench # run all decode benchmarks
213213+just bench-zig # run a single language
214214+215215+# sig verify benchmarks
216216+just capture-sigs # capture signed commits + resolve public keys (~10s + DID resolution)
217217+just bench-sigs # run all sig verify benchmarks (zig + go)
218218+just bench-sigs-zig
219219+just bench-sigs-go
162220```
163221164222## methodology