on reject drops the frame, connection stays up
Validates the whole frame against DRISL at ingest — encoding only. It treats the commit's CAR as opaque bytes, so it does no signature, CID, MST, or record checks (that's its unbuilt M2).
The same malformed subscribeRepos frame, fed to each relay's real decoder and commit-verifier — Rust, Go, and Zig. One defect per frame, so every verdict is attributable — across #account events, real signed #commits carrying a CAR of blocks, and stateful #sync/sync-1.1 commit sequences.
Even the float64 “control” splits: zlay's DAG-CBOR decoder rejects all floats, so only the tag-42 control and the valid signed commit are accepted by all five.
on reject drops the frame, connection stays up
Validates the whole frame against DRISL at ingest — encoding only. It treats the commit's CAR as opaque bytes, so it does no signature, CID, MST, or record checks (that's its unbuilt M2).
on reject drops the whole PDS connection & reconnects
A frame CBOR-decode error unwinds the socket. Commit repo/MST/signature failures instead drop one event — and MST/record-presence checks are advisory (logged, not enforced).
on reject drops the event, connection stays up
Lenient ciborium header, stricter dag-cbor body. CAR CID integrity & envelope checks are enforced; but default lenient mode publishes signature failures, and record-presence is a TODO.
on reject CBOR error drops the connection; else drops the event
Always resolves the signing key, so signature verify and MST inversion run by default. Uniquely decodes record CBOR and enforces prevData correctness (inverts the MST and checks the root). Unknown types skip one frame, not the socket. verify_cids off by default.
on reject drops the frame, connection stays up
zat.cbor is a strict whole-frame DAG-CBOR decoder, so it matches atmoq on encoding — and goes further, rejecting all floats incl. float64. But its commit/sync path is fail-open: a bad signature, bad CID, or cache miss forwards the frame unvalidated and re-resolves the key in the background — it never drops a commit on crypto.
Signature- and CAR-free events: a reject can only mean the encoding or shape was rejected.
| Malformed case | atmoq | indigo | rsky | hydrant | zlay | |
|---|---|---|---|---|---|---|
| Frame framingthe two-object wire contract | ||||||
header only, no payload object
single-object |
REJECT | REJECT | REJECT | REJECT | REJECT | |
valid frame + one trailing byte
trailing-bytes |
REJECT | ACCEPT | REJECT | ACCEPT | REJECT | |
zero-length binary message
empty-message |
REJECT | REJECT | REJECT | REJECT | REJECT | |
| CBOR well-formednesscan the bytes be decoded at all? | ||||||
payload truncated mid-item
truncated-payload |
REJECT | REJECT | REJECT | REJECT | REJECT | |
reserved additional-info 28 in payload
reserved-ai-payload |
REJECT | REJECT | REJECT | REJECT | REJECT | |
bare break code (0xff) as payload
bare-break-payload |
REJECT | REJECT | REJECT | REJECT | REJECT | |
random non-CBOR bytes as payload
garbage-payload |
REJECT | REJECT | REJECT | REJECT | REJECT | |
| DRISL determinismvalid CBOR, non-canonical encoding | ||||||
payload map keys out of DRISL order
unordered-keys-payload |
REJECT | ACCEPT | ACCEPT | ACCEPT | REJECT | |
header keys out of DRISL order (op before t)
unordered-keys-header |
REJECT | ACCEPT | ACCEPT | ACCEPT | REJECT | |
duplicate map key in payload
duplicate-key-payload |
REJECT | ACCEPT | REJECT | REJECT | REJECT | |
non-minimal integer encoding in payload
nonminimal-int-payload |
REJECT | REJECT | ACCEPT | ACCEPT | REJECT | |
non-minimal string length in payload
nonminimal-len-payload |
REJECT | REJECT | ACCEPT | REJECT | REJECT | |
indefinite-length map in payload
indefinite-map-payload |
REJECT | REJECT | REJECT | REJECT | REJECT | |
indefinite-length text string in payload
indefinite-str-payload |
REJECT | REJECT | ACCEPT | REJECT | REJECT | |
| Floats & simple valuesfloat width, NaN, undefined | ||||||
float16 value in a (forward-compat) field
float16-payload † in unknown field |
REJECT | ACCEPT | ACCEPT | ACCEPT | REJECT | |
float32 value in a field
float32-payload † in unknown field |
REJECT | ACCEPT | ACCEPT | ACCEPT | REJECT | |
float64 value in a field (CONTROL: valid DRISL)
float64-ok-payload control · must accept |
ACCEPT | ACCEPT | ACCEPT | ACCEPT | REJECT | |
float64 NaN in a field
nan-payload † in unknown field |
REJECT | ACCEPT | ACCEPT | ACCEPT | REJECT | |
float64 +Infinity in a field
infinity-payload † in unknown field |
REJECT | ACCEPT | ACCEPT | ACCEPT | REJECT | |
CBOR undefined (0xf7) as a field value
undefined-payload † in unknown field |
REJECT | ACCEPT | ACCEPT | ACCEPT | REJECT | |
disallowed simple value simple(19) (0xf3)
simple-value-payload † in unknown field |
REJECT | ACCEPT | REJECT | REJECT | REJECT | |
| Tags & keystag 42 only, text keys, UTF-8 | ||||||
CBOR tag 0 (datetime) in a field
tag-0-payload † in unknown field |
REJECT | ACCEPT | ACCEPT | ACCEPT | REJECT | |
CBOR tag 2 (bignum) in a field
tag-2-bignum-payload † in unknown field |
REJECT | ACCEPT | ACCEPT | ACCEPT | REJECT | |
CBOR tag 42 CID in a field (CONTROL: valid DRISL)
tag-42-ok-payload control · must accept |
ACCEPT | ACCEPT | ACCEPT | ACCEPT | ACCEPT | |
tag 42 whose byte string lacks the 0x00 prefix
tag-42-no-prefix-payload † in unknown field |
REJECT | REJECT | ACCEPT | ACCEPT | REJECT | |
integer map key in payload
int-map-key-payload |
REJECT | REJECT | REJECT | REJECT | REJECT | |
invalid UTF-8 in the did text string
invalid-utf8-payload |
REJECT | ACCEPT | REJECT | REJECT | REJECT | |
| at-sync semanticsvalid encoding, event shape | ||||||
unknown message type #futurething
unknown-type forward-compat |
ACCEPT | ACCEPT | REJECT | ACCEPT | ACCEPT | |
known type with an unknown extra field
unknown-field forward-compat |
ACCEPT | ACCEPT | ACCEPT | ACCEPT | ACCEPT | |
#account payload missing required seq
missing-seq |
ACCEPT | ACCEPT | REJECT | REJECT | ACCEPT | |
seq encoded as a text string
wrong-type-seq |
ACCEPT | REJECT | REJECT | REJECT | ACCEPT | |
op:1 header with no t
op-1-no-t |
REJECT | ACCEPT | REJECT | REJECT | REJECT | |
Real signed commits carrying a CAR of blocks — the validation that matters happens below frame-decode.
| Malformed case | atmoq | indigo | rsky | hydrant | zlay | |
|---|---|---|---|---|---|---|
| Valid baselinecorrect CAR, CIDs, signature | ||||||
valid signed #commit (CONTROL)
valid control · must accept |
ACCEPT | ACCEPT | ACCEPT | ACCEPT | ACCEPT | |
| Envelope encodingDRISL on the outer payload map | ||||||
#commit envelope map keys out of DRISL order
envelope-unordered |
REJECT | ACCEPT | ACCEPT | ACCEPT | REJECT | |
#commit envelope carries a float16 field
envelope-float16 |
REJECT | ACCEPT | ACCEPT | ACCEPT | REJECT | |
| Record contents$type & record shape — inside the CAR | ||||||
create op whose record has no $type
record-no-type reported case |
ACCEPT | ACCEPT | ACCEPT | ACCEPT | ACCEPT | |
create op whose record has an unknown $type / NSID
record-unknown-type forward-compat |
ACCEPT | ACCEPT | ACCEPT | ACCEPT | ACCEPT | |
create op whose record is a CBOR array, not a map
record-not-map |
ACCEPT | ACCEPT | ACCEPT | ACCEPT | ACCEPT | |
| Repo & crypto integrityCAR CIDs, blocks, signature, flags | ||||||
#commit with tooBig=true
too-big |
ACCEPT | REJECT | REJECT | ACCEPT | ACCEPT | |
CAR block whose bytes no longer hash to its CID
cid-mismatch |
ACCEPT | REJECT | REJECT | REJECT | ACCEPT | |
create op references a record CID absent from the CAR
missing-block |
ACCEPT | ACCEPT | ACCEPT | REJECT | ACCEPT | |
#commit signed by the wrong key
bad-signature |
ACCEPT | REJECT | ACCEPT | REJECT | ACCEPT | |
The at-synchronization rules: the #sync event, and the prevData / rev-ordering checks that only fire on a second commit once the relay holds prior repo state.
| Malformed case | atmoq | indigo | rsky | hydrant | zlay | |
|---|---|---|---|---|---|---|
| #sync eventsync-1.1's compact repo-state message | ||||||
valid #sync event (CONTROL)
sync-event-valid control · must accept |
ACCEPT | ACCEPT | ACCEPT | ACCEPT | ACCEPT | |
#sync event signed by the wrong key
sync-event-bad-sig |
ACCEPT | REJECT | ACCEPT | REJECT | ACCEPT | |
| Stateful commit checksprevData, rev order, rebase — need prior state | ||||||
valid second commit with correct prevData (CONTROL)
commit2-valid control · must accept |
ACCEPT | ACCEPT | ACCEPT | ACCEPT | ACCEPT | |
second commit omits prevData
commit2-missing-prevdata |
ACCEPT | REJECT | ACCEPT | ACCEPT | ACCEPT | |
second commit with a mismatched prevData CID
commit2-wrong-prevdata |
ACCEPT | ACCEPT | ACCEPT | REJECT | ACCEPT | |
second commit whose rev is not greater than the last seen
commit2-rev-rollback |
ACCEPT | REJECT | ACCEPT | SKIP | SKIP | |
#commit with the deprecated rebase flag set
commit-rebase-flag |
ACCEPT | ACCEPT | REJECT | ACCEPT | ACCEPT | |
Give five relays a commit signed by the wrong key: hydrant and indigo drop it; zlay forwards it unvalidated (and re-resolves the key in the background), rsky publishes it with a warning, atmoq has no crypto. zlay is fail-open by design — it never drops a commit on bad signature, CID, or MST. hydrant is its mirror image. The spec never says which is conformant.
A commit whose prevData is present but wrong (the MST inversion fails) is dropped only by hydrant — it always holds the signing key, so it always inverts the MST. The other four accept it. The property that lets a consumer verify an op without fetching the repo is honored by one relay of five. MUST, or hint?
The float64 case is a control — valid DRISL, meant to be accepted everywhere. It is, by four relays. zlay's zat DAG-CBOR decoder rejects all floats, float64 included — atproto records carry no floats, so it forbids them outright. Two whole-frame validators (atmoq, zlay) enforce DRISL; they still disagree on whether float64 is even legal.
A #commit whose record omits $type — or is a list, not a map — passes all five. Only hydrant decodes record bodies at all (as generic CBOR), so it alone would reject a malformed-CBOR record; none require a lexicon $type. Record semantics are left to PDSes and AppViews.
A bad frame costs atmoq/rsky/zlay one event; it costs indigo and hydrant the whole connection to that PDS until reconnect — a mild DoS foothold. And rsky is still the one relay whose parser errors on an unknown message type (#futurething); the other four tolerate it. Specify the consequence, not only reject-or-not.
Two relays validate the whole frame's encoding (atmoq, zlay); three decode into structs and never re-check it. rsky's default publishes bad sigs and rev rollbacks; hydrant enforces them; zlay forwards them. indigo drops deprecated tooBig/rebase flags that others ignore. Five codebases, five postures — a spec's MUST list should say which is conformant, and match what ships enabled by default.