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:

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).