Skip to content

Expose the API schema at runtime via a stable C entry point #95

Description

@gmelodie

Problem

A loaded .so can't be asked "what's your API?" at runtime. generateCddlSchema/generateCddlBindings (ffi/codegen/cddl.nim) already produce a complete CDDL document from ffiProcRegistry/ffiTypeRegistry (per-type rules, per-proc request envelopes, request/response rules), but it's only emitted to a file at codegen time (genbindings_cddl). Nothing surfaces it at runtime.

Proposal

The CDDL string is fully determined by the compile-time registries, so it can be baked into the library as a const and returned through a C export.

6a — bake the schema as a const. At genBindings() time (runs in the library's own compilation), emit const ffiSchemaCddl* = "..." and const ffiSchemaCbor* = [...] (a CBOR descriptor: {lib, version, requests:[{name, reqFields, respFields}], events:[{wireName, payloadFields}], abi}). Factor the registry→descriptor step out of generateCddlSchema so the file emitter and the const emitter share it; add a CBOR renderer.

6b — C exports (static, borrowed, callable before create):

const char* <lib>_get_schema_cddl(void);                       // NUL-terminated, never freed
void        <lib>_get_schema_cbor(const uint8_t** out, size_t* outLen);
uint64_t    <lib>_schema_version(void);                        // stable hash for ABI-mismatch detection

schema_version = a deterministic hash over sorted proc/type names + field types.

6c — wrapper surface. Thin schema()/getSchema() accessor per wrapper (rust/cpp/python/go) returning the string/parsed descriptor. Cheap once the C export exists.

Gotchas

  • The descriptor must reflect the runtime ABI mode — if the C-wire ABI lands (Minor: Fixed indentation #2), include a per-proc abi field (wire FFIProcMeta.abiFormat, already present).
  • Keep it borrowed/static — no per-call alloc, no ownership API for a read-only constant.
  • Hash stability: CBOR maps are keyed, so sort before hashing; don't include field order unless order is part of the wire contract.

Tests

  • Load the built timer lib; assert get_schema_cddl() equals the checked-in examples/timer/cddl_bindings/my_timer.cddl (ties into check_bindings).
  • Round-trip: parse get_schema_cbor(), assert it lists every {.ffi.} proc/event in the example.
  • schema_version stability: identical source → identical hash across builds and orc/refc.

Effort: small-medium. The registry walk already exists in cddl.nim; mostly "render to a const + add three trivial C exports."

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions