DEOS Receipt ↔ W3C Verifiable Credential Bridge
Status: v1.1 — published 2026-05-05.
Spec URI: https://projectmaya.deoscomputing.io/interop/vc-jwt/v1
Maintainer: DEOS Computing (did:web:deos.computing)
Audience: integrators bridging DEOS receipts to the W3C VC ecosystem (DIF, ToIP, EBSI, learning credentials, healthcare credentials).
1. Why a bridge
DEOS receipts and W3C Verifiable Credentials describe overlapping facts using different wire formats. A buyer who already has a VC-based identity stack will want to consume DEOS-attested actions without re-implementing the full DEOS verifier surface. Conversely, a DEOS-attested action may want to be presented as a VC to a verifier that only speaks W3C semantics.
Both formats share the same trust model — content-addressed, signed under DID-resolved keys, with chained delegations. The fields just have different names and the serialization is different (canonical JSON vs JWT base64-encoded JSON or JSON-LD).
This document specifies a lossless bidirectional mapping for the DEOS-receipt → VC-JWT direction (cited buyers' stronger near-term ask) and a lossy VC-JWT → DEOS-receipt direction documented for completeness.
2. DEOS Receipt → VC-JWT
A DEOS receipt is projected to a VC-JWT using the W3C VC Data Model 2.0 shape.
2.1 Field mapping (normative)
| VC-JWT JWT claim | DEOS receipt source | Notes |
|---|---|---|
iss |
receipt.issuer |
Substrate DID |
sub |
receipt.subject |
Agent DID |
iat |
receipt.issued_at / 1_000_000_000 (rounded down, seconds) |
VC field is in seconds; receipt is ns |
nbf |
receipt.issued_at / 1_000_000_000 |
Same as iat for an action receipt |
jti |
receipt.id |
Receipt id is BLAKE3-256 hex; usable as a unique JWT id |
vc.type |
["VerifiableCredential", "DeosActionAttestation"] |
DEOS-specific extension type |
vc.@context |
["https://www.w3.org/ns/credentials/v2", "https://deos.computing/contexts/receipt-v1"] |
First entry is W3C; second is DEOS-specific |
vc.credentialSubject.id |
receipt.principal |
The user on whose behalf the action ran |
vc.credentialSubject.actor |
receipt.subject |
The agent that executed |
vc.credentialSubject.action |
receipt.action (whole object) |
DEOS-specific subject claim |
vc.credentialSubject.capability |
receipt.capability (whole object) |
Carries the chain so a VC verifier can re-walk |
vc.credentialSubject.replay |
receipt.replay (whole object, optional) |
Replay-bundle pointer |
vc.evidence |
[{"id": <receipt-id>, "type": "DeosMmrInclusion", "log": receipt.log, "anchors": receipt.anchors}] |
W3C evidence carries the MMR proof + OTS anchors |
2.2 Signature
Per W3C VC-JWT spec the VC-JWT is signed by the iss (substrate) over the JWT canonical bytes. DEOS substrates use Ed25519 (alg: EdDSA per RFC 8037), matching signatures[i].alg = "ed25519" on the receipt.
To preserve the DEOS receipt's own signature inside the bridge, include it as a vc.proof entry of type: "DeosReceiptSignature2024":
"proof": [{
"type": "DeosReceiptSignature2024",
"verificationMethod": "<receipt.signatures[0].kid>",
"proofValue": "<receipt.signatures[0].sig>",
"created": "<ISO-8601 of receipt.issued_at>"
}]
The bridging tool emits both: a fresh VC-JWT signature over the projected JWT body AND the original DEOS signature in vc.proof. A VC-only verifier checks the JWT signature; a DEOS-aware verifier additionally checks vc.proof[0] against the receipt's canonical bytes (re-derivable from the JWT body via reverse projection).
2.3 Reference projection (Rust pseudocode)
fn receipt_to_vc_jwt(receipt: &Value, substrate_jwt_signer: &SigningKey) -> Result<String> {
let iat = receipt["issued_at"].as_i64()? / 1_000_000_000;
let claims = json!({
"iss": receipt["issuer"],
"sub": receipt["subject"],
"iat": iat,
"nbf": iat,
"jti": receipt["id"],
"vc": {
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://deos.computing/contexts/receipt-v1"
],
"type": ["VerifiableCredential", "DeosActionAttestation"],
"credentialSubject": {
"id": receipt["principal"],
"actor": receipt["subject"],
"action": receipt["action"],
"capability": receipt["capability"],
"replay": receipt.get("replay"),
},
"evidence": [{
"id": receipt["id"],
"type": "DeosMmrInclusion",
"log": receipt["log"],
"anchors": receipt.get("anchors"),
}],
"proof": [{
"type": "DeosReceiptSignature2024",
"verificationMethod": receipt["signatures"][0]["kid"],
"proofValue": receipt["signatures"][0]["sig"],
}]
}
});
encode_jwt(&claims, substrate_jwt_signer)
}
The reverse vc_jwt_to_receipt function reads the same fields back; the original receipt's signatures[0] is reconstructed from vc.proof[0]. The MMR root and anchor proofs come from evidence[0]. The result hashes back to the original receipt.id, preserving content-addressing.
3. VC-JWT → DEOS Receipt (lossy)
A non-DEOS-issued VC cannot be projected to a DEOS receipt without losing structure: VCs in the wild rarely carry a UCAN-shaped capability chain or an MMR inclusion proof. The result of running a non-DEOS VC through vc_jwt_to_receipt is a "shadow receipt" with empty capability.chain and no log inclusion — useful for inspection only, not for chain validation.
Implementations SHOULD reject shadow receipts at append-time. The substrate's validate_receipt_chain will fail because chain is empty; this is the correct outcome.
4. Trust transitivity
A VC verifier accepting a VC-JWT projected from a DEOS receipt MUST resolve the substrate's DID and verify the JWT signature under it. If the VC verifier additionally validates vc.proof[0] (DeosReceiptSignature2024), they get the original DEOS receipt's content-address binding for free.
A DEOS verifier accepting a VC-JWT MUST reverse-project to a receipt, then run the standard DEOS verifier surface against the result. The MMR inclusion proof in evidence[0] is what makes the bridge round-trip safe.
5. Trust failure modes
| Scenario | DEOS detection | VC detection |
|---|---|---|
| Substrate signing key compromised post-issuance | revoked_kids fetched from substrate; VC signature still verifies but receipt is rejected by chain validator |
iss DID document advertises rotation; verifier rejects |
| Receipt body tampered after VC projection | DEOS id recomputation fails |
JWT signature fails |
| VC body tampered after issuance | Reverse-projection's recomputed id doesn't match jti |
JWT signature fails |
| Capability chain forged | DEOS validate_chain rejects |
VC verifier doesn't natively check chain — DEOS-aware verifier required |
The bridge does NOT make a non-DEOS-aware VC verifier able to validate UCAN chains. That's a structural limitation of the W3C VC data model, not a bug in the bridge. Buyers that need UCAN chain validation MUST use a DEOS-aware verifier (or a VC verifier with a DEOS extension).
6. Interop targets
Bridging is currently planned for v1.2 interop testing against:
- Microsoft Entra Verified ID — VC-JWT acceptance
- Trinsic SaaS verifier — VC presentation
- DIF Universal Resolver —
did:deos↔ resolution metadata
A Rust reference bridge ships in DEOS Computing's working repo (v1.2, access-controlled during alpha).
7. Why the bridge matters
Healthcare and banking integrations frequently come in via existing VC-based stacks (HL7 FHIR + IDM, OIDC4VP, EBSI). The bridge lets a DEOS-issued action receipt flow through those pipelines as a VC and retain its provenance under both interpretations. Cost: ~2× signature evaluation per receipt and ~30% larger wire format. For audit-grade actions, that's the right trade.
For receipts that don't need to leave DEOS-aware infrastructure, use the native receipt format directly — there's no benefit to the bridge.
8. Maintainer
DEOS Computing — interop reports and conformance feedback to github.com/DEOS-Computing.
License: CC BY 4.0 (text), Apache-2.0 (reference code).