Skip to content

Migration v0.1 → v0.2

This guide covers all breaking changes introduced in CDS v0.2.0 and provides concrete migration examples for the Python and TypeScript SDKs.

Fieldv0.1.0v0.2.0Change
@contextabsent"https://signed-data.org/contexts/cds/v1.jsonld"new
@typeabsent"https://signed-data.org/vocab/CuratedDataEvent"new
@idabsent"https://signed-data.org/events/{uuid}"new
spec_version"0.1.0""0.2.0"bumped
content_typeCDSContentType objectURI stringbreaking
source.idopaque stringabsentremoved
source.@idabsentHTTP URIreplaces source.id
integrity.signed_by"signed-data.org""https://signed-data.org"breaking
from cds.schema import CDSContentType
content_type = CDSContentType(
domain="lottery.brazil",
schema_name="mega-sena.result",
version="1",
encoding="json",
)
event = CDSEvent(content_type=content_type, ...)

If you were reading the domain or schema from the content type object, use the new helper properties:

# Before
event.content_type.domain # "lottery.brazil"
event.content_type.schema_name # "mega-sena.result"
# After
event.domain # "lottery.brazil" (computed from URI)
event.event_type # "mega-sena.result" (computed from URI)
from cds.schema import SourceMeta
source = SourceMeta(id="caixa.gov.br.loterias.v1", fingerprint="sha256:...")

In the Python SDK, the SourceMeta constructor still uses id= as the keyword argument. The @id alias is applied during JSON serialisation via Pydantic’s alias="@id".

event.context.summary
event.context.model

The JSON key in the serialised output is still "context" (via Pydantic alias). The Python attribute was renamed to event_context to avoid shadowing Pydantic internals. If you read from raw JSON/dict, the key is still "context".

integrity.signed_by: bare domain → full URI

Section titled “integrity.signed_by: bare domain → full URI”
# Before
assert event.integrity.signed_by == "signed-data.org"
# After
assert event.integrity.signed_by == "https://signed-data.org"
from datetime import datetime, timezone
from cds.schema import CDSEvent, SourceMeta, ContextMeta
from cds.vocab import CDSVocab, CDSSources
event = CDSEvent(
content_type = CDSVocab.LOTTERY_MEGA_SENA,
source = SourceMeta(id=CDSSources.CAIXA_LOTERIAS, fingerprint="sha256:b310..."),
occurred_at = datetime.now(timezone.utc),
lang = "pt-BR",
payload = {"concurso": 2800, "dezenas": ["03", "17", "25", "38", "44", "55"]},
event_context = ContextMeta(summary="Mega Sena concurso 2800 resultado..."),
)
jsonld = event.to_jsonld()
# jsonld["@context"] == "https://signed-data.org/contexts/cds/v1.jsonld"
# jsonld["@type"] == "https://signed-data.org/vocab/CuratedDataEvent"
# jsonld["@id"] == "https://signed-data.org/events/<uuid>"
import { CDSContentType } from "@signeddata/cds-sdk";
const contentType = new CDSContentType({
domain: "lottery.brazil",
schema_name: "mega-sena.result",
version: "1",
encoding: "json",
});
const event = new CDSEvent({ content_type: contentType });

event.content_type is now a string (was a CDSContentType object). If you were accessing event.content_type.domain, use the new getter:

event.domain // "lottery.brazil" (computed from URI)
event.event_type // "mega-sena.result" (computed from URI)
const source = { id: "caixa.gov.br.loterias.v1", fingerprint: "sha256:..." };

The SourceMeta interface now uses "@id" instead of id. This is a breaking change in the object shape.

integrity.signed_by: bare domain → full URI

Section titled “integrity.signed_by: bare domain → full URI”
// Before
event.integrity.signed_by === "signed-data.org"
// After
event.integrity.signed_by === "https://signed-data.org"
import { CDSEvent, CDSVocab, CDSSources } from "@signeddata/cds-sdk";
const event = new CDSEvent({
content_type: CDSVocab.LOTTERY_MEGA_SENA,
source: {
"@id": CDSSources.CAIXA_LOTERIAS,
fingerprint: "sha256:b310...",
},
occurred_at: new Date(),
lang: "pt-BR",
payload: { concurso: 2800, dezenas: ["03", "17", "25", "38", "44", "55"] },
context: {
summary: "Mega Sena concurso 2800 resultado...",
model: "rule-based-v1",
generated_at: new Date().toISOString(),
},
});
const json = event.toJSON();
// json["@context"] === "https://signed-data.org/contexts/cds/v1.jsonld"
// json["@type"] === "https://signed-data.org/vocab/CuratedDataEvent"
// json["@id"] === "https://signed-data.org/events/<uuid>"
  • v0.1.0 canonical bytes: JSON-serialised event excluding integrity and ingested_at. Does not include @context, @type, or @id (these fields did not exist).
  • v0.2.0 canonical bytes: JSON-serialised event excluding integrity and ingested_at. Includes @context, @type, and @id.

Because the canonical byte representations differ, a signature produced under v0.1.0 will not match the canonical bytes computed by a v0.2.0 verifier. The hash will differ, and RSA-PSS verification will fail.

If you need to verify historical v0.1.0 events, you must use a v0.1.0-compatible verifier that computes canonical bytes without the @context, @type, and @id fields.

v0.1.0 events remain valid under their own specification. The CDS v0.1.0 spec is not retracted or deprecated — it simply does not support Linked Data features. A v0.1.0 event with spec_version: "0.1.0" can still be verified using v0.1.0 canonical byte rules and the same RSA-PSS algorithm.

Consumers SHOULD check the spec_version field to determine which canonical byte computation to use during verification:

if event_data["spec_version"] == "0.1.0":
# use v0.1.0 canonical bytes (no @context/@type/@id)
pass
elif event_data["spec_version"] == "0.2.0":
# use v0.2.0 canonical bytes (includes @context/@type/@id)
pass