commits
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
exposes the _closed atomic flag so callers can check connection
liveness before attempting writes. used by zlay's pingLoop to
prevent use-after-free on connection teardown.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
websocket.zig change: plain HTTP requests on the websocket port are now
dispatched to Handler.httpFallback() instead of getting a 400 response.
this restores health probe / XRPC support on port 3000 that was lost
in the 0.16 fork migration.
also removes std.debug.print calls from test code — these corrupted
the zig 0.16 --listen=- IPC protocol between the build runner and
test binary, causing spurious "failed command" output.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
upstream fix: adds Io.Mutex to websocket client writeFrame() to
serialize concurrent writes (ping loop, auto-pong, close). prevents
GPF from interleaved frame headers/payloads on shared stream.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MstError includes WriteFailed (from Io.Writer.Allocating.drain when
allocation fails). map it to OutOfMemory since that's the underlying
cause.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- add `io: std.Io` field to JetstreamClient and FirehoseClient
- subscribe() returns Io.Cancelable!void (enables async cancellation)
- replace libc.nanosleep() with io.sleep() for reconnect backoff
- pass caller-provided io to websocket.Client.init() instead of debug_io
- fix bug: firehose connectAndRead() was missing required `io` param
- bump version to 0.3.0-alpha.6
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- add scripts/jetstream_smoke.zig with `zig build smoke` step
- fix publish-docs.zig: posix.getenv → std.c.getenv, std.time.timestamp
→ Io.Timestamp, std.fs.cwd → Io.Dir, XrpcClient.init signature
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes websocket server hanging on Linux — O_NONBLOCK was hardcoded
as 0x0004 (macOS) instead of using libc.O packed struct.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
net.Stream.handle moved to net.Stream.socket.handle in zig 0.16.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
websocket.zig 0.16 requires io as first parameter to Client.init.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- minimum_zig_version bumped to 0.16.0, version 0.3.0-alpha.1
- link_libc on module (required for libc socket/time ops)
- std.crypto.random → io.random(), std.time.timestamp → gettimeofday
- posix.nanosleep/setsockopt → std.c equivalents
- HttpTransport/XrpcClient/DidResolver/HandleResolver thread io param
- websocket dep updated to Io-migrated fork (b6eb671)
- all 203 tests pass
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keypair gains uncompressedPublicKey(), jwk(), jwkThumbprint() for both
P-256 and secp256k1. New oauth module provides stateless PKCE, DPoP,
client assertion, and form encoding helpers extracted from pollz.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
wire up 3 new fixture sets from atproto-interop-tests:
- did:key derivation K256 (5 vectors) and P256 (1 vector)
- data model JSON↔DAG-CBOR round-trip (3 vectors)
also fix keypair zero-scalar validation that surfaced when
the new import path pulled keypair tests into a second compilation unit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
exposes which host the client connected to, enabling callers to track
the current relay. follows the same comptime @hasDecl pattern as onError.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
completes the crypto module's did:key lifecycle — formatDidKey already
existed but the inverse (parsing a did:key string back to key type +
raw bytes) was missing. adds a unified Keypair struct for sign/verify
workflows and a convenience verifyDidKeySignature that dispatches by
curve type.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
corrections to 006, ReleaseSafe stack incident, sync 1.1
verification plumbing, lightrail collection index design.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: enable TCP keepalive on websocket connections — detect dead peers
in ~20s instead of blocking forever when remote disappears without
FIN/RST.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update websocket.zig fork with HTTP fallback support for serving
both WebSocket and HTTP on a single port.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
devlog 006: building a relay in zig — zlay architecture, deployment
war stories (musl/glibc, TCP splits, RocksDB iterator lifetimes,
pg.zig type strictness), collection index backfill, and operational
numbers.
also: SPA deep link fixes for standard.site, missing devlog entries
in publish-docs.zig, CI glibc fix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ATProto paths use /devlog/001 but files are 001-self-publishing-docs.md.
copy short-name versions (001.md) during build so SPA deep links resolve.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
relative ./style.css etc. resolve against the current pathname, so
/devlog/002 requests /devlog/style.css (404). <base href="/"> anchors
all relative URLs to the site root.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
add _redirects with SPA rewrite so Wisp serves index.html for all
paths. app.js converts pathname to hash route on load, making URLs
like /roadmap and /devlog/001 work when linked from search indexes.
also add glibc to nixery deps so patchelf can actually find the
dynamic linker for wisp-cli.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
wisp-cli is dynamically linked against glibc, but nixery containers
don't have /lib64/ld-linux-x86-64.so.2. Use patchelf to rewrite the
interpreter and rpath to the nix store glibc paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch to zzstoatzz/websocket.zig fix/handshake-tcp-split which
prevents panics when TCP splits data between \r and \n during
WebSocket handshake parsing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
getInt returns ?i64 which truncates values > i64 max. upstream AT Protocol
firehose seq numbers now exceed i64 max, so callers need direct u64 access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ChildRef union (none/node/stub) for partial trees loaded from CAR blocks.
putReturn/deleteReturn return displaced CIDs, copy() for deep clone,
loadFromBlocks() deserializes commit CARs into partial MSTs.
Operation/normalizeOps/invertOp implement the inductive firehose:
undoing ops against the post-commit MST root must recover prevData CID.
verifyCommitDiff() is the top-level pipeline: parse CAR, verify signature,
load partial MST, copy, normalize, invert, compare root to prevData.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
deprecated in zig 0.15 — migrate all 11 callsites across cbor tests,
firehose client, and jetstream client to the new API.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
decodeMstNode() parses known MST CBOR schema directly — zero-copy byte
slicing, no Value unions. walkAndVerifyMst checks key heights during
traversal instead of full rebuild. MST step: 218ms → 39ms (5.5x),
compute total: 300ms → 123ms (2.4x) on pfrazee.com (192k records).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CAR parser now builds a StringHashMap index during read(), findBlock()
uses O(1) hash lookup instead of linear scan. verifyRepo bypasses
default 2MB/10k block limits for fetched repos.
Clarify Rust ecosystem in devlog: rsky (BlackSky), jacquard
(@nonbinary.computer), and hand-rolled RustCrypto bench.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
relative path doesn't resolve on zat.dev (serves from ATProto records,
not git). use tangled.org raw URL so the SVG renders on both zat.dev
and tangled.org.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
README was missing everything since 0.1.3 — added CBOR, CAR, MST,
firehose, jetstream, signing, repo verification sections. embedded
verify-compute.svg in devlog 005. updated changelog and roadmap.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CAR parser now accepts max_size and max_blocks options for large repo
verification. export jwt module for verify tool. devlog 005 covers the
three-way trust chain comparison (zig vs go vs rust).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
k256 5×52-bit field, Fermat scalar inversion, three-way benchmark
with blacksky's rsky stack. changelog backfill for 0.2.1 and 0.2.2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
exposes the _closed atomic flag so callers can check connection
liveness before attempting writes. used by zlay's pingLoop to
prevent use-after-free on connection teardown.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
websocket.zig change: plain HTTP requests on the websocket port are now
dispatched to Handler.httpFallback() instead of getting a 400 response.
this restores health probe / XRPC support on port 3000 that was lost
in the 0.16 fork migration.
also removes std.debug.print calls from test code — these corrupted
the zig 0.16 --listen=- IPC protocol between the build runner and
test binary, causing spurious "failed command" output.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
upstream fix: adds Io.Mutex to websocket client writeFrame() to
serialize concurrent writes (ping loop, auto-pong, close). prevents
GPF from interleaved frame headers/payloads on shared stream.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- add `io: std.Io` field to JetstreamClient and FirehoseClient
- subscribe() returns Io.Cancelable!void (enables async cancellation)
- replace libc.nanosleep() with io.sleep() for reconnect backoff
- pass caller-provided io to websocket.Client.init() instead of debug_io
- fix bug: firehose connectAndRead() was missing required `io` param
- bump version to 0.3.0-alpha.6
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
websocket.zig 0.16 requires io as first parameter to Client.init.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- minimum_zig_version bumped to 0.16.0, version 0.3.0-alpha.1
- link_libc on module (required for libc socket/time ops)
- std.crypto.random → io.random(), std.time.timestamp → gettimeofday
- posix.nanosleep/setsockopt → std.c equivalents
- HttpTransport/XrpcClient/DidResolver/HandleResolver thread io param
- websocket dep updated to Io-migrated fork (b6eb671)
- all 203 tests pass
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
wire up 3 new fixture sets from atproto-interop-tests:
- did:key derivation K256 (5 vectors) and P256 (1 vector)
- data model JSON↔DAG-CBOR round-trip (3 vectors)
also fix keypair zero-scalar validation that surfaced when
the new import path pulled keypair tests into a second compilation unit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
completes the crypto module's did:key lifecycle — formatDidKey already
existed but the inverse (parsing a did:key string back to key type +
raw bytes) was missing. adds a unified Keypair struct for sign/verify
workflows and a convenience verifyDidKeySignature that dispatches by
curve type.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: enable TCP keepalive on websocket connections — detect dead peers
in ~20s instead of blocking forever when remote disappears without
FIN/RST.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update websocket.zig fork with HTTP fallback support for serving
both WebSocket and HTTP on a single port.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
devlog 006: building a relay in zig — zlay architecture, deployment
war stories (musl/glibc, TCP splits, RocksDB iterator lifetimes,
pg.zig type strictness), collection index backfill, and operational
numbers.
also: SPA deep link fixes for standard.site, missing devlog entries
in publish-docs.zig, CI glibc fix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
add _redirects with SPA rewrite so Wisp serves index.html for all
paths. app.js converts pathname to hash route on load, making URLs
like /roadmap and /devlog/001 work when linked from search indexes.
also add glibc to nixery deps so patchelf can actually find the
dynamic linker for wisp-cli.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ChildRef union (none/node/stub) for partial trees loaded from CAR blocks.
putReturn/deleteReturn return displaced CIDs, copy() for deep clone,
loadFromBlocks() deserializes commit CARs into partial MSTs.
Operation/normalizeOps/invertOp implement the inductive firehose:
undoing ops against the post-commit MST root must recover prevData CID.
verifyCommitDiff() is the top-level pipeline: parse CAR, verify signature,
load partial MST, copy, normalize, invert, compare root to prevData.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
decodeMstNode() parses known MST CBOR schema directly — zero-copy byte
slicing, no Value unions. walkAndVerifyMst checks key heights during
traversal instead of full rebuild. MST step: 218ms → 39ms (5.5x),
compute total: 300ms → 123ms (2.4x) on pfrazee.com (192k records).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CAR parser now builds a StringHashMap index during read(), findBlock()
uses O(1) hash lookup instead of linear scan. verifyRepo bypasses
default 2MB/10k block limits for fetched repos.
Clarify Rust ecosystem in devlog: rsky (BlackSky), jacquard
(@nonbinary.computer), and hand-rolled RustCrypto bench.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>