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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions