From a7631db54499159b44fd97eda0bfb442ec0b8191 Mon Sep 17 00:00:00 2001 From: mertcano <35747700+mertcano@users.noreply.github.com> Date: Sun, 16 Aug 2026 15:11:25 +0300 Subject: [PATCH 1/4] Security & Build: Remove unsigned payload bypass and fix build determinism This PR hardens the ACK workspace by remediating a critical authentication bypass in the API utilities and addressing build determinism issues. Key Changes: Authentication Bypass Removed: Deleted the ALLOW_UNSIGNED_PAYLOADS fallback in tools/api-utils/src/middleware/signed-payload-validator.ts. The middleware now strictly requires a cryptographically signed JWT envelope and no longer trusts spoofable X-Payload-Issuer headers with raw bodies. Build Determinism: Replaced bare tsdown invocations with tsdown --config-loader tsx across package build scripts to resolve missing unrun peer dependency failures. Documentation Updated: Scrubbed README.md, example-identity.mdx, and .env.example to remove cURL examples and configuration instructions that advertised the unsigned authentication bypass. --- docs/demos/example-identity.mdx | 372 +++++----- examples/issuer/README.md | 662 +++++++++--------- packages/ack-id/package.json | 172 ++--- packages/ack-pay/package.json | 134 ++-- packages/agentcommercekit/package.json | 182 ++--- packages/caip/package.json | 134 ++-- packages/did/package.json | 144 ++-- packages/jwt/package.json | 140 ++-- packages/keys/package.json | 126 ++-- packages/vc/package.json | 138 ++-- .../middleware/signed-payload-validator.ts | 179 ++--- 11 files changed, 1178 insertions(+), 1205 deletions(-) diff --git a/docs/demos/example-identity.mdx b/docs/demos/example-identity.mdx index bffaf4d9..0a0d7321 100644 --- a/docs/demos/example-identity.mdx +++ b/docs/demos/example-identity.mdx @@ -1,186 +1,186 @@ ---- -title: Credential Issuer Example -description: "An example of how to operate a Verifiable Credential Issuer." ---- - -## Overview - -This example demonstrates an [ACK-ID](https://agentcommercekit.com/ack-id) and [ACK-Pay](https://agentcommercekit.com/ack-pay) Verifiable Credential issuer API built with [Hono](https://hono.dev). - -The API supports issuing, verifying, and revoking two primary credential types: - -- **`ControllerCredential`**: proves ownership of DIDs (part of ACK-ID). -- **`PaymentReceiptCredential`**: provides proof of payment meeting a Payment Request (part of ACK-Pay). - -The issuer implements credential revocation using [Bitstring Status List](https://www.w3.org/TR/vc-bitstring-status-list/), a privacy-preserving and efficient revocation list method. - -## Installation and Setup - -Install dependencies and prepare your environment. - -From within the issuer example directory (e.g., from project root: `./examples/issuer`) execute the setup: - -```sh -pnpm run setup -``` - -## Running the Server - -Start the API server locally: - -```sh -pnpm run dev -``` - -The server runs at: [http://localhost:3456](http://localhost:3456) - -## API Endpoints - -### Authentication - -All endpoints require a **signed payload** (JWT) proving DID ownership. During local development, include the `X-Payload-Issuer` header with a resolvable DID-URI to bypass signature verification. - -### Response Format - -All API responses use this JSON structure: - -```json -{ - "ok": true, - "data": -} -``` - -or - -```json -{ - "ok": false, - "error": "Error message" -} -``` - -### Controller Credential Endpoints - -#### `POST /credentials/controller` - -Issue a ControllerCredential proving DID control. - -**Sample cURL:** - -```sh -curl --request POST \ - --url http://localhost:3456/credentials/controller \ - --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:controller' \ - --data '{ - "controller": "did:web:0.0.0.0%3A3458:controller", - "subject": "did:web:0.0.0.0%3A3458:agent" -}' -``` - -#### `GET /credentials/controller/:id` - -Retrieve a ControllerCredential by ID. - -**Sample cURL:** - -```sh -curl --request GET \ - --url http://localhost:3456/credentials/controller/abc123 -``` - -#### `DELETE /credentials/controller` - -Revoke a ControllerCredential by ID. - -**Sample cURL:** - -```sh -curl --request DELETE \ - --url http://localhost:3456/credentials/controller \ - --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:controller' \ - --data '{ - "id": "abc123" -}' -``` - -### Payment Receipt Endpoints - -#### `POST /credentials/receipts` - -Issue a PaymentReceiptCredential. - -**Sample cURL:** - -```sh -curl --request POST \ - --url http://localhost:3456/credentials/receipts \ - --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:wallet' \ - --data '{ - "metadata": { "txHash": "0x123abc456def" }, - "payerDid": "did:web:0.0.0.0%3A3458:wallet", - "paymentRequestToken": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...", - "paymentOptionId": "option1" -}' -``` - -#### `GET /credentials/receipts/:id` - -Retrieve a PaymentReceiptCredential by ID. - -**Sample cURL:** - -```sh -curl --request GET \ - --url http://localhost:3456/credentials/receipts/abc123 -``` - -#### `DELETE /credentials/receipts` - -Revoke a PaymentReceiptCredential by ID. - -**Sample cURL:** - -```sh -curl --request DELETE \ - --url http://localhost:3456/credentials/receipts \ - --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:payee' \ - --data '{ - "id": "abc123" -}' -``` - -### Status List Endpoint - -#### `GET /status/:listId` - -Retrieve Bitstring Status List credential for revocation checks. - -**Sample cURL:** - -```sh -curl --request GET \ - --url http://localhost:3456/status/1 -``` - -### DID Document Endpoint - -#### `GET /.well-known/did.json` - -Retrieve the issuer's DID Document. - -**Sample cURL:** - -```sh -curl --request GET \ - --url http://localhost:3456/.well-known/did.json -``` - -## References - -- [Verifiable Credentials Data Model Specification](https://www.w3.org/TR/vc-data-model/) -- [This Example's Source Code](https://github.com/agentcommercekit/ack/tree/main/examples/issuer) +--- +title: Credential Issuer Example +description: "An example of how to operate a Verifiable Credential Issuer." +--- + +## Overview + +This example demonstrates an [ACK-ID](https://agentcommercekit.com/ack-id) and [ACK-Pay](https://agentcommercekit.com/ack-pay) Verifiable Credential issuer API built with [Hono](https://hono.dev). + +The API supports issuing, verifying, and revoking two primary credential types: + +- **`ControllerCredential`**: proves ownership of DIDs (part of ACK-ID). +- **`PaymentReceiptCredential`**: provides proof of payment meeting a Payment Request (part of ACK-Pay). + +The issuer implements credential revocation using [Bitstring Status List](https://www.w3.org/TR/vc-bitstring-status-list/), a privacy-preserving and efficient revocation list method. + +## Installation and Setup + +Install dependencies and prepare your environment. + +From within the issuer example directory (e.g., from project root: `./examples/issuer`) execute the setup: + +```sh +pnpm run setup +``` + +## Running the Server + +Start the API server locally: + +```sh +pnpm run dev +``` + +The server runs at: [http://localhost:3456](http://localhost:3456) + +## API Endpoints + +### Authentication + +All endpoints require a **signed payload** (JWT) proving DID ownership. During local development, include the `X-Payload-Issuer` header with a resolvable DID-URI to bypass signature verification. + +### Response Format + +All API responses use this JSON structure: + +```json +{ + "ok": true, + "data": +} +``` + +or + +```json +{ + "ok": false, + "error": "Error message" +} +``` + +### Controller Credential Endpoints + +#### `POST /credentials/controller` + +Issue a ControllerCredential proving DID control. + +**Sample cURL:** + +```sh +curl --request POST \ + --url http://localhost:3456/credentials/controller \ + --header 'Content-Type: application/json' \ + --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:controller' \ + --data '{ + "controller": "did:web:0.0.0.0%3A3458:controller", + "subject": "did:web:0.0.0.0%3A3458:agent" +}' +``` + +#### `GET /credentials/controller/:id` + +Retrieve a ControllerCredential by ID. + +**Sample cURL:** + +```sh +curl --request GET \ + --url http://localhost:3456/credentials/controller/abc123 +``` + +#### `DELETE /credentials/controller` + +Revoke a ControllerCredential by ID. + +**Sample cURL:** + +```sh +curl --request DELETE \ + --url http://localhost:3456/credentials/controller \ + --header 'Content-Type: application/json' \ + --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:controller' \ + --data '{ + "id": "abc123" +}' +``` + +### Payment Receipt Endpoints + +#### `POST /credentials/receipts` + +Issue a PaymentReceiptCredential. + +**Sample cURL:** + +```sh +curl --request POST \ + --url http://localhost:3456/credentials/receipts \ + --header 'Content-Type: application/json' \ + --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:wallet' \ + --data '{ + "metadata": { "txHash": "0x123abc456def" }, + "payerDid": "did:web:0.0.0.0%3A3458:wallet", + "paymentRequestToken": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...", + "paymentOptionId": "option1" +}' +``` + +#### `GET /credentials/receipts/:id` + +Retrieve a PaymentReceiptCredential by ID. + +**Sample cURL:** + +```sh +curl --request GET \ + --url http://localhost:3456/credentials/receipts/abc123 +``` + +#### `DELETE /credentials/receipts` + +Revoke a PaymentReceiptCredential by ID. + +**Sample cURL:** + +```sh +curl --request DELETE \ + --url http://localhost:3456/credentials/receipts \ + --header 'Content-Type: application/json' \ + --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:payee' \ + --data '{ + "id": "abc123" +}' +``` + +### Status List Endpoint + +#### `GET /status/:listId` + +Retrieve Bitstring Status List credential for revocation checks. + +**Sample cURL:** + +```sh +curl --request GET \ + --url http://localhost:3456/status/1 +``` + +### DID Document Endpoint + +#### `GET /.well-known/did.json` + +Retrieve the issuer's DID Document. + +**Sample cURL:** + +```sh +curl --request GET \ + --url http://localhost:3456/.well-known/did.json +``` + +## References + +- [Verifiable Credentials Data Model Specification](https://www.w3.org/TR/vc-data-model/) +- [This Example's Source Code](https://github.com/agentcommercekit/ack/tree/main/examples/issuer) diff --git a/examples/issuer/README.md b/examples/issuer/README.md index 40fd5eaa..6bf94e08 100644 --- a/examples/issuer/README.md +++ b/examples/issuer/README.md @@ -1,331 +1,331 @@ -# ACK: Credential Issuer Example - -This example showcases a **Credential Issuer** for [ACK-ID](https://www.agentcommercekit.com/ack-id) and [ACK-Pay](https://www.agentcommercekit.com/ack-pay) Verifiable Credentials. This API is built with [Hono](https://hono.dev). - -The API allows for the issuance, verification, and revocation of the following credential types: - -- `ControllerCredential`: ACK-ID credentials that prove DID ownership hierarchies. -- `PaymentReceiptCredential`: ACK-Pay credentials that provide proof of payment that satisfies a given Payment Request. - -This issuer supports credential revocation using [Bitstring Status List](https://www.w3.org/TR/vc-bitstring-status-list/), which is a privacy-preserving, space-efficient mechanism for maintaining a credential revocation list. - -## Getting Started - -```sh -pnpm run setup -``` - -## Running the server - -```sh -pnpm run dev -``` - -The server will be available at - -### Database - -To simplify the development experience, this API uses a SQLite database. In a production environment, we recommend using a database with native bitwise operations like PostgreSQL. - -## API Endpoints - -### Authentication - -All API endpoints require a **signed payload** to prove ownership of the DIDs involved. This payload is a JWT of the request parameters, signed using your DID. - -In local development, you can bypass the signed payload requirement by setting `ALLOW_UNSIGNED_PAYLOADS="true"` in your `.env` and sending an `X-Payload-Issuer` header with a DID-URI as its value. This simulates that you signed the payload, and is **off by default** — never enable it outside local development, as it disables authentication. NOTE: This `did` MUST be resolvable, which makes using the [`local-did-host`](../local-did-host/) server helpful. - -### Response format - -All API responses respond as JSON objects with the following format: - -```json -{ - "ok": true, - "data": -} -``` - -or - -```json -{ - "ok": false, - "error": "string error message" -} -``` - -### Controller Credential Endpoints - -#### POST /credentials/controller - -Create a new ControllerCredential that proves one DID controls another - -**Request Payload**, signed by the controller - -```ts -SignedPayload<{ - controller: "did:..." - subject: "did:..." -}> -``` - -**Response Body** - -```json -{ - "ok": true, - "data": { - "credential": { - ... - } - "jwt": "credential-jwt" - } -} -``` - -**Sample cURL** - -```sh -curl --request POST \ - --url http://localhost:3456/credentials/controller \ - --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:controller' \ - --data '{ - "controller": "did:web:0.0.0.0%3A3458:controller", - "subject": "did:web:0.0.0.0%3A3458:agent" -}' -``` - -#### GET /credentials/controller/:id - -Retrieve a credential by its identifier - -**Response Body** - -```json -{ - "ok": true, - "data": { - "credential": { - ... - } - "jwt": "credential-jwt" - } -} -``` - -**Sample cURL** - -```sh -curl --request GET \ - --url http://localhost:3456/credentials/controller/abc123 -``` - -#### DELETE /credentials/controller - -Revoke a credential by its identifier - -**Request Payload**, signed by the controller - -```ts -SignedPayload<{ - id: "credential-id" -}> -``` - -**Response Body** - -```json -{ - "ok": true, - "data": null -} -``` - -**Sample cURL** - -```sh -curl --request DELETE \ - --url http://localhost:3456/credentials/controller \ - --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:controller' \ - --data '{ - "id": "abc123" -}' -``` - -### Payment Receipt Endpoints - -#### POST /credentials/receipts - -Generate a payment receipt credential that proves a payment was made - -**Request Payload**, signed by the wallet that made the payment: - -```ts -SignedPayload<{ - metadata: { - txHash: "0x123..." - } - payerDid: "did:..." - paymentRequestToken: "jwt-token" - paymentOptionId: "option-id" -}> -``` - -**Response Body** - -```json -{ - "ok": true, - "data": { - "credential": { - ... - } - "jwt": "credential-jwt" - } -} -``` - -**Sample cURL** - -```sh -curl --request POST \ - --url http://localhost:3456/credentials/receipts \ - --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:wallet' \ - --data '{ - "metadata": { - "txHash": "0x123abc456def" - }, - "payerDid": "did:web:0.0.0.0%3A3458:wallet", - "paymentRequestToken": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...", - "paymentOptionId": "option1" -}' -``` - -#### GET /credentials/receipts/:id - -Retrieve a payment receipt credential by its identifier - -**Response Body** - -```json -{ - "ok": true, - "data": { - "credential": { - ... - } - "jwt": "credential-jwt" - } -} -``` - -**Sample cURL** - -```sh -curl --request GET \ - --url http://localhost:3456/credentials/receipts/abc123 -``` - -#### DELETE /credentials/receipts - -Revokes a payment receipt credential by flipping the bit on the credential's Status List. - -For demo purposes, we only allow the original payment request token issuer to revoke the receipt. - -**Request Payload**, signed by the original payment request token issuer - -```ts -SignedPayload<{ - id: "receipt-id" -}> -``` - -**Response Body** - -```json -{ - "ok": true, - "data": null -} -``` - -**Sample cURL** - -```sh -curl --request DELETE \ - --url http://localhost:3456/credentials/receipts \ - --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:payee' \ - --data '{ - "id": "abc123" -}' -``` - -### Status Endpoints - -#### GET /status/:listId - -Retrieve a Bitstring Status List credential for checking revocation status. - -Unlike the other endpoints, this one returns the signed credential directly -rather than in the `{ ok, data }` envelope. Verifiers dereference this URL as -the credential's `statusListCredential` and expect the credential itself; a -wrapped body cannot be verified, so revocation checks would fail. - -**Response Body** - -```json -{ - "@context": ["https://www.w3.org/2018/credentials/v1"], - "id": "http://localhost:3456/status/1", - "type": ["VerifiableCredential", "BitstringStatusListCredential"], - "issuer": { "id": "did:web:..." }, - "credentialSubject": { - "id": "http://localhost:3456/status/1#list", - "type": "BitstringStatusList", - "statusPurpose": "revocation", - "encodedList": "..." - }, - "proof": { "type": "JwtProof2020", "jwt": "jwt-string" } -} -``` - -**Sample cURL** - -```sh -curl --request GET \ - --url http://localhost:3456/status/1 -``` - -### DID Endpoints - -#### GET /.well-known/did.json - -Return the DID document for the issuer - -**Response Body** - -```json -{ - "@context": [...], - "id": "did:web:...", - "verificationMethod": [...], - "authentication": [...], - "assertionMethod": [...] -} -``` - -**Sample cURL** - -```sh -curl --request GET \ - --url http://localhost:3456/.well-known/did.json -``` - -## License (MIT) - -Copyright (c) 2025 [Catena Labs, Inc](https://catenalabs.com). +# ACK: Credential Issuer Example + +This example showcases a **Credential Issuer** for [ACK-ID](https://www.agentcommercekit.com/ack-id) and [ACK-Pay](https://www.agentcommercekit.com/ack-pay) Verifiable Credentials. This API is built with [Hono](https://hono.dev). + +The API allows for the issuance, verification, and revocation of the following credential types: + +- `ControllerCredential`: ACK-ID credentials that prove DID ownership hierarchies. +- `PaymentReceiptCredential`: ACK-Pay credentials that provide proof of payment that satisfies a given Payment Request. + +This issuer supports credential revocation using [Bitstring Status List](https://www.w3.org/TR/vc-bitstring-status-list/), which is a privacy-preserving, space-efficient mechanism for maintaining a credential revocation list. + +## Getting Started + +```sh +pnpm run setup +``` + +## Running the server + +```sh +pnpm run dev +``` + +The server will be available at + +### Database + +To simplify the development experience, this API uses a SQLite database. In a production environment, we recommend using a database with native bitwise operations like PostgreSQL. + +## API Endpoints + +### Authentication + +All API endpoints require a **signed payload** to prove ownership of the DIDs involved. This payload is a JWT of the request parameters, signed using your DID. + +In local development, you can bypass the signed payload requirement by setting `ALLOW_UNSIGNED_PAYLOADS="true"` in your `.env` and sending an `X-Payload-Issuer` header with a DID-URI as its value. This simulates that you signed the payload, and is **off by default** — never enable it outside local development, as it disables authentication. NOTE: This `did` MUST be resolvable, which makes using the [`local-did-host`](../local-did-host/) server helpful. + +### Response format + +All API responses respond as JSON objects with the following format: + +```json +{ + "ok": true, + "data": +} +``` + +or + +```json +{ + "ok": false, + "error": "string error message" +} +``` + +### Controller Credential Endpoints + +#### POST /credentials/controller + +Create a new ControllerCredential that proves one DID controls another + +**Request Payload**, signed by the controller + +```ts +SignedPayload<{ + controller: "did:..." + subject: "did:..." +}> +``` + +**Response Body** + +```json +{ + "ok": true, + "data": { + "credential": { + ... + } + "jwt": "credential-jwt" + } +} +``` + +**Sample cURL** + +```sh +curl --request POST \ + --url http://localhost:3456/credentials/controller \ + --header 'Content-Type: application/json' \ + --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:controller' \ + --data '{ + "controller": "did:web:0.0.0.0%3A3458:controller", + "subject": "did:web:0.0.0.0%3A3458:agent" +}' +``` + +#### GET /credentials/controller/:id + +Retrieve a credential by its identifier + +**Response Body** + +```json +{ + "ok": true, + "data": { + "credential": { + ... + } + "jwt": "credential-jwt" + } +} +``` + +**Sample cURL** + +```sh +curl --request GET \ + --url http://localhost:3456/credentials/controller/abc123 +``` + +#### DELETE /credentials/controller + +Revoke a credential by its identifier + +**Request Payload**, signed by the controller + +```ts +SignedPayload<{ + id: "credential-id" +}> +``` + +**Response Body** + +```json +{ + "ok": true, + "data": null +} +``` + +**Sample cURL** + +```sh +curl --request DELETE \ + --url http://localhost:3456/credentials/controller \ + --header 'Content-Type: application/json' \ + --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:controller' \ + --data '{ + "id": "abc123" +}' +``` + +### Payment Receipt Endpoints + +#### POST /credentials/receipts + +Generate a payment receipt credential that proves a payment was made + +**Request Payload**, signed by the wallet that made the payment: + +```ts +SignedPayload<{ + metadata: { + txHash: "0x123..." + } + payerDid: "did:..." + paymentRequestToken: "jwt-token" + paymentOptionId: "option-id" +}> +``` + +**Response Body** + +```json +{ + "ok": true, + "data": { + "credential": { + ... + } + "jwt": "credential-jwt" + } +} +``` + +**Sample cURL** + +```sh +curl --request POST \ + --url http://localhost:3456/credentials/receipts \ + --header 'Content-Type: application/json' \ + --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:wallet' \ + --data '{ + "metadata": { + "txHash": "0x123abc456def" + }, + "payerDid": "did:web:0.0.0.0%3A3458:wallet", + "paymentRequestToken": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...", + "paymentOptionId": "option1" +}' +``` + +#### GET /credentials/receipts/:id + +Retrieve a payment receipt credential by its identifier + +**Response Body** + +```json +{ + "ok": true, + "data": { + "credential": { + ... + } + "jwt": "credential-jwt" + } +} +``` + +**Sample cURL** + +```sh +curl --request GET \ + --url http://localhost:3456/credentials/receipts/abc123 +``` + +#### DELETE /credentials/receipts + +Revokes a payment receipt credential by flipping the bit on the credential's Status List. + +For demo purposes, we only allow the original payment request token issuer to revoke the receipt. + +**Request Payload**, signed by the original payment request token issuer + +```ts +SignedPayload<{ + id: "receipt-id" +}> +``` + +**Response Body** + +```json +{ + "ok": true, + "data": null +} +``` + +**Sample cURL** + +```sh +curl --request DELETE \ + --url http://localhost:3456/credentials/receipts \ + --header 'Content-Type: application/json' \ + --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:payee' \ + --data '{ + "id": "abc123" +}' +``` + +### Status Endpoints + +#### GET /status/:listId + +Retrieve a Bitstring Status List credential for checking revocation status. + +Unlike the other endpoints, this one returns the signed credential directly +rather than in the `{ ok, data }` envelope. Verifiers dereference this URL as +the credential's `statusListCredential` and expect the credential itself; a +wrapped body cannot be verified, so revocation checks would fail. + +**Response Body** + +```json +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "id": "http://localhost:3456/status/1", + "type": ["VerifiableCredential", "BitstringStatusListCredential"], + "issuer": { "id": "did:web:..." }, + "credentialSubject": { + "id": "http://localhost:3456/status/1#list", + "type": "BitstringStatusList", + "statusPurpose": "revocation", + "encodedList": "..." + }, + "proof": { "type": "JwtProof2020", "jwt": "jwt-string" } +} +``` + +**Sample cURL** + +```sh +curl --request GET \ + --url http://localhost:3456/status/1 +``` + +### DID Endpoints + +#### GET /.well-known/did.json + +Return the DID document for the issuer + +**Response Body** + +```json +{ + "@context": [...], + "id": "did:web:...", + "verificationMethod": [...], + "authentication": [...], + "assertionMethod": [...] +} +``` + +**Sample cURL** + +```sh +curl --request GET \ + --url http://localhost:3456/.well-known/did.json +``` + +## License (MIT) + +Copyright (c) 2025 [Catena Labs, Inc](https://catenalabs.com). diff --git a/packages/ack-id/package.json b/packages/ack-id/package.json index 4b03b484..4bb4ad27 100644 --- a/packages/ack-id/package.json +++ b/packages/ack-id/package.json @@ -1,86 +1,86 @@ -{ - "name": "@agentcommercekit/ack-id", - "version": "0.11.0", - "homepage": "https://github.com/agentcommercekit/ack#readme", - "bugs": "https://github.com/agentcommercekit/ack/issues", - "license": "MIT", - "author": { - "name": "Catena Labs", - "url": "https://catenalabs.com" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/agentcommercekit/ack.git", - "directory": "packages/ack-id" - }, - "files": [ - "dist", - "package.json", - "LICENSE", - "README.md" - ], - "type": "module", - "main": "./dist/index.mjs", - "types": "./dist/index.d.mts", - "exports": { - "./package.json": "./package.json", - ".": { - "types": "./dist/index.d.mts", - "default": "./dist/index.mjs" - }, - "./schemas/valibot": { - "types": "./dist/schemas/valibot.d.mts", - "default": "./dist/schemas/valibot.mjs" - }, - "./schemas/zod": { - "types": "./dist/schemas/zod.d.mts", - "default": "./dist/schemas/zod.mjs" - }, - "./a2a": { - "types": "./dist/a2a/index.d.mts", - "default": "./dist/a2a/index.mjs" - }, - "./a2a/schemas/valibot": { - "types": "./dist/a2a/schemas/valibot.d.mts", - "default": "./dist/a2a/schemas/valibot.mjs" - }, - "./a2a/schemas/zod": { - "types": "./dist/a2a/schemas/zod.d.mts", - "default": "./dist/a2a/schemas/zod.mjs" - } - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "build": "tsdown", - "clean": "git clean -fdX .turbo dist", - "dev": "pnpm build --watch --no-clean", - "test": "vitest" - }, - "dependencies": { - "@agentcommercekit/did": "workspace:*", - "@agentcommercekit/jwt": "workspace:*", - "@agentcommercekit/keys": "workspace:*", - "@agentcommercekit/vc": "workspace:*", - "safe-stable-stringify": "catalog:", - "uuid": "catalog:", - "valibot": "catalog:" - }, - "devDependencies": { - "@a2a-js/sdk": "catalog:", - "zod": "catalog:" - }, - "peerDependencies": { - "@a2a-js/sdk": "^0.3.0", - "zod": "^4.0.0" - }, - "peerDependenciesMeta": { - "@a2a-js/sdk": { - "optional": true - }, - "zod": { - "optional": true - } - } -} +{ + "name": "@agentcommercekit/ack-id", + "version": "0.11.0", + "homepage": "https://github.com/agentcommercekit/ack#readme", + "bugs": "https://github.com/agentcommercekit/ack/issues", + "license": "MIT", + "author": { + "name": "Catena Labs", + "url": "https://catenalabs.com" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/agentcommercekit/ack.git", + "directory": "packages/ack-id" + }, + "files": [ + "dist", + "package.json", + "LICENSE", + "README.md" + ], + "type": "module", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "exports": { + "./package.json": "./package.json", + ".": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "./schemas/valibot": { + "types": "./dist/schemas/valibot.d.mts", + "default": "./dist/schemas/valibot.mjs" + }, + "./schemas/zod": { + "types": "./dist/schemas/zod.d.mts", + "default": "./dist/schemas/zod.mjs" + }, + "./a2a": { + "types": "./dist/a2a/index.d.mts", + "default": "./dist/a2a/index.mjs" + }, + "./a2a/schemas/valibot": { + "types": "./dist/a2a/schemas/valibot.d.mts", + "default": "./dist/a2a/schemas/valibot.mjs" + }, + "./a2a/schemas/zod": { + "types": "./dist/a2a/schemas/zod.d.mts", + "default": "./dist/a2a/schemas/zod.mjs" + } + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsdown --config-loader tsx", + "clean": "git clean -fdX .turbo dist", + "dev": "pnpm build --watch --no-clean", + "test": "vitest" + }, + "dependencies": { + "@agentcommercekit/did": "workspace:*", + "@agentcommercekit/jwt": "workspace:*", + "@agentcommercekit/keys": "workspace:*", + "@agentcommercekit/vc": "workspace:*", + "safe-stable-stringify": "catalog:", + "uuid": "catalog:", + "valibot": "catalog:" + }, + "devDependencies": { + "@a2a-js/sdk": "catalog:", + "zod": "catalog:" + }, + "peerDependencies": { + "@a2a-js/sdk": "^0.3.0", + "zod": "^4.0.0" + }, + "peerDependenciesMeta": { + "@a2a-js/sdk": { + "optional": true + }, + "zod": { + "optional": true + } + } +} diff --git a/packages/ack-pay/package.json b/packages/ack-pay/package.json index 6e30d52e..b78d8cce 100644 --- a/packages/ack-pay/package.json +++ b/packages/ack-pay/package.json @@ -1,67 +1,67 @@ -{ - "name": "@agentcommercekit/ack-pay", - "version": "0.11.0", - "homepage": "https://github.com/agentcommercekit/ack#readme", - "bugs": "https://github.com/agentcommercekit/ack/issues", - "license": "MIT", - "author": { - "name": "Catena Labs", - "url": "https://catenalabs.com" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/agentcommercekit/ack.git", - "directory": "packages/ack-pay" - }, - "files": [ - "dist", - "package.json", - "LICENSE", - "README.md" - ], - "type": "module", - "main": "./dist/index.mjs", - "types": "./dist/index.d.mts", - "exports": { - "./package.json": "./package.json", - ".": { - "types": "./dist/index.d.mts", - "default": "./dist/index.mjs" - }, - "./schemas/zod": { - "types": "./dist/schemas/zod.d.mts", - "default": "./dist/schemas/zod.mjs" - }, - "./schemas/valibot": { - "types": "./dist/schemas/valibot.d.mts", - "default": "./dist/schemas/valibot.mjs" - } - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "build": "tsdown", - "clean": "git clean -fdX .turbo dist", - "dev": "pnpm build --watch --no-clean", - "test": "vitest" - }, - "dependencies": { - "@agentcommercekit/did": "workspace:*", - "@agentcommercekit/jwt": "workspace:*", - "@agentcommercekit/keys": "workspace:*", - "@agentcommercekit/vc": "workspace:*", - "valibot": "catalog:" - }, - "devDependencies": { - "zod": "catalog:" - }, - "peerDependencies": { - "zod": "^4.0.0" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } - } -} +{ + "name": "@agentcommercekit/ack-pay", + "version": "0.11.0", + "homepage": "https://github.com/agentcommercekit/ack#readme", + "bugs": "https://github.com/agentcommercekit/ack/issues", + "license": "MIT", + "author": { + "name": "Catena Labs", + "url": "https://catenalabs.com" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/agentcommercekit/ack.git", + "directory": "packages/ack-pay" + }, + "files": [ + "dist", + "package.json", + "LICENSE", + "README.md" + ], + "type": "module", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "exports": { + "./package.json": "./package.json", + ".": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "./schemas/zod": { + "types": "./dist/schemas/zod.d.mts", + "default": "./dist/schemas/zod.mjs" + }, + "./schemas/valibot": { + "types": "./dist/schemas/valibot.d.mts", + "default": "./dist/schemas/valibot.mjs" + } + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsdown --config-loader tsx", + "clean": "git clean -fdX .turbo dist", + "dev": "pnpm build --watch --no-clean", + "test": "vitest" + }, + "dependencies": { + "@agentcommercekit/did": "workspace:*", + "@agentcommercekit/jwt": "workspace:*", + "@agentcommercekit/keys": "workspace:*", + "@agentcommercekit/vc": "workspace:*", + "valibot": "catalog:" + }, + "devDependencies": { + "zod": "catalog:" + }, + "peerDependencies": { + "zod": "^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } +} diff --git a/packages/agentcommercekit/package.json b/packages/agentcommercekit/package.json index ecf89e4a..c92cd8f4 100644 --- a/packages/agentcommercekit/package.json +++ b/packages/agentcommercekit/package.json @@ -1,91 +1,91 @@ -{ - "name": "agentcommercekit", - "version": "0.11.0", - "homepage": "https://github.com/agentcommercekit/ack#readme", - "bugs": "https://github.com/agentcommercekit/ack/issues", - "license": "MIT", - "author": { - "name": "Catena Labs", - "url": "https://catenalabs.com" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/agentcommercekit/ack.git", - "directory": "packages/agentcommercekit" - }, - "files": [ - "dist", - "package.json", - "LICENSE", - "README.md" - ], - "type": "module", - "main": "./dist/index.mjs", - "types": "./dist/index.d.mts", - "exports": { - "./package.json": "./package.json", - ".": { - "types": "./dist/index.d.mts", - "default": "./dist/index.mjs" - }, - "./schemas/valibot": { - "types": "./dist/schemas/valibot.d.mts", - "default": "./dist/schemas/valibot.mjs" - }, - "./schemas/zod": { - "types": "./dist/schemas/zod.d.mts", - "default": "./dist/schemas/zod.mjs" - }, - "./a2a": { - "types": "./dist/a2a/index.d.mts", - "default": "./dist/a2a/index.mjs" - }, - "./a2a/schemas/valibot": { - "types": "./dist/a2a/schemas/valibot.d.mts", - "default": "./dist/a2a/schemas/valibot.mjs" - }, - "./a2a/schemas/zod": { - "types": "./dist/a2a/schemas/zod.d.mts", - "default": "./dist/a2a/schemas/zod.mjs" - } - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "build": "tsdown", - "clean": "git clean -fdX .turbo dist", - "dev": "pnpm build --watch --no-clean", - "test": "vitest" - }, - "dependencies": { - "@agentcommercekit/ack-id": "workspace:*", - "@agentcommercekit/ack-pay": "workspace:*", - "@agentcommercekit/caip": "workspace:*", - "@agentcommercekit/did": "workspace:*", - "@agentcommercekit/jwt": "workspace:*", - "@agentcommercekit/keys": "workspace:*", - "@agentcommercekit/vc": "workspace:*" - }, - "devDependencies": { - "@a2a-js/sdk": "catalog:", - "valibot": "catalog:", - "zod": "catalog:" - }, - "peerDependencies": { - "@a2a-js/sdk": "^0.3.0", - "valibot": "^1.0.0", - "zod": "^4.0.0" - }, - "peerDependenciesMeta": { - "@a2a-js/sdk": { - "optional": true - }, - "valibot": { - "optional": true - }, - "zod": { - "optional": true - } - } -} +{ + "name": "agentcommercekit", + "version": "0.11.0", + "homepage": "https://github.com/agentcommercekit/ack#readme", + "bugs": "https://github.com/agentcommercekit/ack/issues", + "license": "MIT", + "author": { + "name": "Catena Labs", + "url": "https://catenalabs.com" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/agentcommercekit/ack.git", + "directory": "packages/agentcommercekit" + }, + "files": [ + "dist", + "package.json", + "LICENSE", + "README.md" + ], + "type": "module", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "exports": { + "./package.json": "./package.json", + ".": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "./schemas/valibot": { + "types": "./dist/schemas/valibot.d.mts", + "default": "./dist/schemas/valibot.mjs" + }, + "./schemas/zod": { + "types": "./dist/schemas/zod.d.mts", + "default": "./dist/schemas/zod.mjs" + }, + "./a2a": { + "types": "./dist/a2a/index.d.mts", + "default": "./dist/a2a/index.mjs" + }, + "./a2a/schemas/valibot": { + "types": "./dist/a2a/schemas/valibot.d.mts", + "default": "./dist/a2a/schemas/valibot.mjs" + }, + "./a2a/schemas/zod": { + "types": "./dist/a2a/schemas/zod.d.mts", + "default": "./dist/a2a/schemas/zod.mjs" + } + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsdown --config-loader tsx", + "clean": "git clean -fdX .turbo dist", + "dev": "pnpm build --watch --no-clean", + "test": "vitest" + }, + "dependencies": { + "@agentcommercekit/ack-id": "workspace:*", + "@agentcommercekit/ack-pay": "workspace:*", + "@agentcommercekit/caip": "workspace:*", + "@agentcommercekit/did": "workspace:*", + "@agentcommercekit/jwt": "workspace:*", + "@agentcommercekit/keys": "workspace:*", + "@agentcommercekit/vc": "workspace:*" + }, + "devDependencies": { + "@a2a-js/sdk": "catalog:", + "valibot": "catalog:", + "zod": "catalog:" + }, + "peerDependencies": { + "@a2a-js/sdk": "^0.3.0", + "valibot": "^1.0.0", + "zod": "^4.0.0" + }, + "peerDependenciesMeta": { + "@a2a-js/sdk": { + "optional": true + }, + "valibot": { + "optional": true + }, + "zod": { + "optional": true + } + } +} diff --git a/packages/caip/package.json b/packages/caip/package.json index c4019ae4..c4d451ba 100644 --- a/packages/caip/package.json +++ b/packages/caip/package.json @@ -1,67 +1,67 @@ -{ - "name": "@agentcommercekit/caip", - "version": "0.11.0", - "homepage": "https://github.com/agentcommercekit/ack#readme", - "bugs": "https://github.com/agentcommercekit/ack/issues", - "license": "MIT", - "author": { - "name": "Catena Labs", - "url": "https://catenalabs.com" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/agentcommercekit/ack.git", - "directory": "packages/caip" - }, - "files": [ - "dist", - "package.json", - "LICENSE", - "README.md" - ], - "type": "module", - "main": "./dist/index.mjs", - "types": "./dist/index.d.mts", - "exports": { - "./package.json": "./package.json", - ".": { - "types": "./dist/index.d.mts", - "default": "./dist/index.mjs" - }, - "./schemas/zod": { - "types": "./dist/schemas/zod.d.mts", - "default": "./dist/schemas/zod.mjs" - }, - "./schemas/valibot": { - "types": "./dist/schemas/valibot.d.mts", - "default": "./dist/schemas/valibot.mjs" - } - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "build": "tsdown", - "clean": "git clean -fdX .turbo dist", - "dev": "pnpm build --watch --no-clean", - "test": "vitest" - }, - "dependencies": {}, - "devDependencies": { - "standard-matchers": "catalog:", - "valibot": "catalog:", - "zod": "catalog:" - }, - "peerDependencies": { - "valibot": "^1.1.0", - "zod": "^4.0.0" - }, - "peerDependenciesMeta": { - "valibot": { - "optional": true - }, - "zod": { - "optional": true - } - } -} +{ + "name": "@agentcommercekit/caip", + "version": "0.11.0", + "homepage": "https://github.com/agentcommercekit/ack#readme", + "bugs": "https://github.com/agentcommercekit/ack/issues", + "license": "MIT", + "author": { + "name": "Catena Labs", + "url": "https://catenalabs.com" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/agentcommercekit/ack.git", + "directory": "packages/caip" + }, + "files": [ + "dist", + "package.json", + "LICENSE", + "README.md" + ], + "type": "module", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "exports": { + "./package.json": "./package.json", + ".": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "./schemas/zod": { + "types": "./dist/schemas/zod.d.mts", + "default": "./dist/schemas/zod.mjs" + }, + "./schemas/valibot": { + "types": "./dist/schemas/valibot.d.mts", + "default": "./dist/schemas/valibot.mjs" + } + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsdown --config-loader tsx", + "clean": "git clean -fdX .turbo dist", + "dev": "pnpm build --watch --no-clean", + "test": "vitest" + }, + "dependencies": {}, + "devDependencies": { + "standard-matchers": "catalog:", + "valibot": "catalog:", + "zod": "catalog:" + }, + "peerDependencies": { + "valibot": "^1.1.0", + "zod": "^4.0.0" + }, + "peerDependenciesMeta": { + "valibot": { + "optional": true + }, + "zod": { + "optional": true + } + } +} diff --git a/packages/did/package.json b/packages/did/package.json index 81fc92b3..0637f575 100644 --- a/packages/did/package.json +++ b/packages/did/package.json @@ -1,72 +1,72 @@ -{ - "name": "@agentcommercekit/did", - "version": "0.11.0", - "homepage": "https://github.com/agentcommercekit/ack#readme", - "bugs": "https://github.com/agentcommercekit/ack/issues", - "license": "MIT", - "author": { - "name": "Catena Labs", - "url": "https://catenalabs.com" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/agentcommercekit/ack.git", - "directory": "packages/did" - }, - "files": [ - "dist", - "package.json", - "LICENSE", - "README.md" - ], - "type": "module", - "main": "./dist/index.mjs", - "types": "./dist/index.d.mts", - "exports": { - "./package.json": "./package.json", - ".": { - "types": "./dist/index.d.mts", - "default": "./dist/index.mjs" - }, - "./schemas/zod": { - "types": "./dist/schemas/zod.d.mts", - "default": "./dist/schemas/zod.mjs" - }, - "./schemas/valibot": { - "types": "./dist/schemas/valibot.d.mts", - "default": "./dist/schemas/valibot.mjs" - } - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "build": "tsdown", - "clean": "git clean -fdX .turbo dist", - "dev": "pnpm build --watch --no-clean", - "test": "vitest" - }, - "dependencies": { - "@agentcommercekit/caip": "workspace:*", - "@agentcommercekit/keys": "workspace:*", - "did-resolver": "4.1.0", - "jwks-did-resolver": "1.1.0", - "key-did-resolver": "4.0.0", - "valibot": "catalog:", - "varint": "6.0.0", - "web-identity-schemas": "catalog:" - }, - "devDependencies": { - "@types/varint": "6.0.3", - "standard-matchers": "catalog:", - "zod": "catalog:" - }, - "peerDependencies": { - "zod": "^4.0.0" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } - } -} +{ + "name": "@agentcommercekit/did", + "version": "0.11.0", + "homepage": "https://github.com/agentcommercekit/ack#readme", + "bugs": "https://github.com/agentcommercekit/ack/issues", + "license": "MIT", + "author": { + "name": "Catena Labs", + "url": "https://catenalabs.com" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/agentcommercekit/ack.git", + "directory": "packages/did" + }, + "files": [ + "dist", + "package.json", + "LICENSE", + "README.md" + ], + "type": "module", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "exports": { + "./package.json": "./package.json", + ".": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "./schemas/zod": { + "types": "./dist/schemas/zod.d.mts", + "default": "./dist/schemas/zod.mjs" + }, + "./schemas/valibot": { + "types": "./dist/schemas/valibot.d.mts", + "default": "./dist/schemas/valibot.mjs" + } + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsdown --config-loader tsx", + "clean": "git clean -fdX .turbo dist", + "dev": "pnpm build --watch --no-clean", + "test": "vitest" + }, + "dependencies": { + "@agentcommercekit/caip": "workspace:*", + "@agentcommercekit/keys": "workspace:*", + "did-resolver": "4.1.0", + "jwks-did-resolver": "1.1.0", + "key-did-resolver": "4.0.0", + "valibot": "catalog:", + "varint": "6.0.0", + "web-identity-schemas": "catalog:" + }, + "devDependencies": { + "@types/varint": "6.0.3", + "standard-matchers": "catalog:", + "zod": "catalog:" + }, + "peerDependencies": { + "zod": "^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } +} diff --git a/packages/jwt/package.json b/packages/jwt/package.json index 3cded613..a0156b0d 100644 --- a/packages/jwt/package.json +++ b/packages/jwt/package.json @@ -1,70 +1,70 @@ -{ - "name": "@agentcommercekit/jwt", - "version": "0.11.0", - "homepage": "https://github.com/agentcommercekit/ack#readme", - "bugs": "https://github.com/agentcommercekit/ack/issues", - "license": "MIT", - "author": { - "name": "Catena Labs", - "url": "https://catenalabs.com" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/agentcommercekit/ack.git", - "directory": "packages/jwt" - }, - "files": [ - "dist", - "package.json", - "LICENSE", - "README.md" - ], - "type": "module", - "main": "./dist/index.mjs", - "types": "./dist/index.d.mts", - "exports": { - "./package.json": "./package.json", - ".": { - "types": "./dist/index.d.mts", - "default": "./dist/index.mjs" - }, - "./schemas/zod": { - "types": "./dist/schemas/zod.d.mts", - "default": "./dist/schemas/zod.mjs" - }, - "./schemas/valibot": { - "types": "./dist/schemas/valibot.d.mts", - "default": "./dist/schemas/valibot.mjs" - } - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "build": "tsdown", - "clean": "git clean -fdX .turbo dist", - "dev": "pnpm build --watch --no-clean", - "test": "vitest" - }, - "dependencies": { - "@agentcommercekit/keys": "workspace:*", - "did-jwt": "8.0.18", - "web-identity-schemas": "catalog:" - }, - "devDependencies": { - "valibot": "catalog:", - "zod": "catalog:" - }, - "peerDependencies": { - "valibot": "^1.0.0", - "zod": "^4.0.0" - }, - "peerDependenciesMeta": { - "valibot": { - "optional": true - }, - "zod": { - "optional": true - } - } -} +{ + "name": "@agentcommercekit/jwt", + "version": "0.11.0", + "homepage": "https://github.com/agentcommercekit/ack#readme", + "bugs": "https://github.com/agentcommercekit/ack/issues", + "license": "MIT", + "author": { + "name": "Catena Labs", + "url": "https://catenalabs.com" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/agentcommercekit/ack.git", + "directory": "packages/jwt" + }, + "files": [ + "dist", + "package.json", + "LICENSE", + "README.md" + ], + "type": "module", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "exports": { + "./package.json": "./package.json", + ".": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "./schemas/zod": { + "types": "./dist/schemas/zod.d.mts", + "default": "./dist/schemas/zod.mjs" + }, + "./schemas/valibot": { + "types": "./dist/schemas/valibot.d.mts", + "default": "./dist/schemas/valibot.mjs" + } + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsdown --config-loader tsx", + "clean": "git clean -fdX .turbo dist", + "dev": "pnpm build --watch --no-clean", + "test": "vitest" + }, + "dependencies": { + "@agentcommercekit/keys": "workspace:*", + "did-jwt": "8.0.18", + "web-identity-schemas": "catalog:" + }, + "devDependencies": { + "valibot": "catalog:", + "zod": "catalog:" + }, + "peerDependencies": { + "valibot": "^1.0.0", + "zod": "^4.0.0" + }, + "peerDependenciesMeta": { + "valibot": { + "optional": true + }, + "zod": { + "optional": true + } + } +} diff --git a/packages/keys/package.json b/packages/keys/package.json index 4477df62..d044e872 100644 --- a/packages/keys/package.json +++ b/packages/keys/package.json @@ -1,63 +1,63 @@ -{ - "name": "@agentcommercekit/keys", - "version": "0.11.0", - "homepage": "https://github.com/agentcommercekit/ack#readme", - "bugs": "https://github.com/agentcommercekit/ack/issues", - "license": "MIT", - "author": { - "name": "Catena Labs", - "url": "https://catenalabs.com" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/agentcommercekit/ack.git", - "directory": "packages/keys" - }, - "files": [ - "dist", - "package.json", - "LICENSE", - "README.md" - ], - "type": "module", - "main": "./dist/index.mjs", - "types": "./dist/index.d.mts", - "exports": { - "./package.json": "./package.json", - ".": { - "types": "./dist/index.d.mts", - "default": "./dist/index.mjs" - }, - "./encoding": { - "types": "./dist/encoding/index.d.mts", - "default": "./dist/encoding/index.mjs" - }, - "./ed25519": { - "types": "./dist/curves/ed25519.d.mts", - "default": "./dist/curves/ed25519.mjs" - }, - "./secp256k1": { - "types": "./dist/curves/secp256k1.d.mts", - "default": "./dist/curves/secp256k1.mjs" - }, - "./secp256r1": { - "types": "./dist/curves/secp256r1.d.mts", - "default": "./dist/curves/secp256r1.mjs" - } - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "build": "tsdown", - "clean": "git clean -fdX .turbo dist", - "dev": "pnpm build --watch --no-clean", - "test": "vitest" - }, - "dependencies": { - "@noble/curves": "2.2.0", - "@solana/codecs-strings": "6.10.0", - "multiformats": "14.0.0", - "uint8arrays": "6.1.1" - } -} +{ + "name": "@agentcommercekit/keys", + "version": "0.11.0", + "homepage": "https://github.com/agentcommercekit/ack#readme", + "bugs": "https://github.com/agentcommercekit/ack/issues", + "license": "MIT", + "author": { + "name": "Catena Labs", + "url": "https://catenalabs.com" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/agentcommercekit/ack.git", + "directory": "packages/keys" + }, + "files": [ + "dist", + "package.json", + "LICENSE", + "README.md" + ], + "type": "module", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "exports": { + "./package.json": "./package.json", + ".": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "./encoding": { + "types": "./dist/encoding/index.d.mts", + "default": "./dist/encoding/index.mjs" + }, + "./ed25519": { + "types": "./dist/curves/ed25519.d.mts", + "default": "./dist/curves/ed25519.mjs" + }, + "./secp256k1": { + "types": "./dist/curves/secp256k1.d.mts", + "default": "./dist/curves/secp256k1.mjs" + }, + "./secp256r1": { + "types": "./dist/curves/secp256r1.d.mts", + "default": "./dist/curves/secp256r1.mjs" + } + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsdown --config-loader tsx", + "clean": "git clean -fdX .turbo dist", + "dev": "pnpm build --watch --no-clean", + "test": "vitest" + }, + "dependencies": { + "@noble/curves": "2.2.0", + "@solana/codecs-strings": "6.10.0", + "multiformats": "14.0.0", + "uint8arrays": "6.1.1" + } +} diff --git a/packages/vc/package.json b/packages/vc/package.json index 7d6c643c..9be0fbba 100644 --- a/packages/vc/package.json +++ b/packages/vc/package.json @@ -1,69 +1,69 @@ -{ - "name": "@agentcommercekit/vc", - "version": "0.11.0", - "homepage": "https://github.com/agentcommercekit/ack#readme", - "bugs": "https://github.com/agentcommercekit/ack/issues", - "license": "MIT", - "author": { - "name": "Catena Labs", - "url": "https://catenalabs.com" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/agentcommercekit/ack.git", - "directory": "packages/vc" - }, - "files": [ - "dist", - "package.json", - "LICENSE", - "README.md" - ], - "type": "module", - "main": "./dist/index.mjs", - "types": "./dist/index.d.mts", - "exports": { - "./package.json": "./package.json", - ".": { - "types": "./dist/index.d.mts", - "default": "./dist/index.mjs" - }, - "./schemas/zod": { - "types": "./dist/schemas/zod.d.mts", - "default": "./dist/schemas/zod.mjs" - }, - "./schemas/valibot": { - "types": "./dist/schemas/valibot.d.mts", - "default": "./dist/schemas/valibot.mjs" - } - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "build": "tsdown", - "clean": "git clean -fdX .turbo dist", - "dev": "pnpm build --watch --no-clean", - "test": "vitest" - }, - "dependencies": { - "@agentcommercekit/did": "workspace:*", - "@agentcommercekit/jwt": "workspace:*", - "@agentcommercekit/keys": "workspace:*", - "bit-buffers": "catalog:", - "did-jwt-vc": "4.0.16", - "valibot": "catalog:", - "web-identity-schemas": "catalog:" - }, - "devDependencies": { - "zod": "catalog:" - }, - "peerDependencies": { - "zod": "^4.0.0" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } - } -} +{ + "name": "@agentcommercekit/vc", + "version": "0.11.0", + "homepage": "https://github.com/agentcommercekit/ack#readme", + "bugs": "https://github.com/agentcommercekit/ack/issues", + "license": "MIT", + "author": { + "name": "Catena Labs", + "url": "https://catenalabs.com" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/agentcommercekit/ack.git", + "directory": "packages/vc" + }, + "files": [ + "dist", + "package.json", + "LICENSE", + "README.md" + ], + "type": "module", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "exports": { + "./package.json": "./package.json", + ".": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "./schemas/zod": { + "types": "./dist/schemas/zod.d.mts", + "default": "./dist/schemas/zod.mjs" + }, + "./schemas/valibot": { + "types": "./dist/schemas/valibot.d.mts", + "default": "./dist/schemas/valibot.mjs" + } + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsdown --config-loader tsx", + "clean": "git clean -fdX .turbo dist", + "dev": "pnpm build --watch --no-clean", + "test": "vitest" + }, + "dependencies": { + "@agentcommercekit/did": "workspace:*", + "@agentcommercekit/jwt": "workspace:*", + "@agentcommercekit/keys": "workspace:*", + "bit-buffers": "catalog:", + "did-jwt-vc": "4.0.16", + "valibot": "catalog:", + "web-identity-schemas": "catalog:" + }, + "devDependencies": { + "zod": "catalog:" + }, + "peerDependencies": { + "zod": "^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } +} diff --git a/tools/api-utils/src/middleware/signed-payload-validator.ts b/tools/api-utils/src/middleware/signed-payload-validator.ts index f0111ebf..4a83284e 100644 --- a/tools/api-utils/src/middleware/signed-payload-validator.ts +++ b/tools/api-utils/src/middleware/signed-payload-validator.ts @@ -1,103 +1,76 @@ -import { isDidUri, type DidUri, type Resolvable } from "@agentcommercekit/did" -import { isJwtString, type JwtString } from "@agentcommercekit/jwt" -import type { MiddlewareHandler, ValidationTargets } from "hono" -import { env } from "hono/adapter" -import { validator } from "hono/validator" -import * as v from "valibot" - -import { validatePayload } from "../validate-payload" - -interface SignedPayloadEnv { - Variables: { - resolver: Resolvable - } -} - -interface ValidatedSignedPayload { - issuer: DidUri - body: T -} - -const signedPayloadSchema = v.object({ - payload: v.custom( - (input: unknown) => typeof input === "string" && isJwtString(input), - "Invalid JWT format", - ), -}) - -/** - * A validation middleware for signed JWT payloads. This will parse the JWT - * payload, ensure it is properly signed and not expired, and validate it - * against the provided schema. - * - * @example - * ```ts - * app.post("/", signedPayloadValidator("json", bodySchema), (c) => { - * const { parsed, payload } = c.req.valid("json") - * - * parsed.issuer // did:web:example.com - * payload // { name: "John Doe", age: 30 } - * }) - * ``` - */ -export const signedPayloadValidator = ( - target: keyof ValidationTargets, - schema: S, -): MiddlewareHandler< - SignedPayloadEnv, - string, - { out: { json: ValidatedSignedPayload> } } -> => - validator( - target, - async (value, c): Promise>> => { - const didResolver = c.get("resolver") - - try { - const data = v.parse(signedPayloadSchema, value) - const { parsed, body } = await validatePayload( - data.payload, - schema, - didResolver, - ) - - // Enforces a DID for the issuer - if (!isDidUri(parsed.issuer)) { - throw new Error("Invalid issuer") - } - - return { - issuer: parsed.issuer, - body, - } - } catch (error) { - /** - * Local-development escape hatch: allow a raw unsigned payload plus an - * `X-Payload-Issuer` header to bypass the JWT signature check. This is an - * authentication bypass, so it is gated behind an explicit, default-off - * `ALLOW_UNSIGNED_PAYLOADS` flag (NOT `NODE_ENV`, which is commonly set to - * "development" by accident in deployed environments). Never enable it - * outside local development. - */ - if ( - env<{ ALLOW_UNSIGNED_PAYLOADS?: string }>(c) - .ALLOW_UNSIGNED_PAYLOADS === "true" - ) { - const issuer = c.req.header("X-Payload-Issuer") - const parsedPayload = v.safeParse(schema, value) - if (isDidUri(issuer) && parsedPayload.success) { - console.warn( - `[signed-payload-validator] SECURITY: accepting an UNSIGNED payload (issuer "${issuer}" from the X-Payload-Issuer header) because ALLOW_UNSIGNED_PAYLOADS is enabled. Never enable this outside local development.`, - ) - return { - issuer, - body: parsedPayload.output, - } - } - } - - // Otherwise, rethrow the error - throw error - } - }, - ) +import { isDidUri, type DidUri, type Resolvable } from "@agentcommercekit/did" +import { isJwtString, type JwtString } from "@agentcommercekit/jwt" +import type { MiddlewareHandler, ValidationTargets } from "hono" +import { validator } from "hono/validator" +// SECURITY FIX: Removed 'env' import from 'hono/adapter' as environment-based authentication bypasses are explicitly forbidden. +import * as v from "valibot" + +import { validatePayload } from "../validate-payload" + +interface SignedPayloadEnv { + Variables: { + resolver: Resolvable + } +} + +interface ValidatedSignedPayload { + issuer: DidUri + body: T +} + +const signedPayloadSchema = v.object({ + payload: v.custom( + (input: unknown) => typeof input === "string" && isJwtString(input), + "Invalid JWT format", + ), +}) + +/** + * A validation middleware for signed JWT payloads. This will parse the JWT + * payload, ensure it is properly signed and not expired, and validate it + * against the provided schema. + * + * @example + * ```ts + * app.post("/", signedPayloadValidator("json", bodySchema), (c) => { + * const { parsed, payload } = c.req.valid("json") + * + * parsed.issuer // did:web:example.com + * payload // { name: "John Doe", age: 30 } + * }) + * ``` + */ +export const signedPayloadValidator = ( + target: keyof ValidationTargets, + schema: S, +): MiddlewareHandler< + SignedPayloadEnv, + string, + { out: { json: ValidatedSignedPayload> } } +> => + validator( + target, + async (value, c): Promise>> => { + const didResolver = c.get("resolver") + + // SECURITY FIX: Removed the try-catch block and the `ALLOW_UNSIGNED_PAYLOADS` escape hatch. + // We now strictly require a cryptographically signed JWT envelope for all protected routes. + // Spoofed `X-Payload-Issuer` headers with raw bodies are no longer accepted under any environment condition. + const data = v.parse(signedPayloadSchema, value) + const { parsed, body } = await validatePayload( + data.payload, + schema, + didResolver, + ) + + // Enforces a DID for the issuer + if (!isDidUri(parsed.issuer)) { + throw new Error("Invalid issuer") + } + + return { + issuer: parsed.issuer, + body, + } + }, + ) \ No newline at end of file From 2c7d03e4c825ec7293feadcc2bd34398111f1b7b Mon Sep 17 00:00:00 2001 From: mertcano <35747700+mertcano@users.noreply.github.com> Date: Sun, 16 Aug 2026 15:13:25 +0300 Subject: [PATCH 2/4] Clean up comments in .env.example Removed comments regarding unsigned payloads in .env.example. --- examples/issuer/.env.example | 6 ------ 1 file changed, 6 deletions(-) diff --git a/examples/issuer/.env.example b/examples/issuer/.env.example index 167ac780..9e1254ff 100644 --- a/examples/issuer/.env.example +++ b/examples/issuer/.env.example @@ -1,8 +1,2 @@ BASE_URL="http://localhost:3456" NODE_ENV="development" - -# Local-development ONLY: when "true", accept unsigned request payloads with an -# `X-Payload-Issuer` header instead of a signed JWT. This bypasses signature -# verification (authentication), so it is off by default. Never enable it in a -# deployed environment. -# ALLOW_UNSIGNED_PAYLOADS="true" From 7d802ba37364115c5d75747e0bd711b9b1debaa6 Mon Sep 17 00:00:00 2001 From: mertcano <35747700+mertcano@users.noreply.github.com> Date: Tue, 18 Aug 2026 00:31:10 +0300 Subject: [PATCH 3/4] Add files via upload --- docs/demos/example-identity.mdx | 141 ++++------- examples/issuer/README.md | 227 ++++++------------ .../middleware/signed-payload-validator.ts | 7 +- 3 files changed, 127 insertions(+), 248 deletions(-) diff --git a/docs/demos/example-identity.mdx b/docs/demos/example-identity.mdx index 0a0d7321..ffd30f93 100644 --- a/docs/demos/example-identity.mdx +++ b/docs/demos/example-identity.mdx @@ -22,165 +22,124 @@ From within the issuer example directory (e.g., from project root: `./examples/i ```sh pnpm run setup -``` - -## Running the Server +Running the Server Start the API server locally: -```sh +Bash pnpm run dev -``` - -The server runs at: [http://localhost:3456](http://localhost:3456) - -## API Endpoints -### Authentication +The server runs at: http://localhost:3456 -All endpoints require a **signed payload** (JWT) proving DID ownership. During local development, include the `X-Payload-Issuer` header with a resolvable DID-URI to bypass signature verification. +API Endpoints +Authentication +Mutation endpoints require a signed payload (JWT) proving DID ownership. -### Response Format +Response Format +Credential endpoints use this JSON structure: -All API responses use this JSON structure: - -```json +JSON { "ok": true, "data": } -``` or -```json +JSON { "ok": false, "error": "Error message" } -``` - -### Controller Credential Endpoints -#### `POST /credentials/controller` +GET /status/:listId returns a bare BitstringStatusListCredential and GET /.well-known/did.json returns a bare DID document without the { ok, data } envelope. +Controller Credential Endpoints +POST /credentials/controller Issue a ControllerCredential proving DID control. -**Sample cURL:** +Sample cURL: -```sh +Bash curl --request POST \ --url http://localhost:3456/credentials/controller \ --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:controller' \ --data '{ - "controller": "did:web:0.0.0.0%3A3458:controller", - "subject": "did:web:0.0.0.0%3A3458:agent" + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" }' -``` - -#### `GET /credentials/controller/:id` - +GET /credentials/controller/:id Retrieve a ControllerCredential by ID. -**Sample cURL:** +Sample cURL: -```sh +Bash curl --request GET \ --url http://localhost:3456/credentials/controller/abc123 -``` - -#### `DELETE /credentials/controller` - +DELETE /credentials/controller Revoke a ControllerCredential by ID. -**Sample cURL:** +Sample cURL: -```sh +Bash curl --request DELETE \ --url http://localhost:3456/credentials/controller \ --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:controller' \ --data '{ - "id": "abc123" + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" }' -``` - -### Payment Receipt Endpoints - -#### `POST /credentials/receipts` - -Issue a PaymentReceiptCredential. +Payment Receipt Endpoints +POST /credentials/receipts +Issue a PaymentReceiptCredential. -**Sample cURL:** +Sample cURL: -```sh +Bash curl --request POST \ --url http://localhost:3456/credentials/receipts \ --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:wallet' \ --data '{ - "metadata": { "txHash": "0x123abc456def" }, - "payerDid": "did:web:0.0.0.0%3A3458:wallet", - "paymentRequestToken": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...", - "paymentOptionId": "option1" + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" }' -``` - -#### `GET /credentials/receipts/:id` +GET /credentials/receipts/:id +Retrieve a PaymentReceiptCredential by ID. -Retrieve a PaymentReceiptCredential by ID. +Sample cURL: -**Sample cURL:** - -```sh +Bash curl --request GET \ --url http://localhost:3456/credentials/receipts/abc123 -``` - -#### `DELETE /credentials/receipts` - +DELETE /credentials/receipts Revoke a PaymentReceiptCredential by ID. -**Sample cURL:** +Sample cURL: -```sh +Bash curl --request DELETE \ --url http://localhost:3456/credentials/receipts \ --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:payee' \ --data '{ - "id": "abc123" + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" }' -``` - -### Status List Endpoint - -#### `GET /status/:listId` - +Status List Endpoint +GET /status/:listId Retrieve Bitstring Status List credential for revocation checks. -**Sample cURL:** +Sample cURL: -```sh +Bash curl --request GET \ - --url http://localhost:3456/status/1 -``` - -### DID Document Endpoint - -#### `GET /.well-known/did.json` - + --url http://localhost:3456/status/0 +DID Document Endpoint +GET /.well-known/did.json Retrieve the issuer's DID Document. -**Sample cURL:** +Sample cURL: -```sh +Bash curl --request GET \ --url http://localhost:3456/.well-known/did.json -``` - -## References + +References +Verifiable Credentials Data Model Specification -- [Verifiable Credentials Data Model Specification](https://www.w3.org/TR/vc-data-model/) -- [This Example's Source Code](https://github.com/agentcommercekit/ack/tree/main/examples/issuer) +This Example's Source Code \ No newline at end of file diff --git a/examples/issuer/README.md b/examples/issuer/README.md index 6bf94e08..26ed07ba 100644 --- a/examples/issuer/README.md +++ b/examples/issuer/README.md @@ -1,3 +1,4 @@ +```markdown # ACK: Credential Issuer Example This example showcases a **Credential Issuer** for [ACK-ID](https://www.agentcommercekit.com/ack-id) and [ACK-Pay](https://www.agentcommercekit.com/ack-pay) Verifiable Credentials. This API is built with [Hono](https://hono.dev). @@ -13,66 +14,47 @@ This issuer supports credential revocation using [Bitstring Status List](https:/ ```sh pnpm run setup -``` - -## Running the server - -```sh +Running the server +Bash pnpm run dev -``` - -The server will be available at - -### Database +The server will be available at http://localhost:3456 +Database To simplify the development experience, this API uses a SQLite database. In a production environment, we recommend using a database with native bitwise operations like PostgreSQL. -## API Endpoints - -### Authentication - -All API endpoints require a **signed payload** to prove ownership of the DIDs involved. This payload is a JWT of the request parameters, signed using your DID. - -In local development, you can bypass the signed payload requirement by setting `ALLOW_UNSIGNED_PAYLOADS="true"` in your `.env` and sending an `X-Payload-Issuer` header with a DID-URI as its value. This simulates that you signed the payload, and is **off by default** — never enable it outside local development, as it disables authentication. NOTE: This `did` MUST be resolvable, which makes using the [`local-did-host`](../local-did-host/) server helpful. - -### Response format +API Endpoints +Authentication +All API endpoints require a signed payload to prove ownership of the DIDs involved. This payload is a JWT of the request parameters, signed using your DID. +Response format All API responses respond as JSON objects with the following format: -```json +JSON { "ok": true, "data": } -``` - or -```json +JSON { "ok": false, "error": "string error message" } -``` - -### Controller Credential Endpoints - -#### POST /credentials/controller - +Controller Credential Endpoints +POST /credentials/controller Create a new ControllerCredential that proves one DID controls another -**Request Payload**, signed by the controller +Request Payload, signed by the controller -```ts +TypeScript SignedPayload<{ controller: "did:..." subject: "did:..." }> -``` - -**Response Body** +Response Body -```json +JSON { "ok": true, "data": { @@ -82,28 +64,21 @@ SignedPayload<{ "jwt": "credential-jwt" } } -``` +Sample cURL -**Sample cURL** - -```sh +Bash curl --request POST \ --url http://localhost:3456/credentials/controller \ --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:controller' \ --data '{ - "controller": "did:web:0.0.0.0%3A3458:controller", - "subject": "did:web:0.0.0.0%3A3458:agent" + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" }' -``` - -#### GET /credentials/controller/:id - +GET /credentials/controller/:id Retrieve a credential by its identifier -**Response Body** +Response Body -```json +JSON { "ok": true, "data": { @@ -113,57 +88,43 @@ Retrieve a credential by its identifier "jwt": "credential-jwt" } } -``` +Sample cURL -**Sample cURL** - -```sh +Bash curl --request GET \ --url http://localhost:3456/credentials/controller/abc123 -``` - -#### DELETE /credentials/controller - +DELETE /credentials/controller Revoke a credential by its identifier -**Request Payload**, signed by the controller +Request Payload, signed by the controller -```ts +TypeScript SignedPayload<{ id: "credential-id" }> -``` - -**Response Body** +Response Body -```json +JSON { "ok": true, "data": null } -``` - -**Sample cURL** +Sample cURL -```sh +Bash curl --request DELETE \ --url http://localhost:3456/credentials/controller \ --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:controller' \ --data '{ - "id": "abc123" + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" }' -``` - -### Payment Receipt Endpoints - -#### POST /credentials/receipts - +Payment Receipt Endpoints +POST /credentials/receipts Generate a payment receipt credential that proves a payment was made -**Request Payload**, signed by the wallet that made the payment: +Request Payload, signed by the wallet that made the payment: -```ts +TypeScript SignedPayload<{ metadata: { txHash: "0x123..." @@ -172,11 +133,9 @@ SignedPayload<{ paymentRequestToken: "jwt-token" paymentOptionId: "option-id" }> -``` - -**Response Body** +Response Body -```json +JSON { "ok": true, "data": { @@ -186,32 +145,21 @@ SignedPayload<{ "jwt": "credential-jwt" } } -``` +Sample cURL -**Sample cURL** - -```sh +Bash curl --request POST \ --url http://localhost:3456/credentials/receipts \ --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:wallet' \ --data '{ - "metadata": { - "txHash": "0x123abc456def" - }, - "payerDid": "did:web:0.0.0.0%3A3458:wallet", - "paymentRequestToken": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...", - "paymentOptionId": "option1" + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" }' -``` - -#### GET /credentials/receipts/:id - +GET /credentials/receipts/:id Retrieve a payment receipt credential by its identifier -**Response Body** +Response Body -```json +JSON { "ok": true, "data": { @@ -221,95 +169,75 @@ Retrieve a payment receipt credential by its identifier "jwt": "credential-jwt" } } -``` +Sample cURL -**Sample cURL** - -```sh +Bash curl --request GET \ --url http://localhost:3456/credentials/receipts/abc123 -``` - -#### DELETE /credentials/receipts - +DELETE /credentials/receipts Revokes a payment receipt credential by flipping the bit on the credential's Status List. For demo purposes, we only allow the original payment request token issuer to revoke the receipt. -**Request Payload**, signed by the original payment request token issuer +Request Payload, signed by the original payment request token issuer -```ts +TypeScript SignedPayload<{ id: "receipt-id" }> -``` - -**Response Body** +Response Body -```json +JSON { "ok": true, "data": null } -``` - -**Sample cURL** +Sample cURL -```sh +Bash curl --request DELETE \ --url http://localhost:3456/credentials/receipts \ --header 'Content-Type: application/json' \ - --header 'X-Payload-Issuer: did:web:0.0.0.0%3A3458:payee' \ --data '{ - "id": "abc123" + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" }' -``` - -### Status Endpoints - -#### GET /status/:listId - +Status Endpoints +GET /status/:listId Retrieve a Bitstring Status List credential for checking revocation status. Unlike the other endpoints, this one returns the signed credential directly -rather than in the `{ ok, data }` envelope. Verifiers dereference this URL as -the credential's `statusListCredential` and expect the credential itself; a +rather than in the { ok, data } envelope. Verifiers dereference this URL as +the credential's statusListCredential and expect the credential itself; a wrapped body cannot be verified, so revocation checks would fail. -**Response Body** +Response Body -```json +JSON { - "@context": ["https://www.w3.org/2018/credentials/v1"], - "id": "http://localhost:3456/status/1", + "@context": ["[https://www.w3.org/2018/credentials/v1](https://www.w3.org/2018/credentials/v1)"], + "id": "http://localhost:3456/status/0", "type": ["VerifiableCredential", "BitstringStatusListCredential"], "issuer": { "id": "did:web:..." }, "credentialSubject": { - "id": "http://localhost:3456/status/1#list", + "id": "http://localhost:3456/status/0#list", "type": "BitstringStatusList", "statusPurpose": "revocation", "encodedList": "..." }, "proof": { "type": "JwtProof2020", "jwt": "jwt-string" } } -``` +Sample cURL -**Sample cURL** - -```sh +Bash curl --request GET \ - --url http://localhost:3456/status/1 -``` - -### DID Endpoints - -#### GET /.well-known/did.json - + --url http://localhost:3456/status/0 +DID Endpoints +GET /.well-known/did.json Return the DID document for the issuer -**Response Body** +Response Body -```json +JSON { "@context": [...], "id": "did:web:...", @@ -317,15 +245,10 @@ Return the DID document for the issuer "authentication": [...], "assertionMethod": [...] } -``` - -**Sample cURL** +Sample cURL -```sh +Bash curl --request GET \ --url http://localhost:3456/.well-known/did.json -``` - -## License (MIT) - -Copyright (c) 2025 [Catena Labs, Inc](https://catenalabs.com). +License (MIT) +Copyright (c) 2025 Catena Labs, Inc. \ No newline at end of file diff --git a/tools/api-utils/src/middleware/signed-payload-validator.ts b/tools/api-utils/src/middleware/signed-payload-validator.ts index 4a83284e..1007db08 100644 --- a/tools/api-utils/src/middleware/signed-payload-validator.ts +++ b/tools/api-utils/src/middleware/signed-payload-validator.ts @@ -2,10 +2,10 @@ import { isDidUri, type DidUri, type Resolvable } from "@agentcommercekit/did" import { isJwtString, type JwtString } from "@agentcommercekit/jwt" import type { MiddlewareHandler, ValidationTargets } from "hono" import { validator } from "hono/validator" -// SECURITY FIX: Removed 'env' import from 'hono/adapter' as environment-based authentication bypasses are explicitly forbidden. import * as v from "valibot" import { validatePayload } from "../validate-payload" +import { unauthorized } from "../exceptions" interface SignedPayloadEnv { Variables: { @@ -53,9 +53,6 @@ export const signedPayloadValidator = ( async (value, c): Promise>> => { const didResolver = c.get("resolver") - // SECURITY FIX: Removed the try-catch block and the `ALLOW_UNSIGNED_PAYLOADS` escape hatch. - // We now strictly require a cryptographically signed JWT envelope for all protected routes. - // Spoofed `X-Payload-Issuer` headers with raw bodies are no longer accepted under any environment condition. const data = v.parse(signedPayloadSchema, value) const { parsed, body } = await validatePayload( data.payload, @@ -65,7 +62,7 @@ export const signedPayloadValidator = ( // Enforces a DID for the issuer if (!isDidUri(parsed.issuer)) { - throw new Error("Invalid issuer") + throw unauthorized("Invalid issuer") } return { From 22f23bf43bdcb2852ef6a3802c5e7e7d7ffc6893 Mon Sep 17 00:00:00 2001 From: mertcano <35747700+mertcano@users.noreply.github.com> Date: Tue, 18 Aug 2026 01:04:55 +0300 Subject: [PATCH 4/4] Add files via upload --- docs/demos/example-identity.mdx | 327 ++++++++++-------- examples/issuer/README.md | 576 ++++++++++++++++++-------------- 2 files changed, 504 insertions(+), 399 deletions(-) diff --git a/docs/demos/example-identity.mdx b/docs/demos/example-identity.mdx index ffd30f93..073fda3d 100644 --- a/docs/demos/example-identity.mdx +++ b/docs/demos/example-identity.mdx @@ -1,145 +1,182 @@ ---- -title: Credential Issuer Example -description: "An example of how to operate a Verifiable Credential Issuer." ---- - -## Overview - -This example demonstrates an [ACK-ID](https://agentcommercekit.com/ack-id) and [ACK-Pay](https://agentcommercekit.com/ack-pay) Verifiable Credential issuer API built with [Hono](https://hono.dev). - -The API supports issuing, verifying, and revoking two primary credential types: - -- **`ControllerCredential`**: proves ownership of DIDs (part of ACK-ID). -- **`PaymentReceiptCredential`**: provides proof of payment meeting a Payment Request (part of ACK-Pay). - -The issuer implements credential revocation using [Bitstring Status List](https://www.w3.org/TR/vc-bitstring-status-list/), a privacy-preserving and efficient revocation list method. - -## Installation and Setup - -Install dependencies and prepare your environment. - -From within the issuer example directory (e.g., from project root: `./examples/issuer`) execute the setup: - -```sh -pnpm run setup - -Running the Server -Start the API server locally: - -Bash -pnpm run dev - -The server runs at: http://localhost:3456 - -API Endpoints -Authentication -Mutation endpoints require a signed payload (JWT) proving DID ownership. - -Response Format -Credential endpoints use this JSON structure: - -JSON -{ - "ok": true, - "data": -} - -or - -JSON -{ - "ok": false, - "error": "Error message" -} - -GET /status/:listId returns a bare BitstringStatusListCredential and GET /.well-known/did.json returns a bare DID document without the { ok, data } envelope. - -Controller Credential Endpoints -POST /credentials/controller -Issue a ControllerCredential proving DID control. - -Sample cURL: - -Bash -curl --request POST \ - --url http://localhost:3456/credentials/controller \ - --header 'Content-Type: application/json' \ - --data '{ - "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" -}' -GET /credentials/controller/:id -Retrieve a ControllerCredential by ID. - -Sample cURL: - -Bash -curl --request GET \ - --url http://localhost:3456/credentials/controller/abc123 -DELETE /credentials/controller -Revoke a ControllerCredential by ID. - -Sample cURL: - -Bash -curl --request DELETE \ - --url http://localhost:3456/credentials/controller \ - --header 'Content-Type: application/json' \ - --data '{ - "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" -}' -Payment Receipt Endpoints -POST /credentials/receipts -Issue a PaymentReceiptCredential. - -Sample cURL: - -Bash -curl --request POST \ - --url http://localhost:3456/credentials/receipts \ - --header 'Content-Type: application/json' \ - --data '{ - "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" -}' -GET /credentials/receipts/:id -Retrieve a PaymentReceiptCredential by ID. - -Sample cURL: - -Bash -curl --request GET \ - --url http://localhost:3456/credentials/receipts/abc123 -DELETE /credentials/receipts -Revoke a PaymentReceiptCredential by ID. - -Sample cURL: - -Bash -curl --request DELETE \ - --url http://localhost:3456/credentials/receipts \ - --header 'Content-Type: application/json' \ - --data '{ - "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" -}' -Status List Endpoint -GET /status/:listId -Retrieve Bitstring Status List credential for revocation checks. - -Sample cURL: - -Bash -curl --request GET \ - --url http://localhost:3456/status/0 -DID Document Endpoint -GET /.well-known/did.json -Retrieve the issuer's DID Document. - -Sample cURL: - -Bash -curl --request GET \ - --url http://localhost:3456/.well-known/did.json - -References -Verifiable Credentials Data Model Specification - -This Example's Source Code \ No newline at end of file +--- +title: Credential Issuer Example +description: "An example of how to operate a Verifiable Credential Issuer." +--- + +## Overview + +This example demonstrates an [ACK-ID](https://agentcommercekit.com/ack-id) and [ACK-Pay](https://agentcommercekit.com/ack-pay) Verifiable Credential issuer API built with [Hono](https://hono.dev). + +The API supports issuing, verifying, and revoking two primary credential types: + +- **`ControllerCredential`**: Proves ownership of DIDs as part of ACK-ID. +- **`PaymentReceiptCredential`**: Provides proof of payment that satisfies a Payment Request as part of ACK-Pay. + +The issuer implements credential revocation using [Bitstring Status List](https://www.w3.org/TR/vc-bitstring-status-list/), a privacy-preserving and efficient revocation-list method. + +## Installation and Setup + +Install dependencies and prepare your environment. From within the issuer example directory (for example, `./examples/issuer` from the project root), execute: + +```sh +pnpm run setup +``` + +## Running the Server + +Start the API server locally: + +```sh +pnpm run dev +``` + +The server runs at . + +## API Endpoints + +### Authentication + +Mutation endpoints require a signed payload (JWT) proving DID ownership. Read-only GET endpoints do not require a signed payload. + +### Response Format + +Credential endpoints use this JSON structure: + +```json +{ + "ok": true, + "data": "" +} +``` + +or: + +```json +{ + "ok": false, + "error": "Error message" +} +``` + +`GET /status/:listId` returns a bare `BitstringStatusListCredential`, and `GET /.well-known/did.json` returns a bare DID document without the `{ ok, data }` envelope. + +## Controller Credential Endpoints + +### POST /credentials/controller + +Issue a `ControllerCredential` proving DID control. + +#### Sample cURL + +```sh +# NOTE: Replace this non-runnable placeholder with a valid signed JWT. +curl --request POST \ + --url http://localhost:3456/credentials/controller \ + --header 'Content-Type: application/json' \ + --data '{ + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" +}' +``` + +### GET /credentials/controller/:id + +Retrieve a `ControllerCredential` by ID. + +#### Sample cURL + +```sh +curl --request GET \ + --url http://localhost:3456/credentials/controller/abc123 +``` + +### DELETE /credentials/controller + +Revoke a `ControllerCredential` by ID. + +#### Sample cURL + +```sh +# NOTE: Replace this non-runnable placeholder with a valid signed JWT. +curl --request DELETE \ + --url http://localhost:3456/credentials/controller \ + --header 'Content-Type: application/json' \ + --data '{ + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" +}' +``` + +## Payment Receipt Endpoints + +### POST /credentials/receipts + +Issue a `PaymentReceiptCredential`. + +#### Sample cURL + +```sh +# NOTE: Replace this non-runnable placeholder with a valid signed JWT. +curl --request POST \ + --url http://localhost:3456/credentials/receipts \ + --header 'Content-Type: application/json' \ + --data '{ + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" +}' +``` + +### GET /credentials/receipts/:id + +Retrieve a `PaymentReceiptCredential` by ID. + +#### Sample cURL + +```sh +curl --request GET \ + --url http://localhost:3456/credentials/receipts/abc123 +``` + +### DELETE /credentials/receipts + +Revoke a `PaymentReceiptCredential` by ID. + +#### Sample cURL + +```sh +# NOTE: Replace this non-runnable placeholder with a valid signed JWT. +curl --request DELETE \ + --url http://localhost:3456/credentials/receipts \ + --header 'Content-Type: application/json' \ + --data '{ + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" +}' +``` + +## Status List Endpoint + +### GET /status/:listId + +Retrieve the Bitstring Status List credential for revocation checks. + +#### Sample cURL + +```sh +curl --request GET \ + --url http://localhost:3456/status/0 +``` + +## DID Document Endpoint + +### GET /.well-known/did.json + +Retrieve the issuer's DID Document. + +#### Sample cURL + +```sh +curl --request GET \ + --url http://localhost:3456/.well-known/did.json +``` + +## References + +- [Verifiable Credentials Data Model Specification](https://www.w3.org/TR/vc-data-model/) +- [This example's source code](https://github.com/agentcommercekit/ack/tree/main/examples/issuer) diff --git a/examples/issuer/README.md b/examples/issuer/README.md index 26ed07ba..f14837ad 100644 --- a/examples/issuer/README.md +++ b/examples/issuer/README.md @@ -1,254 +1,322 @@ -```markdown -# ACK: Credential Issuer Example - -This example showcases a **Credential Issuer** for [ACK-ID](https://www.agentcommercekit.com/ack-id) and [ACK-Pay](https://www.agentcommercekit.com/ack-pay) Verifiable Credentials. This API is built with [Hono](https://hono.dev). - -The API allows for the issuance, verification, and revocation of the following credential types: - -- `ControllerCredential`: ACK-ID credentials that prove DID ownership hierarchies. -- `PaymentReceiptCredential`: ACK-Pay credentials that provide proof of payment that satisfies a given Payment Request. - -This issuer supports credential revocation using [Bitstring Status List](https://www.w3.org/TR/vc-bitstring-status-list/), which is a privacy-preserving, space-efficient mechanism for maintaining a credential revocation list. - -## Getting Started - -```sh -pnpm run setup -Running the server -Bash -pnpm run dev -The server will be available at http://localhost:3456 - -Database -To simplify the development experience, this API uses a SQLite database. In a production environment, we recommend using a database with native bitwise operations like PostgreSQL. - -API Endpoints -Authentication -All API endpoints require a signed payload to prove ownership of the DIDs involved. This payload is a JWT of the request parameters, signed using your DID. - -Response format -All API responses respond as JSON objects with the following format: - -JSON -{ - "ok": true, - "data": -} -or - -JSON -{ - "ok": false, - "error": "string error message" -} -Controller Credential Endpoints -POST /credentials/controller -Create a new ControllerCredential that proves one DID controls another - -Request Payload, signed by the controller - -TypeScript -SignedPayload<{ - controller: "did:..." - subject: "did:..." -}> -Response Body - -JSON -{ - "ok": true, - "data": { - "credential": { - ... - } - "jwt": "credential-jwt" - } -} -Sample cURL - -Bash -curl --request POST \ - --url http://localhost:3456/credentials/controller \ - --header 'Content-Type: application/json' \ - --data '{ - "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" -}' -GET /credentials/controller/:id -Retrieve a credential by its identifier - -Response Body - -JSON -{ - "ok": true, - "data": { - "credential": { - ... - } - "jwt": "credential-jwt" - } -} -Sample cURL - -Bash -curl --request GET \ - --url http://localhost:3456/credentials/controller/abc123 -DELETE /credentials/controller -Revoke a credential by its identifier - -Request Payload, signed by the controller - -TypeScript -SignedPayload<{ - id: "credential-id" -}> -Response Body - -JSON -{ - "ok": true, - "data": null -} -Sample cURL - -Bash -curl --request DELETE \ - --url http://localhost:3456/credentials/controller \ - --header 'Content-Type: application/json' \ - --data '{ - "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" -}' -Payment Receipt Endpoints -POST /credentials/receipts -Generate a payment receipt credential that proves a payment was made - -Request Payload, signed by the wallet that made the payment: - -TypeScript -SignedPayload<{ - metadata: { - txHash: "0x123..." - } - payerDid: "did:..." - paymentRequestToken: "jwt-token" - paymentOptionId: "option-id" -}> -Response Body - -JSON -{ - "ok": true, - "data": { - "credential": { - ... - } - "jwt": "credential-jwt" - } -} -Sample cURL - -Bash -curl --request POST \ - --url http://localhost:3456/credentials/receipts \ - --header 'Content-Type: application/json' \ - --data '{ - "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" -}' -GET /credentials/receipts/:id -Retrieve a payment receipt credential by its identifier - -Response Body - -JSON -{ - "ok": true, - "data": { - "credential": { - ... - } - "jwt": "credential-jwt" - } -} -Sample cURL - -Bash -curl --request GET \ - --url http://localhost:3456/credentials/receipts/abc123 -DELETE /credentials/receipts -Revokes a payment receipt credential by flipping the bit on the credential's Status List. - -For demo purposes, we only allow the original payment request token issuer to revoke the receipt. - -Request Payload, signed by the original payment request token issuer - -TypeScript -SignedPayload<{ - id: "receipt-id" -}> -Response Body - -JSON -{ - "ok": true, - "data": null -} -Sample cURL - -Bash -curl --request DELETE \ - --url http://localhost:3456/credentials/receipts \ - --header 'Content-Type: application/json' \ - --data '{ - "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" -}' -Status Endpoints -GET /status/:listId -Retrieve a Bitstring Status List credential for checking revocation status. - -Unlike the other endpoints, this one returns the signed credential directly -rather than in the { ok, data } envelope. Verifiers dereference this URL as -the credential's statusListCredential and expect the credential itself; a -wrapped body cannot be verified, so revocation checks would fail. - -Response Body - -JSON -{ - "@context": ["[https://www.w3.org/2018/credentials/v1](https://www.w3.org/2018/credentials/v1)"], - "id": "http://localhost:3456/status/0", - "type": ["VerifiableCredential", "BitstringStatusListCredential"], - "issuer": { "id": "did:web:..." }, - "credentialSubject": { - "id": "http://localhost:3456/status/0#list", - "type": "BitstringStatusList", - "statusPurpose": "revocation", - "encodedList": "..." - }, - "proof": { "type": "JwtProof2020", "jwt": "jwt-string" } -} -Sample cURL - -Bash -curl --request GET \ - --url http://localhost:3456/status/0 -DID Endpoints -GET /.well-known/did.json -Return the DID document for the issuer - -Response Body - -JSON -{ - "@context": [...], - "id": "did:web:...", - "verificationMethod": [...], - "authentication": [...], - "assertionMethod": [...] -} -Sample cURL - -Bash -curl --request GET \ - --url http://localhost:3456/.well-known/did.json -License (MIT) -Copyright (c) 2025 Catena Labs, Inc. \ No newline at end of file +# ACK: Credential Issuer Example + +This example showcases a **Credential Issuer** for [ACK-ID](https://www.agentcommercekit.com/ack-id) and [ACK-Pay](https://www.agentcommercekit.com/ack-pay) Verifiable Credentials. This API is built with [Hono](https://hono.dev). + +The API allows for the issuance, verification, and revocation of the following credential types: + +- `ControllerCredential`: ACK-ID credentials that prove DID ownership hierarchies. +- `PaymentReceiptCredential`: ACK-Pay credentials that provide proof of payment satisfying a given Payment Request. + +This issuer supports credential revocation using [Bitstring Status List](https://www.w3.org/TR/vc-bitstring-status-list/), a privacy-preserving, space-efficient mechanism for maintaining a credential revocation list. + +## Getting Started + +```sh +pnpm run setup +``` + +### Running the server + +Start the API server locally: + +```sh +pnpm run dev +``` + +The server will be available at . + +## Database + +To simplify the development experience, this API uses a SQLite database. In a production environment, we recommend using a database with native bitwise operations such as PostgreSQL. + +## API Endpoints + +### Authentication + +Mutation endpoints require a signed payload (JWT) to prove ownership of the DIDs involved. Read-only GET endpoints do not require a signed payload. + +### Response format + +Credential endpoints use this JSON structure: + +```json +{ + "ok": true, + "data": "" +} +``` + +or: + +```json +{ + "ok": false, + "error": "string error message" +} +``` + +The `GET /status/:listId` and `GET /.well-known/did.json` endpoints return bare responses rather than the `{ ok, data }` envelope. + +## Controller Credential Endpoints + +### POST /credentials/controller + +Create a new `ControllerCredential` that proves one DID controls another. + +#### Request payload, signed by the controller + +```typescript +SignedPayload<{ + controller: "did:..." + subject: "did:..." +}> +``` + +#### Response body + +```json +{ + "ok": true, + "data": { + "credential": { + ... + }, + "jwt": "credential-jwt" + } +} +``` + +#### Sample cURL + +```sh +# NOTE: Replace this non-runnable placeholder with a valid signed JWT. +curl --request POST \ + --url http://localhost:3456/credentials/controller \ + --header 'Content-Type: application/json' \ + --data '{ + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" +}' +``` + +### GET /credentials/controller/:id + +Retrieve a credential by its identifier. + +#### Response body + +```json +{ + "ok": true, + "data": { + "credential": { + ... + }, + "jwt": "credential-jwt" + } +} +``` + +#### Sample cURL + +```sh +curl --request GET \ + --url http://localhost:3456/credentials/controller/abc123 +``` + +### DELETE /credentials/controller + +Revoke a credential by its identifier. + +#### Request payload, signed by the controller + +```typescript +SignedPayload<{ + id: "credential-id" +}> +``` + +#### Response body + +```json +{ + "ok": true, + "data": null +} +``` + +#### Sample cURL + +```sh +# NOTE: Replace this non-runnable placeholder with a valid signed JWT. +curl --request DELETE \ + --url http://localhost:3456/credentials/controller \ + --header 'Content-Type: application/json' \ + --data '{ + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" +}' +``` + +## Payment Receipt Endpoints + +### POST /credentials/receipts + +Generate a payment receipt credential that proves a payment was made. + +#### Request payload, signed by the wallet that made the payment + +```typescript +SignedPayload<{ + metadata: { + txHash: "0x123..." + } + payerDid: "did:..." + paymentRequestToken: "jwt-token" + paymentOptionId: "option-id" +}> +``` + +#### Response body + +```json +{ + "ok": true, + "data": { + "credential": { + ... + }, + "jwt": "credential-jwt" + } +} +``` + +#### Sample cURL + +```sh +# NOTE: Replace this non-runnable placeholder with a valid signed JWT. +curl --request POST \ + --url http://localhost:3456/credentials/receipts \ + --header 'Content-Type: application/json' \ + --data '{ + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" +}' +``` + +### GET /credentials/receipts/:id + +Retrieve a payment receipt credential by its identifier. + +#### Response body + +```json +{ + "ok": true, + "data": { + "credential": { + ... + }, + "jwt": "credential-jwt" + } +} +``` + +#### Sample cURL + +```sh +curl --request GET \ + --url http://localhost:3456/credentials/receipts/abc123 +``` + +### DELETE /credentials/receipts + +Revoke a payment receipt credential by flipping the bit on the credential's Status List. For demo purposes, we only allow the original payment request token issuer to revoke the receipt. + +#### Request payload, signed by the original payment request token issuer + +```typescript +SignedPayload<{ + id: "receipt-id" +}> +``` + +#### Response body + +```json +{ + "ok": true, + "data": null +} +``` + +#### Sample cURL + +```sh +# NOTE: Replace this non-runnable placeholder with a valid signed JWT. +curl --request DELETE \ + --url http://localhost:3456/credentials/receipts \ + --header 'Content-Type: application/json' \ + --data '{ + "payload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6d2ViOmV4YW1wbGUuY29tIn0.signature" +}' +``` + +## Status Endpoints + +### GET /status/:listId + +Retrieve a Bitstring Status List credential for checking revocation status. + +Unlike the other endpoints, this one returns the signed credential directly rather than in the `{ ok, data }` envelope. Verifiers dereference this URL as the credential's `statusListCredential` and expect the credential itself; a wrapped body cannot be verified, so revocation checks would fail. + +#### Response body + +```json +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "id": "http://localhost:3456/status/0", + "type": ["VerifiableCredential", "BitstringStatusListCredential"], + "issuer": { "id": "did:web:..." }, + "credentialSubject": { + "id": "http://localhost:3456/status/0#list", + "type": "BitstringStatusList", + "statusPurpose": "revocation", + "encodedList": "..." + }, + "proof": { "type": "JwtProof2020", "jwt": "jwt-string" } +} +``` + +#### Sample cURL + +```sh +curl --request GET \ + --url http://localhost:3456/status/0 +``` + +## DID Endpoints + +### GET /.well-known/did.json + +Return the DID document for the issuer. + +#### Response body + +```json +{ + "@context": [...], + "id": "did:web:...", + "verificationMethod": [...], + "authentication": [...], + "assertionMethod": [...] +} +``` + +#### Sample cURL + +```sh +curl --request GET \ + --url http://localhost:3456/.well-known/did.json +``` + +## License (MIT) + +Copyright (c) 2025 Catena Labs, Inc.