|
| 1 | +// SPDX-License-Identifier: PMPL-1.0-or-later |
| 2 | +// Copyright (c) 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk> |
| 3 | +// (MPL-2.0 is the automatic legal fallback until PMPL is formally recognised) |
| 4 | += Unified Zig API Stack — Authoritative Playbook |
| 5 | +:toc: |
| 6 | +:toc-placement: preamble |
| 7 | +:doctype: article |
| 8 | + |
| 9 | +The canonical reference for every Zig-edge repo wiring into the |
| 10 | +hyperpolymath API stack. Read this before designing or wiring any |
| 11 | +Zig-facing service boundary. |
| 12 | + |
| 13 | +== TL;DR |
| 14 | + |
| 15 | +The unified-zig-api stack replaces the retired haxadeca-v-api / unified-v-api / |
| 16 | +v-grpc / v-rest / v-zig-ffi / v-idris-abi family. It is organised as four |
| 17 | +vertically-stacked layers: |
| 18 | + |
| 19 | +[none] |
| 20 | +- *Layer 1 — Idris2 core* (`verification-ecosystem/proven/`): formally verified |
| 21 | + primitive operations; the ultimate source of trust. |
| 22 | +- *Layer 2 — Idris2 ABI* (`developer-ecosystem/zig-api/src/ZigApi/ABI/`): dependent-type |
| 23 | + proofs of the C ABI surface — tag round-trips, safe-path soundness, injectivity, |
| 24 | + memory layout. |
| 25 | +- *Layer 3 — Zig runtime* (`developer-ecosystem/zig-api/ffi/zig/src/`): C-ABI |
| 26 | + implementations of core, process, gnosis, and connector subsystems; |
| 27 | + exports the `uapi_*` symbol set. |
| 28 | +- *Layer 4 — C adaptor* (`developer-ecosystem/zig-api/generated/abi/zig_api.h`): |
| 29 | + auto-generated from the Idris2 ABI; consumed by any language that speaks C FFI. |
| 30 | + |
| 31 | +Edge repos consume from this stack only. Never re-roll equivalent functionality |
| 32 | +inline. |
| 33 | + |
| 34 | +== The Four Layers |
| 35 | + |
| 36 | +=== Layer 1 — Idris2 core (`verification-ecosystem/proven/`) |
| 37 | + |
| 38 | +*What it is.* A formally verified safety library: 104 modules in |
| 39 | +`src/Proven/` written with `%default total`, zero `believe_me`, zero |
| 40 | +`assert_total`. Every function is proved to terminate, cover all cases, and |
| 41 | +return a value — no runtime exceptions possible. |
| 42 | + |
| 43 | +*Canonical entry points for consumers.* |
| 44 | + |
| 45 | +[cols="1,3"] |
| 46 | +|=== |
| 47 | +| Path | Purpose |
| 48 | + |
| 49 | +| `core-only.ipkg` |
| 50 | +| Minimal package — import only the Idris2 proofs, no FFI dependency. |
| 51 | + Preferred when you need proof witnesses in your own Idris2 ABI layer. |
| 52 | + |
| 53 | +| `api/` |
| 54 | +| High-level module surface for direct Idris2 consumers. |
| 55 | + |
| 56 | +| `bindings/` |
| 57 | +| Language-specific thin wrappers (18 complete, 102 scaffolded). |
| 58 | + C, Rust, Python, ReScript bindings are production-ready; |
| 59 | + others are research-stage. |
| 60 | + |
| 61 | +| `ffi/zig/src/main.zig` |
| 62 | +| Zig bridge: wraps Idris2 RefC output, exposes `proven_ffi_*` C symbols. |
| 63 | +|=== |
| 64 | + |
| 65 | +*CRG grade:* C (Beta) — core proven, Rust/Python/C bindings tested; 102 |
| 66 | +targets scaffolded. Never reimplement logic in bindings; call Zig FFI only. |
| 67 | + |
| 68 | +=== Layer 2 — Idris2 ABI (`developer-ecosystem/zig-api/src/ZigApi/ABI/`) |
| 69 | + |
| 70 | +*What it is.* Dependent-type proofs of the C ABI surface shared with the Zig |
| 71 | +runtime. These proofs run at compile time; any ABI drift is a type error. |
| 72 | + |
| 73 | +*Modules present.* |
| 74 | + |
| 75 | +[cols="1,3"] |
| 76 | +|=== |
| 77 | +| File | Content |
| 78 | + |
| 79 | +| `Types.idr` |
| 80 | +| `Platform`, `Result` codes (11 variants), `Handle`, `Slot`, `ValidSlot`, |
| 81 | + platform pointer-width claims. |
| 82 | + |
| 83 | +| `Http.idr` |
| 84 | +| `Method` (7 variants), `ServerState` (4 variants), `HealthStatus`, |
| 85 | + `RouteDescriptor`. Includes `methodTag`, `methodFromTag`, round-trip proofs. |
| 86 | + |
| 87 | +| `Process.idr` |
| 88 | +| `IsSafePath` structural proof, `ExecResult`, `GnosisRenderCmd`. |
| 89 | + A `GnosisRenderCmd` cannot be constructed for an unsafe path without |
| 90 | + discharging the `IsSafePath` proof — path safety is mandatory, not advisory. |
| 91 | + |
| 92 | +| `Connector.idr` |
| 93 | +| `ServiceId` (11 variants, 0–10), `ConnectorState` (6 variants), |
| 94 | + `GrooveManifest`. `serviceIdTag` + `serviceIdFromTag` with round-trip proof. |
| 95 | + |
| 96 | +| `Layout.idr` |
| 97 | +| Machine-checked memory layout claims: tag-count bounds (all enums fit in u8), |
| 98 | + pool/sentinel bounds (capacity 64, sentinel 255, 64 < 255), |
| 99 | + `@sizeOf` claims (handle = 8 bytes, slot = 1 byte). |
| 100 | + |
| 101 | +| `Proofs.idr` |
| 102 | +| Cross-cutting proofs: slot validity (sentinel 255 cannot build `ValidSlot`), |
| 103 | + round-trip completeness across all five enum types, `SafePath` soundness |
| 104 | + (empty allowlist denies; head-match accepts), health status contract |
| 105 | + (200/503 codes, distinctness, 5xx range). |
| 106 | + |
| 107 | +| `Foreign.idr` |
| 108 | +| `%foreign` declarations for every `uapi_*` symbol in the Zig runtime, |
| 109 | + plus safe `IO` wrappers. This is the Idris2 entry point for consumers. |
| 110 | +|=== |
| 111 | + |
| 112 | +*Package file:* `zig-api/zig-api.ipkg` — includes all seven ABI modules. |
| 113 | + |
| 114 | +=== Layer 3 — Zig runtime (`developer-ecosystem/zig-api/ffi/zig/src/`) |
| 115 | + |
| 116 | +*What it is.* Native C-ABI implementation of the API surface. Built with |
| 117 | +Zig 0.15.2. Produces `libzig_api.so` (shared) and `libzig_api.a` (static). |
| 118 | + |
| 119 | +*Modules.* |
| 120 | + |
| 121 | +[cols="1,3"] |
| 122 | +|=== |
| 123 | +| Module | Responsibility |
| 124 | + |
| 125 | +| `lib.zig` |
| 126 | +| Root entry point. Re-exports all `uapi_*` symbols. Provides the three |
| 127 | + lifecycle functions: `uapi_init`, `uapi_teardown`, `uapi_version`. |
| 128 | + |
| 129 | +| `core.zig` |
| 130 | +| `Result` enum (mirrors `ZigApi.ABI.Types`), thread-local error storage, |
| 131 | + `Pool(T, N)` generic with mutex for fixed-capacity slot management. |
| 132 | + |
| 133 | +| `process.zig` |
| 134 | +| Safe-path validation (allowlist prefix match), `runProcess`, |
| 135 | + `runGnosis` (shells out to the `gnosis` binary on `$PATH`). |
| 136 | + |
| 137 | +| `gnosis.zig` |
| 138 | +| HTTP/1.1 server pool. Replaces v-grpc + v-rest. Handles `/render`, |
| 139 | + `/context`, `/health`, `/`, and gRPC-style `/gnosis.GnosisService/*` paths. |
| 140 | + `uapi_gnosis_{create,start,stop,destroy,state,health}`. |
| 141 | + |
| 142 | +| `connector.zig` |
| 143 | +| 64-slot HTTP connector pool for 11 hyperpolymath services. |
| 144 | + `uapi_connector_{create,health,call,destroy,state}`. |
| 145 | + `uapi_connector_call` is the universal request dispatcher. |
| 146 | +|=== |
| 147 | + |
| 148 | +*Build:* `just build` from `zig-api/`. Recipes: `build`, `test`, |
| 149 | +`check-abi`, `test-integration`. |
| 150 | + |
| 151 | +*Current version:* 0.15.2 (matches Zig toolchain; see `ffi/zig/build.zig`). |
| 152 | + |
| 153 | +=== Layer 4 — C adaptor (`developer-ecosystem/zig-api/generated/abi/zig_api.h`) |
| 154 | + |
| 155 | +The machine-readable contract between the Zig runtime and every non-Idris2 |
| 156 | +consumer. Auto-generated from the Idris2 ABI. *Never edit by hand.* |
| 157 | + |
| 158 | +Exposes: lifecycle (`uapi_init`, `uapi_teardown`, `uapi_version`), all |
| 159 | +`UAPI_OK`/`UAPI_ERR_*`/`UAPI_SERVER_*`/`UAPI_CONNECTOR_*`/`UAPI_SERVICE_*`/ |
| 160 | +`UAPI_METHOD_*` constants, and the full `uapi_gnosis_*` and |
| 161 | +`uapi_connector_*` function set. |
| 162 | + |
| 163 | +When the Idris2 ABI changes, the header must be regenerated — not patched |
| 164 | +manually. The regeneration pipeline is currently manual (see §10). |
| 165 | + |
| 166 | +== How to Consume |
| 167 | + |
| 168 | +=== From Idris2 |
| 169 | + |
| 170 | +Import `ZigApi.ABI.Foreign` (which re-exports all seven ABI modules). |
| 171 | +Link against `libzig_api` at build time via the `zig-api.ipkg` package. |
| 172 | + |
| 173 | +[source,idris] |
| 174 | +---- |
| 175 | +import ZigApi.ABI.Foreign |
| 176 | +
|
| 177 | +main : IO () |
| 178 | +main = do |
| 179 | + Ok <- uapiInit | _ => putStrLn "init failed" |
| 180 | + h <- gnosisCreate 8091 |
| 181 | + Ok <- gnosisStart h | _ => putStrLn "start failed" |
| 182 | + -- use the server |
| 183 | + gnosisStop h |
| 184 | + gnosisDestroy h |
| 185 | + uapiTeardown |
| 186 | +---- |
| 187 | + |
| 188 | +=== From another Zig project |
| 189 | + |
| 190 | +Option A — link the shared library and `@cImport` the header: |
| 191 | + |
| 192 | +[source,zig] |
| 193 | +---- |
| 194 | +const c = @cImport(@cInclude("zig_api.h")); |
| 195 | +
|
| 196 | +pub fn main() !void { |
| 197 | + _ = c.uapi_init(); |
| 198 | + defer c.uapi_teardown(); |
| 199 | + const slot = c.uapi_connector_create(c.UAPI_SERVICE_VERISIMDB, |
| 200 | + "http://127.0.0.1:8100"); |
| 201 | + defer c.uapi_connector_destroy(slot); |
| 202 | + // ... |
| 203 | +} |
| 204 | +---- |
| 205 | + |
| 206 | +Option B — add `libzig_api` as a `build.zig` dependency and import symbols |
| 207 | +directly from the Zig modules (internal use only; C ABI is the stable surface). |
| 208 | + |
| 209 | +=== From Rust |
| 210 | + |
| 211 | +Use `bindgen` to generate Rust bindings from `zig_api.h`, or use the |
| 212 | +scaffolded bindings in `verification-ecosystem/proven/bindings/rust/` |
| 213 | +if you also need proven primitives. Link against `libzig_api.so`/`.a`. |
| 214 | + |
| 215 | +=== From ReScript / JavaScript via Deno |
| 216 | + |
| 217 | +Use `Deno.dlopen` against `libzig_api.so` (Linux) to call `uapi_*` |
| 218 | +symbols directly. No Node bindings are available; Deno FFI is the path. |
| 219 | + |
| 220 | +=== From Julia |
| 221 | + |
| 222 | +Use `ccall` against `libzig_api.so`. Example: |
| 223 | +`ccall((:uapi_init, "libzig_api"), UInt8, ())`. |
| 224 | +Julia-specific wrappers are not yet written; `ccall` is the direct path. |
| 225 | + |
| 226 | +== Wiring a New Edge Repo |
| 227 | + |
| 228 | +A new Zig-edge repo is one that calls hyperpolymath services via |
| 229 | +`libzig_api` or extends the Idris2 ABI with repo-specific types. |
| 230 | + |
| 231 | +. *Create from template.* Clone `hyperpolymath/rsr-template-repo`, run |
| 232 | + `just init`, and customise placeholders. |
| 233 | +. *Add `libzig_api` to `build.zig`.* Reference the shared library output |
| 234 | + from `developer-ecosystem/zig-api/ffi/zig/` or install it system-wide via |
| 235 | + `just install` from the `zig-api` Justfile. A minimal skeleton: |
| 236 | ++ |
| 237 | +[source,zig] |
| 238 | +---- |
| 239 | +const zig_api = b.addSharedLibraryFromPath( |
| 240 | + .{ .name = "zig_api", .path = .{ .cwd_relative = PATH_TO_LIBZIG_API } }, |
| 241 | +); |
| 242 | +exe.linkLibrary(zig_api); |
| 243 | +exe.addIncludePath(.{ .cwd_relative = PATH_TO_GENERATED_ABI }); |
| 244 | +---- |
| 245 | +. *Place repo-specific Idris2 ABI proofs in `src/abi/` of the edge repo.* |
| 246 | + Import `ZigApi.ABI.Types` and `ZigApi.ABI.Connector` from `zig-api.ipkg` |
| 247 | + (add it as a dependency in the edge repo's `.ipkg`). Extend, do not fork. |
| 248 | +. *Use the Groove protocol* for inter-service communication wherever possible |
| 249 | + (see `standards/groove-protocol/`). `zig-groove-bridge` in |
| 250 | + `developer-ecosystem/zig-ecosystem/connectors/zig-groove-bridge/` is the |
| 251 | + reference consumer — study `src/main.zig` before writing your own discovery |
| 252 | + or attachment logic. |
| 253 | +. *Do not re-roll connector logic.* Call `uapi_connector_create` with the |
| 254 | + appropriate `UAPI_SERVICE_*` tag. All 11 services have pre-declared IDs. |
| 255 | + |
| 256 | +== Authoritative Component Links |
| 257 | + |
| 258 | +[cols="2,3"] |
| 259 | +|=== |
| 260 | +| Component | Canonical path |
| 261 | + |
| 262 | +| proven (Idris2 core) |
| 263 | +| `verification-ecosystem/proven/` |
| 264 | + |
| 265 | +| zig-api Idris2 ABI |
| 266 | +| `developer-ecosystem/zig-api/src/ZigApi/ABI/` |
| 267 | + |
| 268 | +| zig-api Zig runtime |
| 269 | +| `developer-ecosystem/zig-api/ffi/zig/src/` |
| 270 | + |
| 271 | +| zig-api C header |
| 272 | +| `developer-ecosystem/zig-api/generated/abi/zig_api.h` |
| 273 | + |
| 274 | +| zig-groove-bridge (reference edge) |
| 275 | +| `developer-ecosystem/zig-ecosystem/connectors/zig-groove-bridge/` |
| 276 | + |
| 277 | +| bridge-nginx-zig (skeleton) |
| 278 | +| `developer-ecosystem/bridge-nginx-zig/` |
| 279 | + |
| 280 | +| Groove protocol spec |
| 281 | +| `standards/groove-protocol/` |
| 282 | + |
| 283 | +| rsr-template-repo |
| 284 | +| `hyperpolymath/rsr-template-repo` (GitHub) |
| 285 | +|=== |
| 286 | + |
| 287 | +== Retirement Note |
| 288 | + |
| 289 | +The following repos are retired. Their content has been moved to |
| 290 | +`developer-ecosystem/v-ecosystem/v-api-interfaces/` for potential donation to |
| 291 | +the V community. Do not model any new work on them. |
| 292 | + |
| 293 | +* `developer-ecosystem/v-grpc/` |
| 294 | +* `developer-ecosystem/v-rest/` |
| 295 | +* `developer-ecosystem/v-zig-ffi/` |
| 296 | +* `developer-ecosystem/v-idris-abi/` |
| 297 | +* `developer-ecosystem/haxadeca-v-api/` |
| 298 | +* `developer-ecosystem/unified-v-api/` |
| 299 | + |
| 300 | +The trigger was the estate-wide V-lang ban on 2026-04-10 (V-lang detected via |
| 301 | +`v.mod` / `vpkg.json`; note: `.v` extension is Verilog, not V-lang). |
| 302 | + |
| 303 | +== Current Status |
| 304 | + |
| 305 | +[cols="1,1,3"] |
| 306 | +|=== |
| 307 | +| Component | Readiness | Notes |
| 308 | + |
| 309 | +| `proven` (Idris2 core) |
| 310 | +| ACTIVE |
| 311 | +| 104 modules, `%default total`, 0 `believe_me`, 49 tests. |
| 312 | + 18 bindings complete; 102 scaffolded. |
| 313 | + |
| 314 | +| `zig-api` (Idris2 ABI + Zig runtime) |
| 315 | +| PRODUCTION-ADJACENT |
| 316 | +| Idris2 ABI fully proved; Zig runtime builds and passes unit tests; |
| 317 | + integration tests require `gnosis` binary on `$PATH`. |
| 318 | + |
| 319 | +| `zig-groove-bridge` |
| 320 | +| PARTIAL |
| 321 | +| Dodeca-API + discovery logic written; attachment / full Groove manifest |
| 322 | + negotiation being unblocked. |
| 323 | + |
| 324 | +| `bridge-nginx-zig` |
| 325 | +| SKELETON |
| 326 | +| `build.zig` and directory structure present; HTTP proxy logic not yet written. |
| 327 | +|=== |
| 328 | + |
| 329 | +== Open Questions / Deferred |
| 330 | + |
| 331 | +* *proven -> zig-api active wiring.* `zig-api`'s `Foreign.idr` references |
| 332 | + `libzig_api` only. The narrative goal of calling `proven_ffi_*` symbols from |
| 333 | + within the Zig runtime (e.g. using `proven` SafeMath for buffer bounds in |
| 334 | + `gnosis.zig`) is described in `zig-api/EXPLAINME.adoc` but not yet wired. |
| 335 | + Until wired, proven and zig-api are parallel stacks that share the Idris2/Zig |
| 336 | + FFI pattern but do not chain. |
| 337 | + |
| 338 | +* *C adaptor auto-regeneration pipeline.* `zig_api.h` is marked |
| 339 | + `AUTO-GENERATED` but no `just` recipe or CI step currently regenerates it |
| 340 | + from the Idris2 ABI source. Manual sync is a drift risk. A `just gen-header` |
| 341 | + recipe invoking `idris2` + an extraction pass is needed. |
| 342 | + |
| 343 | +* *Template repo for new edges.* `rsr-template-repo` does not yet include a |
| 344 | + `build.zig` skeleton that depends on `libzig_api`. A `just new-zig-edge` |
| 345 | + subcommand in `scaffoldia` or a template overlay is the right long-term fix. |
0 commit comments