August 12, 2026 at 12:00:00 AM UTC

wolfram's server goes multithreaded, and the Wii build gets un-broken

wolframatprotocsdkwii

Wolfram

Since the multi-tenant/Wii U server post, the server (src/server/xrpc_server.c) and client transport (src/transport/xrpc.c) got the bulk of the attention, with a smaller but concrete batch of Wii/embedded work alongside it.

the server goes multithreaded

wf_xrpc_server_start previously ran a fixed thread count of 4 regardless of the host. It now passes MHD_OPTION_THREAD_POOL_SIZE through to libmicrohttpd when thread_count > 1, and when thread_count == 0 auto-sizes to CPU count × 2 (capped at 8 above 32 cores) instead of the old hardcoded 4. Making a real thread pool safe meant auditing every piece of shared server state: the route table and rate-limit buckets both gained a pthread_mutex_t, route lookups were split into an unlocked _locked internal variant plus a locking public wrapper (to avoid deadlocking against registration paths that already call the _locked form directly), and the bundled mhd_shim.c test double had its varargs parsing fixed — it had been consuming a fixed two-pointer width per option regardless of that option's actual arity, which would have misaligned the va_list the moment a single-unsigned int option like THREAD_POOL_SIZE was added. test_xrpc_server_parallel now fires 16 concurrent requests at a 4-thread server and checks response integrity.

The client transport got the matching treatment: wf_xrpc_client now holds a mutex around its mutable fields, and a config-snapshot mechanism (wf_client_snapshot/wf_config_free) stops a concurrent setter call from tearing down strings out from under an in-flight request. On top of that, wf_xrpc_query_async/wf_xrpc_procedure_async issue requests on a worker thread and return a wf_xrpc_pending handle, with wf_xrpc_pending_wait/_result/_cancel/_free for lifecycle management. AGENTS.md picked up a new numbered rule (9) codifying the locking discipline — every new server-side data structure has to document it and use the _locked-plus-public-wrapper pattern.

rate limiting, finished properly

A chain of four commits on 2026-08-04 turned rate limiting from partially-wired to actually enforced. wf_xrpc_server_set_route_rate_limiter had been dead code since it was added: the setter stored its entry in server->rate_limit_entries, but request dispatch only ever consulted the single global limiter, so calling the public API compiled, ran, and changed nothing. fix(xrpc_server): actually enforce per-route rate limiters wires wf_server_find_route_rate_limiter into the dispatch path ahead of the global check, and fixes the route key format along the way (the setter had been storing the bare URL instead of "GET:/xrpc/io.example.ping", which would have collided a GET and POST to the same path).

Two more pieces filled out the picture: wf_xrpc_request.client_ip exposes the requester's address to handlers for the first time — previously only the rate limiter's own MHD_get_connection_info call had it, so a handler had no way to build the "<identifier>-<ip>" compound key the reference PDS's createSession limiter uses — and 429 responses now carry RateLimit-Limit/Remaining/Reset/Policy headers via a new wf_rate_limiter_consume_status, matching the reference implementation's behavior (including its choice of an absolute Unix timestamp for Reset, not the relative seconds-until-reset the header's name might suggest). A week later, feat(server): opt-in trusted client-IP header for reverse-proxy deployments addressed the flip side: behind a reverse proxy, the raw TCP peer address is always the proxy's own address, which collapses every real client into one shared rate-limit bucket. wf_xrpc_server_set_trusted_client_ip_header lets an operator trust a specific header (CF-Connecting-IP and similar) instead — off by default, and documented as safe only when the proxy topology guarantees the header can't be client-forged.

auth middleware: per-route principal policies and labeler keys

wf_xrpc_server_auth_config_require_principal adds longest-prefix-wins rules restricting which credential kind — service JWT or OAuth user token — may authorize a given NSID prefix: service-only for tools.ozone moderation routes, user-only for app.bsky.feed, with a WF_XRPC_PRINCIPAL_ANY rule available to carve out an override. Separately, the service-auth default resolver previously always fetched a DID doc's #atproto key regardless of the token issuer, which meant a labeler service token (iss = did:...#atproto_labeler, signed with #atproto_label) could never authenticate. wf_did_resolve_verification_key is a new identity helper that resolves any named verification method, and the default resolver now strips the issuer's service fragment and picks the matching key — #atproto_label for an atproto_labeler issuer — mirroring upstream verifyServiceJwt.

A related fix bounded the transport's own patience: wf_xrpc_perform_cfg's libcurl path had no timeout at all, so a peer that accepted a connection but never responded could hang a request indefinitely. It now sets a 10s connect timeout plus a low-speed-abort (1 byte/sec sustained for 30s) rather than a blanket total timeout, so slow-but-progressing blob uploads aren't cut off. The DNS side got a bound too — ares_queue_wait_empty was waiting forever on a DNS server that accepts a query but never replies; now capped at 5s.

Smaller server pieces: WebSocket sends can now distinguish a genuinely broken peer from one that's merely stalled — wf_xrpc_server_ws_send returns WF_ERR_TIMEOUT specifically when the peer's receive window stays full for the whole write timeout, matching the reference firehose's ConsumerTooSlow distinction rather than collapsing both cases into WF_ERR_NETWORK. xrpc_server.c's WebSocket implementation (handshake, framing, upgrade-worker thread) was split out into its own xrpc_server_ws.c, with the shared struct layouts moved to a new xrpc_server_internal.h.

Wii: a build break, then three real fixes

The Wii/embedded side was smaller this cycle but not empty. feat(transport): implement RFC 6455 WebSocket client for the Wii replaced the honest-stub websocket_wii.c with a real client built on wii_tls (mbedTLS + verified certificate chain over lwIP): the HTTP/1.1 Upgrade handshake with Sec-WebSocket-Accept verification, masked client framing per RFC 6455 §5.2/§5.3, fragmented-message reassembly, and ping/pong — wss:// only, since the Wii has no non-TLS transport in this SDK and every AT Protocol subscription endpoint is wss anyway. The mask key and Sec-WebSocket-Key come from the same DRBG that seeds TLS and P-256 signing. A devkitPPC cross-build caught that the prior stub had been missing wf_websocket_send_ping as a defined symbol entirely — exactly the class of link-time gap the project's cross-build guidance exists to catch.

Then a real regression: fix(wii): repair a severed function signature that broke the Wii/Wii U build found that wf_sign's signature line and part of wf_b58_decode's for-loop body in crypto_wii.c had been spliced together since the secp256k1/mbedTLS commit landed — wf_b58_decode's error branch got replaced by an orphaned copy of wf_sign's SECP256K1 signing block, and wf_sign itself lost its return type, name, and SECP256K1 branch. It didn't show up in CI because crypto_wii.c isn't compiled in the desktop build, and nothing had cross-built Wii U since the commit that introduced it — a devkitPPC cross-compile is what surfaced and confirmed the fix. In the same pass, RAND_bytes in openssl_compat.c was implemented for real on Wii/Wii U — it had always returned failure — by delegating to the mbedtls_ctr_drbg_context that wii_tls.c/wiiu_random.c already seed from hardware entropy for TLS and P-256 signing. Nothing currently calls it (every RAND_bytes caller in the tree is excluded from embedded builds), but it no longer dead-ends. 3DS still has no seeded DRBG wired up and keeps failing honestly rather than faking success.

a new, non-console embedded target: Raspberry Pi 1 / Zero

.devdeps/rpi1.cmake adds an ARMv6 cross-compile toolchain for the Pi 1B/Zero — unlike the Wii/Wii U/3DS toolchains, this is a plain Linux server target, not a stripped-down console client: WOLFRAM_BUILD_SERVER works normally, since the point is to run MetalBear itself on the hardware, not just link the SDK. The toolchain file is deliberate about -march=armv6zk rather than plain armv6 or a generic "armhf" flag set: the ARM1176JZF-S needs the "zk" extensions for LDREXD/STREXD, which is what lets 64-bit atomics (MetalBear's request-metrics counters, wolfram's DID-cache refcounts) compile to inline instructions instead of falling back to libatomic calls — a generic Debian/Ubuntu armhf toolchain targets ARMv7 and would produce a binary that SIGILLs on real Pi 1B/Zero hardware. Not yet build-verified: no ARMv6 cross-compiler or Pi-1-compatible rootfs was available to actually exercise it.

state of things

The multithreaded-server and rate-limiting work reads as MetalBear-driven — per-endpoint rate limits and the reverse-proxy client-IP header both showed up because MetalBear needed the primitive to actually work, not as speculative hardening. The Wii build-break find is a reminder that a console target with no CI cross-build gate can silently rot: the spliced crypto_wii.c signature sat broken for a week before a cross-compile happened to catch it.


all entries