Linked Data
CDS v0.2.0 treats every event, source, and content type as a first-class resource on the web. This page explains why, how, and what that buys you.
Why Linked Data
Section titled “Why Linked Data”Tim Berners-Lee proposed four rules for publishing data on the web:
- Use URIs as names for things.
- Use HTTP URIs so that people can look those names up.
- When someone looks up a URI, provide useful information (using standards).
- Include links to other URIs so that they can discover more things.
The Tower of Babel problem. Different systems use different identifiers for
the same things. One lottery API calls the game megasena, another calls it
mega-sena, a third uses an opaque numeric ID. A weather API returns temp_c,
another returns temperature_celsius. The concepts are identical but the names
are not, so combining data from these systems requires hand-written mapping
code for every pair.
URIs solve this. When two systems refer to
https://signed-data.org/vocab/lottery-brazil/mega-sena-result they mean the
same thing — no mapping required.
Without Linked Data, JSON payloads are isolated silos. Each consumer must already know what every field means and where to get more information. Linked Data connects those silos: every field that references an external concept carries a dereferenceable URI that leads to a definition, and that definition links onward to related resources.
How CDS implements each rule
Section titled “How CDS implements each rule”Rule 1 — Use URIs as names for things
Section titled “Rule 1 — Use URIs as names for things”CDS assigns a stable URI to every entity in the system.
| Entity | URI pattern | Example |
|---|---|---|
| Event | https://signed-data.org/events/{uuid} | .../events/a3f1c9e0-7b2d-4e8a-9f01-abc123def456 |
| Source | https://signed-data.org/sources/{source-id} | .../sources/caixa.gov.br.loterias.v1 |
| Content type | https://signed-data.org/vocab/{domain}/{type} | .../vocab/lottery-brazil/mega-sena-result |
These URIs are not just opaque identifiers — they are addresses you can fetch.
Rule 2 — Use HTTP URIs
Section titled “Rule 2 — Use HTTP URIs”All CDS URIs use HTTPS and are served by signed-data.org. No custom URI
schemes, no URNs, no proprietary namespaces. Any HTTP client on any platform
can resolve them.
Rule 3 — Provide useful information when a URI is looked up
Section titled “Rule 3 — Provide useful information when a URI is looked up”Every CDS URI returns a JSON-LD document when dereferenced with an HTTP GET:
GET /sources/caixa.gov.br.loterias.v1 HTTP/1.1Host: signed-data.orgAccept: application/ld+json{ "@context": "https://signed-data.org/contexts/cds/v1.jsonld", "@id": "https://signed-data.org/sources/caixa.gov.br.loterias.v1", "name": "Caixa Econômica Federal — Loterias API v1", "url": "https://servicebus2.caixa.gov.br/portaldeloterias/api/megasena", "country": "BR", "domains": ["lottery-brazil"]}Rule 4 — Include links to other URIs
Section titled “Rule 4 — Include links to other URIs”Every CDS document contains outbound links:
- An event links to its source via
source.@id - The source document links to its domains (e.g.,
lottery-brazil) - The domains link to the vocabulary, which defines every content type and property
This creates a navigable graph. Starting from any event, you can follow links to discover what it means, where it came from, and what other data the same source publishes.
JSON-LD: why not RDF/Turtle
Section titled “JSON-LD: why not RDF/Turtle”Developer ergonomics.
JSON-LD is valid JSON. An existing consumer that reads JSON from an HTTP endpoint does not need a JSON-LD processor, does not need to learn Turtle syntax, and does not need to install an RDF library. The event payload looks like ordinary JSON:
{ "@context": "https://signed-data.org/contexts/cds/v1.jsonld", "@id": "https://signed-data.org/events/a3f1c9e0-...", "content_type": "https://signed-data.org/vocab/lottery-brazil/mega-sena-result", "occurred_at": "2026-03-29T00:00:00Z", "source": { "@id": "https://signed-data.org/sources/caixa.gov.br.loterias.v1" }}The @context is a single URL that maps snake_case keys to full RDF predicates
behind the scenes. For example, content_type maps to
https://signed-data.org/vocab/cds#contentType and occurred_at maps to
https://signed-data.org/vocab/cds#occurredAt. Consumers that do not care
about RDF ignore @context entirely and read the JSON as-is.
No Turtle. No SPARQL required for basic use. If you need RDF, any JSON-LD processor will expand the same document into N-Triples, Turtle, or RDF/XML.
The 5-star rating
Section titled “The 5-star rating”Tim Berners-Lee defined a 5-star scheme for open data. CDS earns all five.
| Stars | Criterion | How CDS meets it |
|---|---|---|
| ★ | Available on the web with an open license | All CDS schemas, libraries, and tooling are MIT-licensed. Events are published at HTTPS URIs. |
| ★★ | Available as machine-readable structured data | Events are JSON — parseable by every language and platform. |
| ★★★ | Available in a non-proprietary open format | JSON is an open ECMA/IETF standard (RFC 8259), not a proprietary format like Excel or PDF. |
| ★★★★ | Published using open standards from W3C | Every event carries a JSON-LD @context. Fields map to W3C RDF predicates. |
| ★★★★★ | All of the above, plus links to other data | Events link to source URIs, source URIs link to domain vocabularies, vocabulary URIs link to RDF definitions. The graph is connected. |
Dereferencing a CDS event
Section titled “Dereferencing a CDS event”Take a Mega Sena event and follow the links step by step.
Step 1 — Resolve the content type.
The event contains:
"content_type": "https://signed-data.org/vocab/lottery-brazil/mega-sena-result"Dereference it:
GET /vocab/lottery-brazil/mega-sena-result HTTP/1.1Host: signed-data.org{ "@id": "https://signed-data.org/vocab/lottery-brazil/mega-sena-result", "label": "Mega Sena draw result", "description": "Official result of a Mega Sena lottery draw conducted by Caixa Econômica Federal.", "domain": "lottery-brazil"}Now you know what the event represents.
Step 2 — Resolve the source.
GET /sources/caixa.gov.br.loterias.v1 HTTP/1.1Host: signed-data.org{ "@id": "https://signed-data.org/sources/caixa.gov.br.loterias.v1", "name": "Caixa Econômica Federal — Loterias API v1", "url": "https://servicebus2.caixa.gov.br/portaldeloterias/api/megasena", "country": "BR", "license": "public-domain", "domains": ["lottery-brazil"]}Now you know where the data came from and how to reach the upstream API.
Step 3 — Resolve the context.
GET /contexts/cds/v1.jsonld HTTP/1.1Host: signed-data.org{ "@context": { "cds": "https://signed-data.org/vocab/cds#", "xsd": "http://www.w3.org/2001/XMLSchema#", "content_type": { "@id": "cds:contentType", "@type": "@id" }, "occurred_at": { "@id": "cds:occurredAt", "@type": "xsd:dateTime" }, "source": { "@id": "cds:source", "@type": "@id" }, "fingerprint": { "@id": "cds:fingerprint" }, "payload": { "@id": "cds:payload" } }}Now you know the RDF mapping for every field.
How events become RDF triples
Section titled “How events become RDF triples”A JSON-LD processor expands the compact event into a set of RDF triples by
applying the @context.
Compact event:
{ "@context": "https://signed-data.org/contexts/cds/v1.jsonld", "@id": "https://signed-data.org/events/a3f1c9e0-7b2d-4e8a-9f01-abc123def456", "content_type": "https://signed-data.org/vocab/lottery-brazil/mega-sena-result", "occurred_at": "2026-03-29T00:00:00Z", "source": { "@id": "https://signed-data.org/sources/caixa.gov.br.loterias.v1" }}Expanded triples:
| Subject | Predicate | Object |
|---|---|---|
<events/a3f1c9e0-...> | cds:contentType | <vocab/lottery-brazil/mega-sena-result> |
<events/a3f1c9e0-...> | cds:occurredAt | "2026-03-29T00:00:00Z"^^xsd:dateTime |
<events/a3f1c9e0-...> | cds:source | <sources/caixa.gov.br.loterias.v1> |
The compact form uses developer-friendly snake_case keys; the expanded form uses full RDF predicates. Both representations carry the same semantics.
The source registry
Section titled “The source registry”The source registry is the canonical list of data sources that CDS recognises.
Each entry is a JSON-LD document served from
https://signed-data.org/sources/{source-id}.
| Field | Type | Description |
|---|---|---|
name | string | Human-readable name of the source |
url | string | Base URL of the upstream API |
auth | string | Authentication method (none, api-key, oauth2) |
license | string | License of the source data (public-domain, cc-by-4.0, etc.) |
country | string | ISO 3166-1 alpha-2 country code |
domains | string[] | List of domain identifiers (e.g., ["lottery-brazil"]) |
fingerprint_algorithm | string | Hash algorithm for source fingerprints (e.g., sha256) |
certified_at | string | ISO 8601 timestamp when the source was certified |
certified_by | string | Identifier of the entity that certified the source |
Self-hosting vocabulary
Section titled “Self-hosting vocabulary”If you run your own CDS issuer, you are not required to use signed-data.org.
You can publish your own vocabulary and source definitions at your own domain.
See Self-hosting for the full guide.
Future: SPARQL
Section titled “Future: SPARQL”Because CDS events are valid JSON-LD, they are valid RDF. Any collection of events can be loaded into a triple store (Apache Jena, Blazegraph, Amazon Neptune, Oxigraph) and queried with SPARQL.
PREFIX cds: <https://signed-data.org/vocab/cds#>PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?event ?contentType ?occurredAtWHERE { ?event cds:contentType ?contentType ; cds:occurredAt ?occurredAt .
FILTER ( STRSTARTS(STR(?contentType), "https://signed-data.org/vocab/lottery-brazil/") && ?occurredAt >= "2026-03-01T00:00:00Z"^^xsd:dateTime && ?occurredAt < "2026-04-01T00:00:00Z"^^xsd:dateTime )}ORDER BY ?occurredAtThis is not a feature CDS needs to build — it falls out of the data model for free. The investment in Linked Data pays forward: every new event is immediately queryable alongside every other event, across sources, domains, and time ranges, without writing any new code.