Self-hosting
You do not need to use signed-data.org as your data provider. CDS is a standard
— the SDK, the spec, and the domain schemas are infrastructure-free. Run your own
ingestors, sign with your own key, and serve your own events.
What you need
Section titled “What you need”| Component | Minimum viable | Production |
|---|---|---|
| Private key | Local PEM file | AWS Secrets Manager / KMS |
| Ingestor | Python script | Lambda / container (cron) |
| Event store | Local files | S3 (append-only, versioned) |
| Consumer API | Read from disk | API Gateway / ALB + Lambda / ECS |
| MCP server | signeddata-mcp-lottery | ECS Fargate behind ALB |
Minimum viable setup (local)
Section titled “Minimum viable setup (local)”pip install signeddata-cds
# Generate keyspython3 -c "from cds import generate_keypairimport os; os.makedirs('keys', exist_ok=True)generate_keypair('keys/private.pem', 'keys/public.pem')"import asynciofrom cds import CDSSignerfrom cds.sources.lottery import MegaSenaIngestor
signer = CDSSigner("keys/private.pem", issuer="https://mycompany.example.com")ingestor = MegaSenaIngestor(signer=signer)events = asyncio.run(ingestor.ingest())
for e in events: print(e.context.summary) print(f" signed_by: {e.integrity.signed_by}") print(f" hash: {e.integrity.hash[:32]}...")Issuer identity
Section titled “Issuer identity”Set your issuer to the URI of your organisation:
signer = CDSSigner("keys/private.pem", issuer="https://mycompany.example.com")Publish your public key at:
https://mycompany.example.com/.well-known/cds-public-key.pemConsumers can then discover and verify your key automatically.
Publishing your vocabulary
Section titled “Publishing your vocabulary”If you run your own CDS issuer, you should publish your vocabulary so consumers can dereference URIs in your events.
-
Create your vocabulary file
Create
vocab/cds.jsonldfor your organisation, listing the classes and properties you use. You can copy and adapt the reference vocabulary fromhttps://signed-data.org/vocab/. -
Serve it at your domain
Upload
vocab/cds.jsonldand any domain files to your web server or CDN:https://mycompany.example.com/vocab/ → cds.jsonldhttps://mycompany.example.com/vocab/domains/ → domain fileshttps://mycompany.example.com/sources/ → source documentshttps://mycompany.example.com/contexts/ → JSON-LD contextSet
Content-Type: application/ld+jsonfor all.jsonldfiles. -
Publish your public key
https://mycompany.example.com/.well-known/cds-public-key.pem -
Reference your vocabulary in events
Your events should use your base URI:
from cds.vocab import content_type_uri, source_uriMY_BASE = "https://mycompany.example.com"my_content_type = f"{MY_BASE}/vocab/custom-domain/custom-schema"my_source = f"{MY_BASE}/sources/my-api.v1"
What stays the same
Section titled “What stays the same”When self-hosting, the envelope format, content types, signing algorithm, and
domain specs are identical to signed-data.org. Your events are verifiable by
any CDS consumer — they just use your public key instead of ours.
What changes
Section titled “What changes”| Property | signed-data.org | Your deployment |
|---|---|---|
integrity.signed_by | https://signed-data.org | https://mycompany.example.com |
| Public key URL | signed-data.org/.well-known/... | mycompany.example.com/.well-known/... |
| Infrastructure | Our AWS account | Your AWS / GCP / Azure account |
| Ingestor schedule | Our crons | Your crons |
Consuming events from multiple issuers
Section titled “Consuming events from multiple issuers”A consumer can hold multiple public keys and verify against the declared issuer:
KNOWN_ISSUERS = { "signed-data.org": CDSVerifier("signed-data-org.pub.pem"), "mycompany.example.com": CDSVerifier("mycompany.pub.pem"),}
verifier = KNOWN_ISSUERS.get(event.integrity.signed_by)if not verifier: raise ValueError(f"Unknown issuer: {event.integrity.signed_by}")
verifier.verify(event)