A historical record of the surface that has shipped (each implemented and tested). For what's still ahead, see Next planned work.
-
Wire in a JSON library (cJSON).
-
DID/handle resolution (
wf_did_resolve,wf_handle_resolve). -
DAG-CBOR decode/encode, with full constraint validation and unit tests.
-
SHA-256 + CID computation (
wf_cid_of_block,wf_cid_to_string). -
CAR parsing (
wf_car_parse). -
MST traversal + mutation (
wf_mst_find,wf_mst_add,wf_mst_delete,wf_mst_node_build,wf_mst_node_finalize). -
secp256k1 and P-256 signing (
wf_sign,wf_verify,wf_signing_key_generate). -
Signed commit creation (
wf_commit_create). -
DNS TXT lookup for AT Protocol handle resolution (c-ares when available, POSIX resolver fallback; multi-record and chunked TXT handling).
-
DAG-CBOR schema-driven encoding (structured record creation).
-
PDS client — session management, credential storage, auth token refresh.
-
Repository data operations — create/update/delete records, full/diff CAR download (
wf_sync_get_repo), and ownership/signature/content-addressed verification/import (wf_repo_verify,wf_repo_import). -
Lexicon integration — typed C schemas, inline and referenced input objects, repeated-key array query parameters, complete JSON input encoders, generated query/procedure wrappers, and owning output decoders.
-
Union/jetstream — libcurl WebSocket transport, filtered Jetstream URL construction, runtime subscriber options, JSON event envelopes, cursor-based reconnect/backoff, and dictionary-based zstd messages.
-
OAuth — protected-resource, authorization-server, and discoverable client metadata discovery, PKCE S256, persistent ES256 DPoP keys, JWK thumbprints/proofs, public-client PAR and authorization-code token exchange with mandatory nonce retry, state/issuer-bound callback validation, subject-bound token refresh, durable token sessions, ES256
private_key_jwtauthentication, authorization-begin orchestration, callback-to-session completion, and managed session refresh. -
Syntax validation — DID, handle, at-identifier, NSID, record key, TID, AT URI, RFC 3339 datetime, and BCP 47 language tag validators.
-
Rich text — grapheme-aware byte indexing, facet detection (mentions, links, tags), and segment iteration.
-
Firehose subscription —
com.atproto.sync.subscribeReposWebSocket stream with CBOR frame parsing, cursor-based reconnect, and backoff. -
Firehose verification — commit signature verification, key resolution, CAR parsing, and MST/root validation.
-
Blob upload — binary POST to
xrpc/{nsid}with custom Content-Type, authenticated via session or DPoP (wf_xrpc_upload_blob,wf_auth_client_upload_blob). -
Label subscription —
com.atproto.label.subscribeLabelsWebSocket stream with JSON frame parsing, cursor reconnect, and backoff. -
Firehose event production —
sync_publishbuilds the framed CBOR event messages a relay/PDS streams over WebSocket: a header map{op, t}immediately followed by the body map, coveringcommit/sync/identity/account/infoandop:-1error frames. Exact inverse of thesync_subscribedecoder; round-trip tested bytest_sync_publish. -
Repo diff tests — comprehensive tests for
wf_repo_diff_applyandwf_repo_operations_invert. -
Lexicon validation — runtime object/record validation against lexicon schemas (
object,record,query,procedure,params,blob,ref,array,string,integer,boolean,unknown,union). -
Agent API wrappers — sync getBlob/getBlocks/getRecord/listBlobs, repo.listRecords, social graph (mute/unmute), graph wrappers (getBlocks, getMutes, getKnownFollowers, getRelationships, getList, getLists), feed wrappers, and notification wrappers. Full input-validation tests.
-
Agent repo sync pipeline — offline mirror seed, verified incremental diff apply, mirror head query, operation inversion, local mirror record lookup.
-
DID PLC operation helpers (
wf_plc_*,plc.h) — build/sign/submitplcoperations (create, rotate signing/handle/rotation keys, tombstone) with ES256 signatures and verification. -
Moderation decision engine (
moderation.h) — blur/alert/inform/filter decisions for accounts, profiles, posts, notifications, feed generators, and user lists from labels, blocks, mutes, hidden posts, and muted words. Offline; ingests API-shaped JSON (wf_mod_prefs_from_json,wf_mod_label_defs_from_labeler,wf_mod_labels_from_json). -
Generic JSON module (
json.h) — canonical round-trip (wf_json_canonicalize) and a JSON-Schema validator subset (type/required/properties/items, enum/const, format, numeric bounds, string length/pattern, array constraints, additionalProperties, anyOf/oneOf/not). -
SQLite store persistence (
store.h) — session + repo-mirror persistence and persisted-label storage for the moderation engine; OFF by default, build withWOLFRAM_BUILD_STORE=ON. WithWOLFRAM_BUILD_STORE_CRYPTO=ON(libsodium) the session credentials are encrypted at rest (XSalsa20-Poly1305, Argon2id). -
Video upload (
blob.h/agent.h) — dedicated video endpoint upload (wf_agent_upload_video), job-status polling (wf_agent_get_video_job_status), and upload limits (wf_agent_get_video_upload_limits). -
Chat typed wrappers (
chat_typed.h) —chat.bsky.convo/group/actor/moderationwrite+query wrappers with chat-service endpoint resolution. The full chat write surface is now implemented. -
Chat moderation event subscription (
chat_typed.h) — real client-side WebSocket subscription forchat.bsky.moderation.subscribeModEvents. Decodes the atproto framed DAG-CBOR envelope (header map{op, t}++ body map) with libcbor, fills awf_chat_mod_event(union tag from the headert), advances a numeric cursor, and reconnects with capped exponential backoff. API:wf_chat_mod_frame_parse_cbor,wf_chat_mod_frame_free,wf_chat_mod_events_build_url,wf_chat_mod_events_start/wf_chat_mod_events_stop, and the agent convenience wrapperwf_agent_chat_subscribe_mod_events_typed(resolves the chat-service endpoint from the agent session). Tested offline: decoder unit tests (message/error/truncated/garbage) + an in-processwf_xrpc_serverWS round-trip that streams the framed CBOR bytes. -
Ozone moderation-service / labeler helper (
ozone.h) — verify and emit labels, build service auth headers for the Ozone moderation service. -
Generated typed wrappers — owning parsers for embed/feed/feed-generator/ graph/list/thread records (
embed_typed.h,feed_typed.h,feedgen_typed.h,graph_typed.h,list_typed.h,thread_typed.h) plus threadgate/postgate record helpers (threadgate_postgate.h). -
Authenticated XRPC client (
auth_client.h) — DPoP-binding OAuth-authenticated XRPC query/procedure/blob-upload wrapper (wf_auth_client_*) with session refresh and DPoP nonce retry. -
Labeler service record coverage (
labeler_typed.h) — owned typed parsers + agent wrappers forcom.atproto.label.queryLabels,app.bsky.labeler.getServices(including embedded service records, policies, and label value defs), andcom.atproto.temp.fetchLabels. -
Identity namespace wrappers (
identity_typed.h) — owned typed parsers + agent wrappers forcom.atproto.identity(resolveHandle, resolveDid, updateHandle, getRecommendedDidCredentials, signPlcOperation, submitPlcOperation, resolveIdentity, refreshIdentity) and a PLC handle-rotation convenience (wf_agent_identity_rotate_handle) built onplc.h. The convenience now wires the full rotation flow: it builds the rotation operation locally as a validation gate, then (given the out-of-bandrequestPlcOperationSignaturetoken) signs it server-side viasignPlcOperationand submits it viasubmitPlcOperation. With a missing token it triggers the email and returns an honestWF_ERR_INVALID_ARG. -
Notification v2 + activity subscriptions (
notification_v2_typed.h) — owned typed parsers + agent wrappers forapp.bsky.notification.putPreferencesV2,listActivitySubscriptions, andputActivitySubscription. The v2 preferences now have a dedicated typed builder/parser:wf_notification_v2_preferences_build/_parse/_freeover the 13-slotwf_notification_v2_preferencesstruct (perapp.bsky.notification.defs#preferences), plus a typed agent wrapperwf_agent_put_notification_preferences_v2_typed. -
Higher-level endpoint examples (
examples/) — self-contained programs exercising generated clients: label query, PLC handle rotation, notification v2, and admin account search. -
High-level
wf_bsky_agentconvenience wrapper (bsky_agent.h) — a BskyAgent equivalent bundling session + xrpc + identity + agent, with one-line helpers for login, post, getProfile, getTimeline, resolveHandle, follow, unfollow, like, repost, mute/unmute, getNotifications, searchActors, and getThread (all delegating to the existingwf_agent_*API). Tested. -
app.bsky.actor.statustyped wrappers (actor_status_typed.h) — owned parsers + a record builder for status records/views following the labeler/actor ownership model.getActorStatus/getStatus/putStatusare not real endpoints in the reference (confirmed against bluesky-social/atproto and bluesky-social/social-app: no such RPCs exist anywhere, only theapp.bsky.actor.statusrecord type), so the agent wrappers read the status embedded in a getProfile(s) response and write it via a plaincom.atproto.repo.putRecord, matching how social-app's liveNow feature actually does it.wf_actor_status_parse_viewalso had a bug fixed alongside this: createdAt/durationMinutes live inside statusView's opaquerecordfield, not at the top level, so real responses always left both unset. Tested. -
tools.ozone.*typed coverage (ozone.h/ozone.c) — initial batch of typed convenience wrappers for moderation (queryStatuses, getLabelDefs, emitEvent, queryEvents, getEvent, getReporterStats, getSubjects), communication templates, and set values. Tested.getSuggestionsis also wrapped but unconfirmed against the reference -- no lexicon, server route, or test fortools.ozone.moderation.getSuggestionsexists anywhere in bluesky-social/atproto as of this writing; see the comment onwf_ozone_get_suggestions. -
wolframCLI social commands —profile,timeline,follow,unfollow,like,repost,search,notifications,mute,unmute, andthread, reusing the existingwf_agent_*APIs. -
Remaining
tools.ozone.*typed wrappers (ozone.h/ozone.c) — additional moderation sub-endpoints (getAccountTimeline, getRecords, getRepo, getRepos, searchRepos, cancelScheduledActions, scheduleAction, listScheduledActions), queue (assignModerator, createQueue, deleteQueue, getAssignments, listQueues, routeReports, unassignModerator, updateQueue), report (14 endpoints), team (add/delete/list/update member), verification (grant/list/revoke), signature (findCorrelation, findRelatedAccounts, searchAccounts), setting (listOptions, removeOptions, upsertOption), hosting (getAccountHistory), server (getConfig), and safelink (addRule, queryEvents, queryRules, removeRule, updateRule). Tested. -
Feed generator + discovery typed wrappers (
feed_gen_typed.h) — owned parsers + agent wrappers forapp.bsky.feedgenerator/discovery endpoints (getFeedGenerator[s], getActorFeeds, getSuggestedFeeds, getLikes, getRepostedBy, getQuotes, getActorLikes, getListFeed, searchPosts[V2]), reusingwf_agent_feed_list/wf_agent_actor_listwhere they fit. Tested. -
Graph social typed wrappers (
graph_social_typed.h) — owned parsers + agent wrappers for mutes/blocks list views (getListMutes, getListBlocks), starter packs (getStarterPack[s], getActorStarterPacks), suggested follows, and mute/unmute (actor + actor-list) procedures. Tested. -
Core notification typed wrappers (
notification_typed.h) — a richer notification view union (all reason types) plus wrappers for listNotifications, getUnreadCount, updateSeen, register/unregisterPush, distinct from the v2 preferences module. Tested. -
Actor preferences typed wrappers (
actor_prefs_typed.h) — an owned preferences union (all 16 known$typepreference objects, unknown types preserved verbatim) with parser + builder, plus getSuggestions and a declaration parser/builder. Tested. -
XRPC server module (
xrpc_server.h) — optionallibmicrohttpd-backed server with route registration (query/procedure), auth middleware, query parameter parsing, buffered JSON POSTs, bounded-memory streaming procedures with early auth and interruption cleanup, CORS headers, integrated token-bucket rate limiter (wf_rate_limiter), and readback of the bound port. Built whenWOLFRAM_BUILD_SERVER=ON. Tested with offline round trips againstwf_xrpc_client, multi-chunk and interrupted streaming requests, rate limiter unit tests, and a server rate-limit integration test. -
Feed-generator skeleton server helper (
feedgen_server.h) — high-level helper built on the XRPC server that servesapp.bsky.feed.getFeedSkeleton(delegated to a caller-supplied callback returning skeleton post AT-URIs) andapp.bsky.feed.getFeedGenerator(synthesised from a config struct of display name, description, DID, and optional avatar/CID). Deep-copied config withwf_feedgen_server_config_free,wf_feedgen_server_new/start/stop/free, and an offline round-trip test againstwf_xrpc_client. Built whenWOLFRAM_BUILD_SERVER=ON. Tested. -
Blob persistence + serving — migrated to MetalBear as
metalbear_blob_store*(see the MetalBear repository for themetalbear_blob_store.h/metalbear_blob_store.cimplementation andmetalbear_blob_store_server.cXRPC route handlers). The originalwf_blob_store*source files remain in this repository for historical reference but are no longer built or part of the SDK. -
Server-Sent Events (SSE) streaming for the XRPC server — real streaming via libmicrohttpd
MHD_suspend_connection/MHD_resume_connection. An SSE route receives awf_xrpc_sse_streamin its handler; frames are pushed withwf_xrpc_server_sse_send(formatsdata: <payload>\n\n, optionalevent:line) orwf_xrpc_server_sse_send_raw, and the connection is closed withwf_xrpc_server_sse_close. Single-shot SSE (send then close) is supported as a fallback. Suspended connections are resumed and drained onwf_xrpc_server_stop/wf_xrpc_server_freeso teardown never hangs. Tested bytest_xrpc_server_sse(streaming + single-shot, clean teardown). -
Graph write wrappers (
graph_write.c/graph_write.h) — agent-level write helpers for the app.bsky.graph lexicon:wf_agent_graph_*for mute/unmute thread and actor-list procedures, and record-backed writes (block/unblock, list create/update/delete, listitem create/delete, starterpack create/update/delete, listblock create/delete). Create operations return an ownedwf_agent_post_result({uri, cid}, freed withwf_agent_post_result_free); deletes and procedures returnwf_status. Inputs are validated (no silent no-ops). Tested end-to-end against an offline mock PDS (asserting returned uri/cid and the exact request payloads). Tested. -
app.bsky.videotyped wrappers (video_typed.h) — owning parsers for thegetJobStatusanduploadVideojobStatus envelopes, thegetUploadLimitsoutput, and the sharedapp.bsky.video.defs#jobStatusblob-bearing object (unknown fields preserved in ownedextra); builders for the defs and upload limits shapes; and agent wrappers (wf_agent_video_get_job_status,wf_agent_video_get_upload_limits,wf_agent_video_upload) that call the existing raw video helpers and return owned structs. Tested. -
C++ RAII wrapper (
wolfram-cpp,cpp/wolfram-cpp/) — header-only consumer layer overlibwolfram:unique_handle<T, Free>mirroring thewf_T+wf_T_freeownership contract, acstringRAII owner for heapchar*, andwf_status→std::error_codemapping with a throwingrequire. Owned handle typedefs are generated from thewf_*_freeset (tools/gen_owners.cpp→generated_owners.hpp, 372 handles from 51 headers). Built withWOLFRAM_BUILD_CPP=ON; covered by an offlinewolfram-cpp-smoketest. As part of adding it, the C public headers were made C++-clean: thewf_status/wf_errorstatus enums were unified (previously duplicated with divergent values),_result.hgainedextern "C"guards, and a straytypetoken beforestatic inlinewas removed. -
C# interop wrapper (
Wolfram.Interop,dotnet/Wolfram.Interop/) — pure pass-through P/Invoke layer overlibwolfram(no reimplementation of crypto/transport/serialization/server logic). Raw tier uses source-generatedLibraryImport(NativeAOT/trim-safe) with explicit UTF-8 marshalling and a combinedDllImportResolverforlibwolfram(+ platform libc to free owned strings); managed tier addsXrpcClientHandle : SafeHandle, aStatusmirror ofwf_status, andWolframExceptionraised on non-Ok. Covered by an offline xUnit smoke test (Wolfram.Interop.Tests). -
WebSocket (RFC 6455) subscription endpoints for the XRPC server — real server→client push over a libmicrohttpd upgrade (
MHD_create_response_for_upgradeMHD_ALLOW_UPGRADE, libmicrohttpd 1.0.5). A WS route receives awf_xrpc_ws_streamin its handler (invoked from the connection's upgrade worker thread after the 101 handshake, withSec-WebSocket-Acceptcomputed via SHA-1 + base64 over the client key and the RFC GUID). Frames are pushed withwf_xrpc_server_ws_send(binary, opcode 0x2, UNMASKED) and the stream is ended withwf_xrpc_server_ws_close(close frame, opcode 0x8). The upgrade worker also reads client control frames — answering ping (0x9) with pong (0xA) and honouring client close (0x8) — while inbound data frames are drained and ignored. Suspended/upgraded connections are resumed, shut down, and joined onwf_xrpc_server_stop/wf_xrpc_server_freeso teardown never hangs. Built whenWOLFRAM_BUILD_SERVER=ON. Tested bytest_xrpc_server_ws(raw-client handshake + accept verification, ordered binary frames, close termination, clean teardown).
-
Typed-wrapper coverage broadened across remaining lexicon endpoints (developed on parallel branches, all merged to
main).embed_typed.h: owning parser + agent wrapper forapp.bsky.embed.getEmbedExternalView.feed_gen_typed.h:wf_feedgen_send_interactions_typedforapp.bsky.feed.sendInteractions.graph_social_typed.h: owning parsers + agent wrappers forapp.bsky.graph.getListsWithMembershipandapp.bsky.graph.getStarterPacksWithMembership(membership envelopes reusewf_graph_list_view/wf_graph_starter_pack_view+ an optionalwf_graph_list_item_view);searchStarterPacksis reused fromunspecced_typed.hrather than redefined.repo_typed.h: owning parser for create/put record results + agent wrappers forcom.atproto.repocreateRecord/putRecord/deleteRecord/uploadBlob/importRepo.server_typed.h: agent wrappers for the remainingcom.atproto.serverendpoints (describeServer, createAccount, createAppPassword, listAppPasswords, revokeAppPassword, deleteSession, activate/deactivateAccount, confirmEmail, resetPassword, updateEmail, createInviteCode(s)) layered on the ownedserver.hresult structs.sync_typed.h:wf_agent_get_blob_typedforcom.atproto.sync.getBlob.temp_typed.h: realrevokeAccountCredentialswrapper carrying the requiredaccountinput (replaces the prior stub). Newlexicon_typed.hmodule: owning parser +wf_agent_resolve_lexicon_typedforcom.atproto.lexicon.resolveLexicon.chat_typed.h: owning parser + agent wrappers forchat.bsky.notification.getPreferences/putPreferences. All tested offline (parser round-trips + argument validation). -
Generic upstream→downstream WebSocket subscription relay for the XRPC server (
relay_server.h) — a protocol-agnostic raw-frame relay built on the XRPC server's WS endpoints and the libcurl WebSocket client transport.wf_xrpc_server_register_relay(server, cfg)registers a WS route (e.g.com.atproto.sync.subscribeRepos) that, on a downstream connect, opens an upstreamws(s)://connection and forwards each received message byte-for-byte to the client until either side closes or errors, then closes the downstream stream. Config (wf_relay_config) deep-copied on registration and freed bywf_relay_config_free; the returnedwf_relay_serverhandle owns that copy and is freed bywf_relay_server_free(afterwf_xrpc_server_free). The forward loop runs on a worker thread spawned by the server's upgrade handler, matching the server's streaming contract; optional reconnect delay (bounded by the downstream client staying connected). Built whenWOLFRAM_BUILD_SERVER=ON. Tested bytest_relay_server(offline: forwards an in-process upstream's ordered binary frames to awf_websocketclient, then a clean close; when the linked libcurl lacks WS support the test still verifies registration, the forward thread, and clean downstream teardown against an unreachable upstream). -
OAuth resource-server token verification (
oauth/verify.h) — validates incomingAuthorization: Bearer <access-token>andDPoP: <proof-jwt>headers the way an XRPC server (or any atproto resource server) must. Reuses the existing P-256 / ES256, SHA-256, base64url and JWK primitives (wf_crypto_p256_verify,wf_crypto_p256_jwk_coords,wf_crypto_sha256,wf_crypto_base64url_decode/_encode, added tocrypto.h/crypto.c) and never hand-rolls crypto. Public API:wf_oauth_trusted_keys(container of one or more trusted JWKs, with_new/_add_jwk/_free),wf_oauth_verified_token(a verified principal with ownedsub/iss/aud/scope/dpop_jktand adpop_boundflag, freed bywf_oauth_verified_token_free),wf_oauth_dpop_replay_cache(in-memoryjticache with_new/_is_seen/_mark_seen/_freeand optional per-entry TTL eviction), and the verify entry pointswf_oauth_verify_bearer,wf_oauth_verify_dpop, and the request-level conveniencewf_oauth_verify_request(parsesBearer+DPoPheaders, verifies both, checks the DPoPathagainstSHA-256(access_token), and enforcescnf.jktconfirmation matching). DPoP checks follow RFC 9449 + atproto: signature over the proof with its own JWK,typ "dpop+jwt",alg ES256,htm/htu/jti/iat/expvalidation, andjtireplay rejection. An XRPCwf_xrpc_auth_cbcan callwf_oauth_verify_requestwith awf_xrpc_server-supplied method/nsid and a shared trusted-keys set + replay cache, returning 401 on any non-WF_OK. Tested offline bytest_oauth_verify(happy path + tampered signature, expired token, wronghtm/htu,athmismatch, reusedjti, missing proofjwk, non-ES256alg, and Bearer-only / DPoP-only paths). -
Service-auth (service JWT) token issuance (
server.h) — the PDS-side complement ofcom.atproto.server.getServiceAuth. Where the SDK could previously only request a service token from a PDS (wf_agent_get_service_auth_typed) and extract the response'stokenfield (wf_server_parse_auth_token), a self-hosted PDS can now mint one locally by signing a compact JWT with its own repo signing key.wf_server_create_service_auth(req, key, &token)builds header{typ:"JWT", alg}+ payload{iat, iss, aud, exp, [lxm], jti, [nuance]}and signsbase64url(header)"."base64url(payload)withwf_sign(P-256 → ES256, secp256k1 → ES256K; 64-byte compact low-S signature), defaultingexptoiat+60and generating a fresh 16-byte hexjtiper call.wf_server_verify_service_auth(token, didkey, now, &claims)decodes and validates the token: it checks the signature withwf_verifyagainst the issuer'sdid:keyand rejects expired tokens, returning owned claims (wf_service_auth_claims_free). The signing key is modelled as the existingwf_signing_key(same representation the crypto layer uses) plus the DID strings inwf_service_auth_request; no JOSE signing is hand-rolled — crypto is delegated towf_sign/wf_verify. Wire format mirrors@atproto/xrpc-servercreateServiceJwt. Tested offline (test_service_auth): field-level round-trip of iss/aud/exp/iat/jti/lxm/ nuance, wrong-key / tampered-payload / expired-token rejection, defaultedexp, and jti uniqueness. -
Injectable DID-key resolver for commit/label verification (
verify.h/verify.c).wf_verify_record_commitalready verified a signed commit CAR against a caller-supplieddid:key:z...signing key; the resolve-and-verify path (wf_agent_verify_record) previously stubbed the live key-fetch behind#ifndef WF_TEST_LIVEand returnedWF_ERR_INVALID_ARG. It now takes an injectable resolver (wf_verify_key_resolver, installed viawf_verify_set_key_resolver) so verification can fetch the signing key for a DID/key-id WITHOUT a hard network dependency. Newwf_verify_record_commit_resolved(did, car, len, &valid)uses the resolver to obtain the key, then delegates to the existingwf_repo_verifycore. The firehose commit verifier (wf_sync_verify_commit, the commit/label stream path) now consults the same resolver viawf_verify_resolve_signing_key, falling back to livewf_did_resolvewhen no resolver is set. A built-inwf_verify_resolve_via_didresolver (backed bywf_did_resolve) is provided for production use. Honest default preserved: with no resolver installed, the resolve-and-verify entry points returnWF_ERR_INVALID_ARGrather than fabricating a key — signatures are never accepted by default. Tested offline bytest_verify_resolver(happy path, wrong-key rejection, unavailable-key error, tampered-commit rejection, no-resolver honest error, and agent-wrapper resolver wiring with no network reached). -
Cross-compilation / platform abstraction (
platform.h,src/platform/,.devdeps/*.cmake) — a thin platform layer (init/shutdown, mutex, monotonic time) with POSIX (Linux/macOS) and Win32 backends fully implemented, plus toolchain files and client-only build wiring for Nintendo Wii/Wii U/3DS (devkitPro), Windows (MinGW-w64), and Linux/AArch64. The Wii platform primitives use libogc (net_init, LWP mutexes, and the timebase clock); Wii U platform primitives use wut (socket_lib_init,OSMutex,OSGetTime, divide-first tick conversion). Wii HTTPS (mbedTLS, verified certificate chain, fail-closed entropy), P-256/ did:key crypto, and secp256k1 (did:key) crypto via mbedTLS are real. 3DS platform primitives (LightLock mutex, osGetTime clock, httpc transport) and mbedtls-based P-256/did:key crypto are real. -
Wii WebSocket client (
src/transport/websocket_wii.c) — a real RFC 6455 implementation replacing the prior honest stub, built onwii_tls(mbedTLS handshake + verified certificate chain over lwIP). Covers the HTTP/1.1 Upgrade handshake with Sec-WebSocket-Accept verification (mbedtls_sha1+ RFC 4648 base64, mirroring the check the SDK's own XRPC server performs as a WS server), masked client framing per RFC 6455 §5.2/§5.3, fragmented-message reassembly, and transparent ping/pong. wss:// only, matching every AT Protocol subscription endpoint. The mask key and Sec-WebSocket-Key are drawn fromwii_tls_random— the same DRBG that seeds TLS and P-256 signing — neversrand()/timing.wii_tls_pending()(new inwii_tls.{c,h}) giveswf_websocket_receivea real non-blocking readiness check: it consults mbedTLS's internal record buffer before falling back to a zero-timeoutselect(), so already-decrypted-but-unread bytes are not missed. Verified via a devkitPPC cross-build andpowerpc-eabi-nm: the prior stub was missingwf_websocket_send_pingentirely, an undefined symbol that would only have surfaced at link time in a real firehose/ Jetstream consumer. As with the rest of the Wii backend, a cross-build proves compile/link compatibility, not HTTP/TLS correctness on hardware — this has not run on real console hardware. -
Typed output-decoder coverage for
ref-shaped endpoint outputs (tools/wf_lexgen.cpp) —wf_lexgenpreviously only cataloged a query/procedure's output as decodable when itsoutput.schemawas an inlineobject; 29 of 314 callable endpoints instead declareoutput.schemaas arefto an object def defined elsewhere (e.g.tools.ozone.moderation.getRecord→tools.ozone.moderation.defs#recordViewDetail,com.atproto.identity.resolveIdentity→com.atproto.identity.defs#identityInfo,app.bsky.actor.getProfile→app.bsky.actor.defs#profileViewDetailed), and those silently got no_output_decode_json/_output_freepair — callers had to hand-parse raw cJSON. Newoutput_object_type()mirrors the existingjson_input_alias_type()pattern for ref-typed inputs: for a ref resolving to an object def already in the object catalog,<base>_outputbecomes a typedef alias onto that type (no duplicated struct), and the generated decode/free functions delegate to the referenced type's ownwf_lex_decode_*/wf_lex_clear_*. Raises typed output coverage from 231/314 to 260/314 callable endpoints.test_lexgen's newoutput_decoders_cover_object_shaped_endpointsparses the full bundled lexicon corpus, resolves every callable endpoint's output schema the same way the generator does, and asserts the generated header declares a decoder/free pair for each of the 260 — regenerating without this fix would fail that test. The remaining 54 endpoints have no JSON output at all: 48 reply with an empty body (mostprocedures with nooutputschema at all), and 6 use a non-JSON encoding for a raw byte/CAR/JSONL stream (com.atproto.sync.getRepo/getRecord/getBlocks/getCheckout,com.atproto.sync.getBlob,chat.bsky.actor.exportAccountData) that already has a dedicated raw-bytes wrapper elsewhere (sync_typed.h,blob.h) — there is no JSON object for a decoder to produce, so this is a structural floor, not a remaining gap. -
Typed agent wrappers for the last generated-only callable endpoints —
app.bsky.graph.searchStarterPacksV2(wf_agent_search_starter_packs_v2_typed/wf_graph_search_starter_packs_v2_resultingraph_social_typed.h, reusing the existing per-item starter-pack-view parser and adding the optionalhitsTotalfield the v1 endpoint doesn't have),tools.ozone.report.closeReports(added to the existing X-macro endpoint table inozone_typed.h/.c—X(report, closeReports, close_reports, P)— rather than a one-off wrapper), andtools.ozone.moderation.getRecordupgraded from a raw-wf_responsewrapper to an owning decoder inozone_admin_typed.h/.cnow that its ref-typed output has one (item 61).app.bsky.video.getJobStatus/getUploadLimits(src/agent/agent.c) were rewired from a hand-builtwf_xrpc_query_paramscall plus aWF_LEX_*_NSIDconstant to the generatedwf_lex_app_bsky_video_get_*_main_call, closing the same class of drift risk the ref-output decoder gap did; a newtest_video_typed_httpd.c(built underWOLFRAM_BUILD_TEST_HTTPD) proves the swap against a real local mock PDS rather than by inspection alone.tools.ozone.set.deleteSetand.querySets— named in the originating issue as missing — turned out to already be wired through the same X-macro table; verified rather than duplicated.tools.ozone.moderation.getRepohas the identical ref-output shape asgetRecordand was upgraded from the stale QR-vs-Q wrapper to an owning decoder alongside it.internal.bsky.actor. getProfilesstays unwrapped by design (internal namespace, excluded from the reference codegen too). -
Authenticated WebSocket connections (
websocket.h/.c,websocket_wii.c) —wf_websocket_connect_with_headers(url, headers, header_count, out)sends extra HTTP header lines with the WS upgrade request on both real backends (desktop libcurl viaCURLOPT_HTTPHEADER; Wii's hand-built RFC 6455 client splices them into the request text).wf_websocket_connectis now defined in terms of it (NULL/0 headers), so every existing caller is unaffected. Used to fixchat.bsky.moderation.subscribeModEvents(chat_typed.h/.c): this is a documented "private endpoint" requiring moderator authentication, but the WS connection previously had no way to carry a credential at all —wf_chat_mod_events_options.access_tokenis now sent asAuthorization: Bearer <token>on every (re)connect, withwf_agent_chat_subscribe_mod_events_typedsupplying the agent's current session token viawf_agent_get_session_data(a session with none connects unauthenticated, matching the prior behavior). This was investigated for issue #19, which also claimed a missingfilterparameter and said the hand-rolled path should use "the generated_main_call" — neither holds up: the bundledsubscribeModEventslexicon (verified byte-identical to the upstream checkout) has nofilterparameter, onlycursor, andwf_lexgennever generates a_main_callforsubscription-kind defs at all (onlyquery/procedure), matchingsubscribeReposandsubscribeLabels, which hand-build their WS URLs the same waysubscribeModEventsalready did. The requested offline frame-decode round-trip test also already existed (test_chat_modevents_sub.c). Fixing the real gap (authentication) surfaced a separate, unrelated pre-existing bug:WOLFRAM_BUILD_SERVERwas never defined as a compile macro for that test's target (the CMake option only controlled which targets got built), so its Layer-2 e2e round-trip had silently never compiled in, even with the option ON — fixed alongside extending that e2e test to assert the server observes theAuthorizationheader for real. Verified with a devkitPPC cross-build of both the Wii and Wii U targets (powerpc-eabi-nmshowswf_websocket_connect_with_headersdefined in both archives). -
Two P-256 crypto primitives (
crypto.h/.c) added for MetalBear's WebAuthn/passkey login support:wf_crypto_p256_verify_allow_malleable(same aswf_crypto_p256_verifybut accepts either low-S or high-S signature form — needed because a WebAuthn authenticator's assertion signature, unlike this SDK's own DPoP/service-JWT signing, is not guaranteed low-S normalized) andwf_crypto_ecdsa_der_to_raw(converts a DER-encoded ECDSA-Sig-Value, the form a browser's WebAuthn implementation produces, into the raw 64-byte r||s form the verify functions expect). The COSE_Key decoder and ceremony/ credential storage the prior scoping note below called out as net-new both landed in MetalBear (src/oauth/webauthn.c, a purpose-built CBOR reader rather thanwf_cbor_parse— see that file's header comment for why DAG-CBOR's canonical-ordering enforcement is the wrong fit for a browser-producedattestationObject), not this SDK; these two primitives were the actual SDK-level gap. -
Removed five speculative
tools.ozone.moderation.*wrappers (getLabelDefinitions/wf_ozone_get_label_defs,getSuggestions/wf_ozone_get_suggestionsand its typed wrapperwf_ozone_moderation_get_suggestions,getLabelDefinitions's typed wrapperwf_ozone_moderation_get_label_definitions,getTag/wf_ozone_get_tag,queryTags/wf_ozone_query_tags, andscanVerdicts/wf_ozone_scan_verdicts), plus their CLI subcommands (ozone moderation get-suggestions/get-label-definitions) and tests. Item 40 flaggedgetSuggestionsas unconfirmed against the reference; revisiting turned up that none of these five NSIDs exist anywhere in bluesky-social/atproto's lexicons, the bluesky-social/ozone frontend, or a GitHub code search of either — not just missing from the local snapshot, but not real, published, or served by any known deployment. Shipping API surface for endpoints that don't exist just misleads wolfram's own users into writing code against something that will 404/MethodNotImplemented on every real server.wf_ozone_get_subjects(tools.ozone.moderation.getSubjects, a real lexicon) is unaffected. -
Found and fixed a real Wii/Wii U build break while cross-compiling to verify item 65 didn't disturb anything nearby:
wf_sign's signature line and part ofwf_b58_decode's for-loop body incrypto_wii.chad been spliced together since dcc913a (the commit that added secp256k1 support), leavingwf_b58_decode's error branch replaced by an orphaned copy ofwf_sign's SECP256K1 block, andwf_signitself missing its return type/name and SECP256K1 branch entirely. This never surfaced in CI's usual desktop build (crypto_wii.c isn't compiled there), and nothing had cross-built Wii U since that commit -- confirmed by cross-compiling with devkitPPC, which failed without the fix and links clean with it. Also implementedRAND_bytes(openssl_compat.c) for real via the same seeded mbedTLS DRBGwii_tls_randomalready uses for TLS/P-256 signing, rather than leaving it permanently failing; nothing currently calls it (every caller in the tree is excluded from embedded builds), but the next thing that starts routing throughopenssl/rand.hon Wii/Wii U shouldn't hit a dead end. 3DS keeps failing honestly (no seeded DRBG wired up yet; separate, already-tracked gap). Also cleaned up three stale TODO comments (actor_status_typed.h,temp_typed.h/.c,plc.c) that described states already resolved or that were never actually gaps. -
wolf browse/wolf identity/wolf plc log-- the CLI equivalent of what a tool like PDSls (pdsls.dev) gives you in a browser, added on request. All three are anonymous/read-only, matching the underlying XRPC endpoints (describeRepo/listRecords/getRecord/resolveDid are public).browse <service> <handle-or-did> [collection] [rkey]composes existing describeRepo/listRecords/getRecord typed wrappers into one drill-down flow instead of requiring three separate raw commands, with full--jsonoutput at every level.identityresolves a DID document and prints every alsoKnownAs handle with a live bidirectional verification check (wf_identity_verify_handle/wf_agent_verify_handle, which already existed), plus verification methods, service endpoints, and (for did:plc) current PLC rotation keys. Buildingidentitysurfaced a real bug in the naive first version: resolving a DID viawf_agent_resolve_didsendscom.atproto.identity.resolveDidthrough whichever PDSservicewas given, and at least one major production PDS (bsky.social) gates that behind auth -- wrong for a general "resolve any DID" tool, since resolution should not depend on being able to log into the repo's own host. Fixed by addingwf_did_resolve_raw(identity.h/.c): same cache and did:plc/ did:web dispatch aswf_did_resolve, but returns the full untrimmed document as raw JSON instead of the fieldswf_did_documentkeeps, and resolves directly against the DID's own authority (PLC directory / did:web well-known) regardless ofservice.plc log <did>is new SDK surface too (wf_plc_get_audit_log, GET{plc_directory}/{did}/log/audit) plus a CLI diff of handles/rotation keys/PDS endpoint/signing key between consecutive operations -- normalizes the pre-2022 legacy genesis format (type: "create", singularhandle/service/signingKey/recoveryKeyfields instead of the modern array/nested shape) so real accounts whose audit log starts with a legacy op still diff correctly instead of showing spurious "(none) -> X" on the first real operation. Verified end to end against live production data (jay.bsky.team's actual repo, identity, and 4-operation PLC history, including its own legacy-format genesis op).
- The "full" CI job now passes
BSKY_HANDLE/BSKY_PASSWORD/BSKY_SERVICEthrough totest_examples_livefrom repo secrets (.github/workflows/ci.yml), pointed at MetalBear's own dev PDS (ewan.bear1.croft.clickon bear1.croft.click) rather than production Bluesky -- once the three secrets are provisioned, every push to main becomes a live smoke test of that deployment, not just an offline build. Still SKIPs cleanly (and CI still passes) on a fork or before the secrets are set. - Continue evaluating upstream C libraries for server-side infrastructure (event loop, config parsing).
- Generated typed-wrapper coverage tracks every query/procedure NSID in
the full lexicon corpus (319 definitions across
com.atproto,app.bsky,chat.bsky,tools.ozone,internal.bsky) has codegen inatproto_lex.h/atproto_lex.c. JSON endpoints have generatedwf_lex_..._main_callwrappers; binary-body procedures intentionally expose their NSID constants and use the dedicated upload/import APIs so codegen never emits a body-less call that violates the wire schema. Symbols are referenced by application code somewhere outside that codegen file itself — verified by deriving each endpoint's expected symbol name from its NSID and grepping the whole tree, not by sampling. Typed output-decoder coverage is the separate, narrower concern tracked by item 61 above (265/319 — the rest have no JSON body to decode). - Service-auth verification middleware for the XRPC server is landed
(
wf_xrpc_server_set_auth_middleware,xrpc_server_auth.h): it resolves the issuer's signing key from its DID document, enforcesaud/lxmbinding, retries once on signature failure for rotated keys, and falls back to DPoP-bound OAuth user tokens. Issuer fragments are handled per upstreamverifyServiceJwt:iss#atproto_labelerselects the#atproto_labelverification method and any other issuer selects#atproto(wf_did_resolve_verification_key). Per-route principal policies (wf_xrpc_server_auth_config_require_principal) are landed too: a SERVICE rule guards routes that must never accept OAuth user credentials, a USER rule guards routes that must never accept service tokens, and an ANY rule overrides a broader rule (longest-prefix wins; any matching rule also protects its prefix). - The blob store (item 58) is wired into the full PDS write path in
MetalBear:
metalbear_blob_store_associate/_dissociate/_is_referencedtrack which record URIs reference each blob, and createRecord/putRecord/deleteRecord/applyWrites keep that bookkeeping current, deleting a blob outright the moment no record references it (mirroring the reference PDS'srecord_blob+deleteDereferencedBlobs). See the MetalBear repository'sblob_store.h/repo_store.cand its AGENTS.md "Repo writes" section for the same-CID-across-replacement invariant. Blob mimetype/size limits (accept/maxSizeon a lexicon'stype: blobfield) were already enforced bywf_validate_record(src/validate/validate.c) — the same place any other schema constraint is checked, and the correct place per the reference PDS, which enforces these "when the reference is created" rather than at upload time; that had SDK-level tests (test_validate.c) but no end-to-end proof that MetalBear's write handlers actually reject an out-of-bounds blob against the realapp.bsky.embed.imageslexicon —test_repo_store.c'srun_blob_constraint_validationnow covers that. Optional at-rest encryption of the stored blob payload bytes (analogous toWOLFRAM_BUILD_STORE_CRYPTO) is now implemented in MetalBear behind theMETALBEAR_BUILD_BLOB_CRYPTOoption — file-backed payloads are sealed with libsodiumcrypto_secretbox_easy(XSalsa20-Poly1305) under acrypto_pwhash(Argon2id) key derived frommetalbear_blob_store_set_crypto_passphrase, with a per-store salt in<dir>/.blobstore.salt; see the MetalBearblob_store.h. Off by default. app.bsky.notification.putPreferencesV2andgetPreferencesshare the fully typed 13-slotdefs#preferencesrepresentation. The legacy v1putPreferencesendpoint carries only its requiredpriorityboolean;wf_agent_put_notification_prioritytransmits that exact schema.- Wii HTTPS and WebSocket, and Wii U platform/transport, are real and
cross-build-verified (though unverified on physical hardware — see items 59
and 60). Wii secp256k1 is now implemented via mbedTLS (which supports
secp256k1 natively). The 3DS is now cross-build-verified too: it shares the
desktop/Wii U libcurl transport (linked against devkitPro's
3ds-curl+3ds-mbedtls+3ds-zlibportlibs) instead of a bespoke httpc one, so there is no undefinedwii_tls_*at consumer link time. P-256 key generation and ECDSA signing are backed by a CTR-DRBG seeded from the console's hardware RNG (sslcGenerateRandomData) insrc/crypto/3ds_random.c, so — unlike the Wii and the Wii U — the 3DS does not require an application-provisioned entropy seed. 3DS platform primitives (LightLock mutex, osGetTime clock) are real. Cross-build verified (library plus a linked.3dsxhomebrew) but, like the other consoles, still unverified on physical hardware. Seesrc/platform/3ds_platform.c,src/crypto/3ds_random.c, andinclude/wolfram/3ds.h. - Sync v1.1 (ordered
com.atproto.sync.getRepoCAR block ordering, and partial repo export by collection) surveyed on request and deliberately not started: neither has a stable upstream target yet. The reference PDS's own ordered-CAR-export work exists but "is not performant enough to merge" per the relay-updates blog post, and that same post says the exact ordering "needs to be described more formally to ensure interoperation" — there is no wire format to cross-reference yet, which point 5 of this file's Technical philosophy requires. Partial sync-by-collection is listed in the sync spec as a "likely" future direction, with implementation "not yet started" upstream. Revisit once bluesky-social/pds actually merges ordered export and the spec text stabilizes, rather than building against a moving target now.
- cJSON — vendored via CMake FetchContent.
- libcbor — vendored via CMake FetchContent for RFC 8949 parsing and serialization primitives.
- OpenSSL (libcrypto) — for SHA-256 hashing.
- libsecp256k1 — for secp256k1 signing on desktop. Console targets use mbedTLS's built-in secp256k1 support instead.
- Classic Mac OS 9 XRPC transport — Open Transport TCP through the macTLS async stream, cooperative pumping/yielding, bearer authentication with one automatic refresh-and-retry, DPoP-Nonce capture, Content-Length/chunked response handling, bounded buffering on every response path, and HTTP GET/POST support. Exposed as the standalone
wolfram-macos9-transporttarget, compiled as strict C89 for CodeWarrior and covered by offline tests through the handler seam. The full SDK still needs a dedicated Mac OS 9 crypto backend before the complete library can replace the desktop/embedded crypto selection; CI verifies the C89 dialect and the transport's own logic, but a native CodeWarrior build and a real Mac OS 9 runtime test are still outstanding.