atproto utils for zig
zat.dev
atproto
sdk
zig
1# archived: expanded plan (partially implemented)
2
3This file is preserved for context/history. Current direction lives in `docs/roadmap.md`.
4
5# zat - expanded scope
6
7the initial release delivered string primitives (Tid, Did, Handle, Nsid, Rkey, AtUri). this plan expands toward a usable AT Protocol sdk.
8
9## motivation
10
11real-world usage shows repeated implementations of:
12- DID resolution (plc.directory lookups, did:web fetches)
13- JWT parsing and signature verification
14- ECDSA verification (P256, secp256k1)
15- base58/base64url decoding
16- XRPC calls with manual json navigation
17
18this is shared infrastructure across any atproto app. zat can absorb it incrementally.
19
20## next: did resolution
21
22```zig
23pub const DidResolver = struct {
24 /// resolve a did to its document
25 pub fn resolve(self: *DidResolver, did: Did) !DidDocument
26
27 /// resolve did:plc via plc.directory
28 fn resolvePlc(self: *DidResolver, id: []const u8) !DidDocument
29
30 /// resolve did:web via .well-known
31 fn resolveWeb(self: *DidResolver, domain: []const u8) !DidDocument
32};
33
34pub const DidDocument = struct {
35 id: Did,
36 also_known_as: [][]const u8, // handles
37 verification_methods: []VerificationMethod,
38 services: []Service,
39
40 pub fn pdsEndpoint(self: DidDocument) ?[]const u8
41 pub fn handle(self: DidDocument) ?[]const u8
42};
43```
44
45## next: cid (content identifiers)
46
47```zig
48pub const Cid = struct {
49 raw: []const u8,
50
51 pub fn parse(s: []const u8) ?Cid
52 pub fn version(self: Cid) u8
53 pub fn codec(self: Cid) u64
54 pub fn hash(self: Cid) []const u8
55};
56```
57
58## later: xrpc client
59
60```zig
61pub const XrpcClient = struct {
62 pds: []const u8,
63 access_token: ?[]const u8,
64
65 pub fn query(self: *XrpcClient, nsid: Nsid, params: anytype) !JsonValue
66 pub fn procedure(self: *XrpcClient, nsid: Nsid, input: anytype) !JsonValue
67};
68```
69
70## later: jwt verification
71
72```zig
73pub const Jwt = struct {
74 header: JwtHeader,
75 payload: JwtPayload,
76 signature: []const u8,
77
78 pub fn parse(token: []const u8) ?Jwt
79 pub fn verify(self: Jwt, public_key: PublicKey) bool
80};
81```
82
83## out of scope
84
85- lexicon codegen (separate project)
86- session management / token refresh (app-specific)
87- jetstream client (websocket.zig + json is enough)
88- application frameworks (too opinionated)
89
90## design principles
91
921. **layered** - each piece usable independently (use Did without DidResolver)
932. **explicit** - no hidden allocations, pass allocators where needed
943. **borrowing** - parse returns slices into input where possible
954. **fallible** - return errors/optionals, don't panic
965. **protocol-focused** - AT Protocol primitives, not app-specific features
97
98## open questions
99
100- should DidResolver cache? or leave that to caller?
101- should XrpcClient handle auth refresh? or just expose tokens?
102- how to handle json parsing without imposing a specific json library?