Documentation
Gaps, reconnections and replicas
Summary. Every hour's manifest states what the record holds and what it misses. It lists each reconnection, each break Nox detects in your venue's message sequence, and what each replica received. A declared gap does not fail verification: the hour still verifies, and its status is partial with the reasons.
The example with a declared gap
The synthetic example has a second pair of hours with a declared gap. In its second hour, both replicas reconnect, and one trade is in no frame of the hour. The record format allows two replicas, a and b, each with its own connections to the venue's public interfaces.
You need jq, zstd, b3sum and the DuckDB command-line client, and a POSIX shell.
curl -fsSO https://noxfze.com/docs/example-gap-hour.tar
tar -xf example-gap-hour.tarcd example-gap-hour/recorder=r1/venue=example/channel=trades
HOUR=date=2026-01-06/hour=00
PREV=date=2026-01-05/hour=231. Read the status
The status is complete, partial or missing, and the reasons say why an hour is not complete:
jq -c .completeness "$HOUR/manifest.json"{"reasons":["reconnect","sequence_gap"],"status":"partial"}The record format lists every reason. An hour in which Nox received no messages still has a manifest, so the chain never skips an hour.
2. Find the break in the sequence
Each entry names the instrument, the last identifier received before the break and the first one after it:
jq -c '.sequence_gaps[]' "$HOUR/manifest.json"{"after_id":"1003","before_id":"1005","instrument":"INST-1"}The raw file holds trades 1003, 1005 and 1006. Trade 1004 is in no frame of the hour, and no value stands in for it:
zstd -dc "$HOUR/raw.jsonl.zst" | jq -r '.payload | fromjson | .id'1003
1005
10063. See each reconnection
Each replica lists its reconnect windows. A window carries the venue's close code when the venue closed the connection with one:
jq -r '.replicas[] | .replica as $r | .reconnect_windows[] |
"\($r) \(.start_time) to \(.end_time) \(.close_code // "none")"' \
"$HOUR/manifest.json"a 2026-01-06T00:00:20.000000000Z to 2026-01-06T00:00:32.000000000Z 1001
b 2026-01-06T00:00:25.000000000Z to 2026-01-06T00:00:35.000000000Z noneFrom 00:00:25 to 00:00:32 neither replica was connected.
4. Where each replica saw no gap
Each replica states the runs in which one connection was subscribed and it detected no gap in the venue's trade sequence. A break lies between the end of one run and the start of the next:
jq -r '.replicas[] | .replica as $r | .gap_free_runs[] |
"\($r) \(.connection) \(.start_time) to \(.end_time)"' \
"$HOUR/manifest.json"a c1 2026-01-06T00:00:00.000000000Z to 2026-01-06T00:00:05.000000001Z
a c3 2026-01-06T00:00:40.000000000Z to 2026-01-06T01:00:00.000000000Z
b c2 2026-01-06T00:00:00.000000000Z to 2026-01-06T00:00:05.001000001Z
b c4 2026-01-06T00:00:40.001000000Z to 2026-01-06T01:00:00.000000000ZA run ends 1 ns after its connection's last frame before the break. The next run starts where the replica detected the break. A run states that the replica detected no gap, not that none occurred.
5. Compare the replicas
Frames received by both replicas, by one only, and trade identifiers the replicas carry with different values:
jq -c .replica_agreement "$HOUR/manifest.json"{"both":3,"conflict":0,"one_only":0}Each frame carries one receipt per replica that received it, with that replica's receive time:
zstd -dc "$HOUR/raw.jsonl.zst" |
jq -r '[.receipts[] | "\(.replica) \(.receive_time)"] | join(" ")'a 2026-01-06T00:00:05.000000000Z b 2026-01-06T00:00:05.001000000Z
a 2026-01-06T00:00:40.000000000Z b 2026-01-06T00:00:40.001000000Z
a 2026-01-06T00:00:50.000000000Z b 2026-01-06T00:00:50.001000000Z6. The hour still verifies
A declared gap is part of the sealed hour. The checks of command-line verification pass on it:
jq -r '.files[] | "\(.blake3) \(.name)"' "$HOUR/manifest.json" |
(cd "$HOUR" && b3sum --check)raw.jsonl.zst: OK
trades.parquet: OKtest "$(b3sum --no-names "$PREV/manifest.json")" = \
"$(jq -r .previous_manifest_blake3 "$HOUR/manifest.json")" &&
echo "chain: OK"chain: OK7. Every hour that is not complete
Over a run of hours, DuckDB reads every hourly manifest as one table. This lists each hour with its status, its reasons and its number of breaks:
duckdb -csv <<SQL
SELECT interval.start_time AS hour,
completeness.status AS status,
completeness.reasons AS reasons,
len(sequence_gaps) AS breaks
FROM read_json('date=*/hour=*/manifest.json')
ORDER BY hour;
SQLhour,status,reasons,breaks
2026-01-05T23:00:00.000000000Z,complete,[],0
2026-01-06T00:00:00.000000000Z,partial,"[reconnect, sequence_gap]",1Add WHERE completeness.status <> 'complete' to keep only the hours that are not complete. To read the trades of those hours, see reading a sealed hour.
Nox works in writing. Email contact@noxfze.com with your venue and the instruments to record.