Single source of truth for every Sphereon-owned REST API specification across the three product layers:
- IDK: Identity Development Kit (open-core SDK).
- EDK: Enterprise Development Kit.
- VDX: Verifiable Data Exchange platform.
Dependencies run VDX -> EDK -> IDK, and an API placed in a lower layer is reachable from the
higher ones. Each spec records which products include it via targets in manifest-catalog.json.
When targets is omitted the spec is included in all three. The default is inclusive (EDK consumes
IDK, VDX consumes EDK and IDK); a lower-layer API is excluded from a higher product only when that
product replaces it with its own (for example the IDK oid4vp-universal session spec is IDK-only,
because the EDK/VDX oid4vp-verifier spec reuses its endpoints by $ref).
Third-party reference specs (vendor APIs we integrate but do not own) live under external/ and
are excluded from aggregation, linting, and release tags.
A single flat folder (the repo root) holds every spec, every component bundle, and the one shared
common-components.yml. There are no product subfolders and no synced copies. Because every file
is in the same directory, each cross-file $ref (./common-components.yml#/...,
./<domain>-components.yml#/...) resolves with no tooling.
common-components.yml the one canonical shared bundle (errors, pagination, security, tenant headers)
<domain>-openapi.yml an API spec (a few use the .yaml extension)
<domain>-components.yml externalized entity/enum/parameter bundle for a spec (e.g. did, kms, dcql)
external/ vendor reference specs (not aggregated)
manifest-catalog.json spec -> owning module, division label, and targets
docs-groups.json capability grouping for the docs sidebar (independent of layout)
redocly.yaml lint config
A spec keeps its reusable models in a sibling <domain>-components.yml and $refs them
(./<domain>-components.yml#/components/schemas/X); the main spec re-exports them as thin stubs. A
higher layer extends a lower layer's API by $refing the same component bundle plus the base
spec's path items, then adding its own, with no duplication. The dcql trio
(dcql-openapi.yml -> dcql-edk-openapi.yml -> dcql-vdx-openapi.yml) is the worked example.
Why one flat folder: the OpenAPI generator used by the consuming Kotlin Multiplatform builds resolves cross-subdirectory parameter
$refs unreliably, andcommon-components.ymlis referenced as parameters on nearly every path. A single folder keeps every$refsame-directory, the one layout the generator handles uniformly, and removes the sync step.
Two machine-readable fields on an operation drive the badges rendered in the documentation and the live console. Both are the single source; the badges are derived at build time.
x-products: the products an operation is available in, as[idk, edk, vdx]. An operation-level value travels with the operation when another spec reuses it by$ref. When absent, the operation falls back to the spec-levelinfo.x-products, then to the spec's catalogtargets.x-license-protection: the license descriptor that gates an operation.entitlementKeyis the canonicalproduct.module.service.commandkey.deploymentShapePredicates,protectionMode,quotaKeys, anddomainPolicyRefdescribe where and how enforcement applies.product,module,service, andcommandmay be supplied only as explicit consistency fields when a consumer needs them; otherwise they are derived fromentitlementKey.
Each consuming repository includes this repo as a git submodule and pins it by commit. Today that is five checkouts: the IDK, EDK, and VDX product builds, the documentation site, and the wallet frontend. After cloning a consuming repo:
git submodule update --init --recursive
A build reads the specs from its submodule checkout in place; there is no copy-into-module step. When the consumers pick up new specs, advance every checkout to the same commit in lockstep so the products, docs, and frontend stay on one version of the contracts.
targets governs which products' documentation include a spec and the default product-availability
badge. It does not, by itself, decide what a running service serves: the live /openapi console
advertises only the specs whose declared paths are actually mounted in that build, determined by
route-table introspection. When two specs describe the same mounted endpoints (a layered base and
the product spec that reuses it by $ref), the console keeps the more representative one.
This repo defines its own release-tag namespace (v0.25, v0.26, and so on), independent of any
product repository's tags. A tag is a release coordinate; an individual spec's info.version is
independent of it. Documentation snapshots and product builds reference these tags.
npx @redocly/cli lint must pass. The no-unresolved-refs gate proves every same-directory $ref
resolves. Remaining stylistic warnings are not release blockers.