This directory contains a reference implementation of a Universal Commerce Protocol (UCP) server built with Node.js, Hono and Zod. It demonstrates how to implement the UCP specifications for shopping, checkout, and order management.
- Node.js 20, 22, or 24
- npm (Node Package Manager)
-
Clone this repo
git clone https://github.com/Universal-Commerce-Protocol/samples.git cd samples/rest/nodejs -
Install Dependencies
Run the following command in this directory to install the required Node.js packages:
npm install
-
Database Setup
The server uses SQLite for persistence. Ensure the
databasesdirectory exists. The server will automatically initialize the database files (products.dbandtransactions.db) and tables on the first run.If the
databasesdirectory does not exist, create it:mkdir -p databases
Note: For the server to function fully (e.g., to create a checkout), you may need to populate
products.dbwith sample product data, as the server expects products to exist for validation.
To start the server in development mode (with hot reloading):
npm run devTo build and start the server for production:
npm run build
npm startThe server will start on port 3000 by default. You can access the discovery endpoint at:
http://localhost:3000/.well-known/ucp
The server verifies UCP request signatures as defined in the specification's
signatures.md:
RFC 9421 HTTP Message Signatures
with an RFC 9530 Content-Digest
over the raw body. The signer's public key is discovered from the profile URL in
the UCP-Agent header (its keys[]). ES256 (fixed-width raw r||s, not
ASN.1/DER) is the baseline; Ed25519 is also supported. The behaviour mirrors
the Python reference server (rest/python/server).
Behaviour is controlled by two environment variables:
| Variable | Default | Effect |
|---|---|---|
REQUIRE_SIGNATURES |
false |
Reject requests whose signature is missing or invalid. When false, a present signature is still verified and the result logged, but unsigned or invalid requests are allowed — so existing clients keep working. |
ALLOW_INSECURE_PROFILE_URLS |
false |
Permit http and loopback/private profile URLs when resolving keys. For localhost demos and CI only; it disables SSRF protections and must never be enabled in production. |
When verification fails under enforcement, the server returns the spec's error
code: 401 signature_missing / signature_invalid / key_not_found,
400 digest_mismatch / algorithm_unsupported / invalid_profile_url,
424 profile_unreachable, or 422 profile_malformed.
To reject anything unsigned, start the server with enforcement on:
REQUIRE_SIGNATURES=true npm run devEach verified request logs
RFC 9421 signature verified (keyid=..., profile=...). The discovery profile
at /.well-known/ucp stays unverified: it is the public document a platform
must read before it can sign anything.
Outbound order-event webhooks are signed as the business, per the
specification's order.md (Webhook Signature Verification): every delivery
carries UCP-Agent (this server's profile URL), Signature,
Signature-Input, and a Content-Digest over the exact raw body bytes. The
signed components cover the full request-signing table (@method,
@authority, @path, @query when the platform URL has one,
content-digest, content-type, idempotency-key, ucp-agent) plus the
Standard Webhooks event headers (webhook-id, webhook-timestamp) and
x-event-type. The matching public JWK is published in the served profile's
signing_keys[] (and mirrored into ucp.keys[]) so platforms can verify.
Retried deliveries reuse the same Webhook-Id and Idempotency-Key and are
re-signed per attempt.
| Env var | Default | Effect |
|---|---|---|
WEBHOOK_SIGNING_KEY |
(ephemeral) | Path to a PEM private key (EC P-256 or Ed25519) to sign webhooks with. When unset, an ephemeral demo key is generated at startup and published in the profile. |
To verify that this server implementation complies with the UCP specifications, use the official UCP Conformance Test Suite.
-
Get the Conformance Tests
Clone the conformance repository:
git clone https://github.com/Universal-Commerce-Protocol/conformance.git cd conformance -
Run the Tests
Follow the instructions in the conformance repository to install its dependencies. Then, run the tests against this local server implementation.
Assuming the conformance suite uses a configuration file or environment variables to target the server, ensure it is pointing to:
http://localhost:3000
src/api: Contains the implementation of UCP services (Discovery, Checkout, Order).src/data: Database access layer (SQLite).src/models: TypeScript types and Zod schemas (some generated from specs).src/utils: Helper utilities for validation and logging.databases: Directory where SQLite database files are stored.