how coral uses zat today
Coral's backend is a zig HTTP + WebSocket server that ingests named entities from a python NER bridge and maintains a co-occurrence graph. The bridge POSTs JSON payloads with entities, DIDs, firehose metrics, and jetstream state. A separate endpoint accepts LLM-curated groups.
What we just wired up:
┌────────────────┬──────────────────────────────────┬───────────────────────────────────────┐ │ zat API │ where in coral │ what it replaced │ ├────────────────┼──────────────────────────────────┼───────────────────────────────────────┤ │ json.getString │ http.zig:199,211,234,237,331,362 │ obj.get("key") + .string type check │ ├────────────────┼──────────────────────────────────┼───────────────────────────────────────┤ │ json.getFloat │ http.zig:206 │ .float / .integer branch │ ├────────────────┼──────────────────────────────────┼───────────────────────────────────────┤ │ json.getBool │ http.zig:222 │ .bool type check │ ├────────────────┼──────────────────────────────────┼───────────────────────────────────────┤ │ json.getArray │ http.zig:192,319,341 │ .array type check │ ├────────────────┼──────────────────────────────────┼───────────────────────────────────────┤ │ Did.parse │ http.zig:200 │ raw string acceptance (no validation) │ └────────────────┴──────────────────────────────────┴───────────────────────────────────────┘
Net result: -23 lines, eliminated every parsed.value.object.get() + type-check pattern. The DID validation is the only behavioral change — malformed DIDs from the bridge are now silently dropped instead of hashed.
what coral does NOT use from zat
Most of zat's surface area is untouched:
- streaming clients (JetstreamClient, FirehoseClient) — coral's python NER bridge handles jetstream consumption directly via the jetstream websocket. the zig backend is purely a graph server, not a firehose consumer.
- identity resolution (DidResolver, HandleResolver, DidDocument) — coral only needs DID hashes for user dedup, not resolution to PDS endpoints or signing keys.
- xrpc client — coral doesn't call any atproto APIs.
- repo/crypto (CBOR, CAR, MST, multibase, multicodec, JWT, repo verification) — coral operates on extracted entity text, not raw atproto records.
- syntax types beyond Did (Tid, Handle, Nsid, Rkey, AtUri) — the bridge sends plain strings, not AT URIs.
- json.extractAt / extractAtOptional — the comptime struct extraction. coral's JSON shapes are flat enough that getString/getArray cover it.
what zat could provide that would help coral
- json response writer helpers
This is the big one. Coral spends more code writing JSON than reading it. handleStats (http.zig:87-157) is 70 lines of manual try w.print(""key":{d},", .{val}) string assembly. entity_graph.zig:toJson() and toDiagnosticsJson() are another ~200 lines of the same.
Coral already uses std.json.Stringify in some places (entities.zig, entity_graph.zig:toJson), so this isn't a gap in std — it's more that the manual bufPrint approach crept in for performance-sensitive paths where Stringify felt heavy. A lightweight "write a field" helper in zat wouldn't add much over what std already provides here. Verdict: not a zat concern.
- json.getString on raw values (not just paths)
When iterating an array of raw strings like ["entity1", "entity2"], getString(ent_val, "") doesn't work because getPath with "" tries to look up an empty key in an object. Coral falls back to if (ent_val != .string) continue; ... ent_val.string for these cases (http.zig:344).
A small addition to zat:
/// unwrap a value as a string (no path navigation) pub fn asString(value: std.json.Value) ?[]const u8 { return switch (value) { .string => |s| s, else => null, }; }
/// similarly: asFloat, asBool, asInt
This would let coral write zat.json.asString(ent_val) orelse continue instead of the manual type check. Small but it's the one spot where the zat helpers fell short.
- Did.hash() convenience
Coral does Did.parse(str) purely to validate, then throws away the parsed result and hashes the raw string separately with Wyhash (http.zig:200-202). A Did.hash() or even just making it easy to go parse → use .raw for hashing could be slightly cleaner, but honestly Did.parse returning the struct with .raw already works — coral just doesn't use it. Verdict: not needed, coral's pattern is fine.
- nothing else, really
Coral is a deliberately simple graph server. It doesn't do identity resolution, repo verification, record parsing, or any XRPC calls. The python bridge handles all the atproto-aware work. Zat's streaming clients (JetstreamClient) could theoretically replace the python bridge's jetstream consumer, but that would be a much larger architectural change (replacing spaCy NER with a zig-native solution).
summary
Actionable for zat: add asString / asFloat / asBool / asInt / asArray value unwrappers (no path navigation) alongside the existing getString / getFloat / etc. path-based helpers. This covers the "iterating an array of leaf values" case that getString(val, "") doesn't handle.
Everything else is a good fit. The json path helpers and Did.parse are the right level of abstraction for coral's needs. Coral doesn't need more from zat — it needs zat to stay small and focused on what it does well.