The specification¶
The normative text lives in this directory: six core chapters and three modules, 313 requirements. A chapter appears here when it is written rather than as a stub that promises it will be, so the tables below are the state of the specification rather than a plan for it.
../docs/roadmaps/spec-0.1.md is what remains before the 0.1
release.
How to read a chapter¶
- RFC 2119 / RFC 8174. Only the uppercase keywords are normative:
MUST,MUST NOT,SHOULD,SHOULD NOT,MAY, and their listed synonyms. A lowercase "must" is ordinary English. - Every normative statement carries a requirement ID and its own anchor, so it can be cited from a conformance test, an implementation note or an OpenAPI operation:
SPS-CRUD-011 → spec/core/lod-crud.md#SPS-CRUD-011
IDs are permanent. They are never reused, never renumbered, and a requirement that is dropped is marked withdrawn rather than deleted — the same promise the vocabulary makes for RDF terms, because an ID ends up in other people's test suites the way a term ends up in other people's data. - Standards are named, not re-explained. A chapter states which RFCs it profiles and specifies the deviations. If a behaviour follows the profiled standard exactly, the chapter is silent about it on purpose. - Prose is not normative. The paragraphs around the requirements exist to make them readable. Where prose and a requirement seem to disagree, the requirement is what binds.
The authoring rules are in ../docs/agents/spec-authoring.md.
The requirement index¶
../requirements.json is the machine-readable form: every identifier, the
part it belongs to, the chapter it lives in, its first sentence, and whether it is withdrawn. A
withdrawn one also carries its withdrawal note and, where it has a successor, that identifier —
because the obligation is what makes a permanent identifier recognisable, and the withdrawal
preamble sits in front of it. It is
generated from the chapters and committed, and CI fails if the committed copy has drifted.
A version per part, not one for the file. Core and each module version independently
(SPS-CORE-005), so versions carries all of them and every
requirement names its part rather than repeating a number that could then disagree with it.
specVersion remains core's, because that is what a consumer pins to say which specification it
implements. The module set comes from the chapters in modules/ rather than from a
list, and CI fails if a module has no version or a version has no module — two registries that must
agree are two registries somebody keeps in step by remembering to. They are all equal today, which is exactly why the shape is settled now: adding the
field after somebody has vendored the file is a change every consumer has to absorb.
It exists for the consumer that is another repository. The reference implementation vendors it, so a
note in its code citing SPS-GRANT-007 can be checked without a network call — and upgrading to a
newer specification arrives there as a reviewable diff rather than as a build that starts failing.
It deliberately carries no URL and no timestamp. A URL would pin a branch or a tag and the consumer is the one who knows which it wants; a timestamp would turn every regeneration into a diff.
Core¶
Every sempods implementation provides all of it. There is no opt-out and no partial core.
| Chapter | Area | Status | Source being extracted from |
|---|---|---|---|
core/index.md — conformance, addressing, discovery, the error model |
CORE |
present | new |
core/contexts.md — the context as the permission boundary, the _system/contexts namespace, lifecycle and discovery |
CTX |
present | docs/auth/authorization.md |
core/grants.md — #read / #write / #manage, resolution, delegation, revocation, the #manage subtree rule |
GRANT |
present | docs/auth/authorization.md |
core/auth.md — OAuth 2.1, PKCE, the three client-identity shapes, consent, refresh rotation, discovery |
AUTH |
present | docs/auth/oauth.md, service-clients.md |
core/lod-crud.md — the LOD layer and the system layer, ?context=, base64url addressing, slots and edges |
CRUD |
present | docs/lod-crud/ |
core/sparql.md — the read-only query surface and the server-enforced sandbox |
SPARQL |
present | new writing — was scattered across three documents |
core/find.md — retrieval: the request, the sandbox, the response graph |
FIND |
present | new writing — existed only as a concept |
Two of these were new writing rather than a move. sparql existed only as fragments across three
files in the reference implementation — whose LOD chapter deferred to a "SPARQL surface" document
that was never written — and find existed only as a concept, which mixes what is built with what
is planned. Both chapters therefore carry more than any single source did, and both were checked
against the implementation rather than against the prose.
Modules¶
Optional, versioned separately from core and from each other. An implementation advertises which modules it provides; a module is all-or-nothing, because a client that has to probe which half it got has no contract.
| Module | Area | Status | Source being extracted from |
|---|---|---|---|
modules/oidc.md — the OIDC bridge: identity assertions, how a pod obtains one, federation |
OIDC |
present | docs/auth/identity.md, split |
modules/media.md — pod-owned binaries: content addressing, the context-bound registry, delivery, lifecycle |
MEDIA |
present | docs/media.md |
modules/mcp.md — the per-pod MCP endpoint, authentication modes, the tool catalogue, closed schemas |
MCP |
present | docs/mcp/endpoint.md, tools.md, authentication.md |
OpenAPI¶
../openapi/ holds hand-written OpenAPI 3.1 descriptions — one for the whole of
core, one per module that has an HTTP surface of its own. Hand-written and part of the contract, not
generated from any implementation: a generated description specifies whatever the reference
implementation happens to do, and an implementer in another language would read one
implementation's choices as obligations.
Every operation names the requirements it realises, and a citation pointing at an identifier no chapter defines fails CI.
What OpenAPI cannot carry is the reason it is not the specification on its own: grant resolution, the context sandbox, the SPARQL sandbox — the behaviour that decides what a request is allowed to see. That lives in the chapters.
Vocabulary¶
../vocabulary/ holds the RDF terms published under
https://schema.sempods.org/, together with the stability guarantees they carry — the strongest
promise this project makes: an IRI never changes, a meaning never narrows, and a retired term is
deprecated for at least twelve months rather than deleted.
It lives here rather than with an implementation because the terms are normative and versioned with the specification, and because a term ends up inside other people's stored data — which is the same reason a requirement identifier is permanent.
Conformance¶
conformance/ will hold the suite that turns a requirement ID from a claim into a check, and it is
what the reserved terms "sempods conformant" and "sempods certified" are gated on. Neither
exists, so nobody can pass it and the terms say nothing today. Write "implements the sempods
specification" and document your deviations.