Protocol / Credentials / Witness Service
in-progressLayer 2 · App

Witness Service

The OpenCredentials issuer — an axum HTTP service at witness.tinycloud.xyz that runs verification flows and issues credentials signed as did:web:issuer.tinycloud.xyz, with its key derived inside a DStack TEE.

Witness Service

The witness service is the OpenCredentials issuer: an HTTP service (deployed at https://witness.tinycloud.xyz) that verifies a real-world fact about a subject and, on success, signs a credential as did:web:issuer.tinycloud.xyz. It "witnesses" a claim — that the subject controls an email address, a DNS record, a GitHub account — and converts that observation into a portable SD-JWT / W3C VC that anyone, including the policy engine, can later verify against the issuer's published DID document.

Role

The witness is the trust anchor of OpenCredentials within Layer 2. A credential is only as trustworthy as the entity that signed it, so the witness's identity (did:web:issuer.tinycloud.xyz) and key custody matter: it is the issuer a Policy names in authority.accepted_issuers when it gates a delegation on credential evidence. The verification logic lives off-chain (talking to email providers, DNS, GitHub APIs); the witness's signature is what makes the result self-verifying afterward.

Mechanics

The service is an axum Router (rust/opencredentials_witness/src/main.rs) exposing:

  • GET /.well-known/did.json — the issuer's DID document (Ed25519 verification key, JsonWebKey2020), generated from the issuer JWK with the private d field stripped (config::did_document).
  • GET /capabilities — which verification flows + credential formats are enabled.
  • POST /instructionsPOST /statementPOST /witness_jwt | /witness_sd_jwt | /witness_ld — the three-step issuance handshake: the client gets flow instructions, submits a signed statement proving the fact, and receives the issued credential in the requested format.
  • POST /verify — stateless verification of a presented VC.

Which flows are active is set per deployment by env vars (config::new_flow): no-API-key flows (DNS, GitHub, Reddit, same-controller assertion, attestation) default on; API-key flows (email via Resend, NFT via Alchemy, POAP, Twitter, SoundCloud) enable when their key is present.

Key custody — DStack TEE

The Ed25519 signing key is, in production, derived inside a DStack TEE (KEYS_TYPE=dstack): build_issuer_dstack calls dstack::get_key("opencredentials/witness/signing-key") to obtain a TEE-bound seed, so the issuer key is never handled as plaintext outside the enclave. Without dstack, the key comes from OPENCREDENTIALS_SK (development). This is the same DStack confidential-compute basis used elsewhere in TinyCloud.

Shape

  • Issuer DID: did:web:issuer.tinycloud.xyz (set via DID_WEB).
  • Service host: https://witness.tinycloud.xyz (the client's DEFAULT_WITNESS_URL).
  • Signature: Ed25519 (JsonWebKey2020 / EdDSA), key derived in a DStack TEE.
  • Issuance handshake: instructions → statement → witness_{jwt|sd_jwt|ld}.

Relationships

The issuer half of OpenCredentials; signs SD-JWT / W3C VCs; its DID did:web:issuer.tinycloud.xyz is the trusted issuer a Policy lists in accepted_issuers for credential-gated delegation; its key is protected by the same DStack TEE confidential-compute model; identified by a did:web; an L2 component.

Example

A client POSTs an email-verification statement to https://witness.tinycloud.xyz. The service runs the email flow, confirms the user controls sam@tinycloud.xyz, and returns from /witness_sd_jwt an SD-JWT credential signed by the TEE-held key of did:web:issuer.tinycloud.xyz. Months later the policy engine resolves that same DID's /.well-known/did.json, finds the verification key, and verifies the credential with no further contact with the witness.

Status & drift

in-progress. The axum service, the route set, the DStack-derived issuer key, and did.json generation are implemented. Many verification flows exist in config, but the only flow currently wired through to TinyCloud authorization is email (the policy engine's opencredentials.email/v1 verifier — see credential-gated-delegation). The did:web:issuer.tinycloud.xyz / witness.tinycloud.xyz hostnames reflect the intended deployment; treat exact operational endpoints as deployment config. See contradictions.

Sources

  • OpenCredentials: rust/opencredentials_witness/src/main.rs (axum routes, DStack key derivation), rust/opencredentials_witness/src/routes.rs (handler set), rust/opencredentials_witness/src/config.rs (new_flow, did_document, did:web issuer)