This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a monorepo for the UNTP (UN Transparency Protocol) test suite and reference implementation. It includes:
- Reference Implementation: Web UI for issuing/verifying UNTP credentials (
packages/reference-implementation/) - Services Package: Core business logic, credential processing, EPCIS events, DID management, and external service adapters
- Components Package: Shared React component library
- UNTP Utils: Shared utility primitives (multibase digests, conformity vocabulary parsing, artefact helpers) consumed by the other packages
- UNTP Playground: Credential validation tool
- Test Suites: Technical and semantic interoperability testing
- Monorepo: pnpm Workspaces
- Language: TypeScript (ESNext)
- Frontend: Next.js 15 (App Router), React 19, Material-UI, Tailwind CSS
- Backend: Next.js API routes, Prisma ORM, PostgreSQL
- Auth: NextAuth.js 5 with Keycloak (OAuth2/OIDC)
- Testing: Jest (unit), Cypress (E2E), Storybook (components)
- Infrastructure: Docker Compose with dependent services
- Node: >= 22.22.2
- pnpm: 9.15.4 (via Corepack)
- Docker: Latest with Compose
# Install Node via NVM; pnpm via Corepack
nvm install 22.22.2 && nvm use 22.22.2
corepack enable
# Environment file
cp .env.example .env # All defaults work for local dev
# Start dependent services (VCKit, Storage, IDR, Keycloak, Postgres)
docker compose up -d
# Install, build, and start
pnpm install
pnpm build
pnpm startAccess the reference implementation at http://localhost:3003 (admin@example.com / changeme)
pnpm start # Start RI dev server (hot reload)
pnpm start:untp-playground # Start playground on port 4001
pnpm build:services # Rebuild services after changes
pnpm build:components # Rebuild components after changes
pnpm build # Full build (utils + services + components + reference implementation + playground)pnpm test # All tests across all packages
pnpm test:coverage # Merged coverage report
pnpm test:services # Services package only
pnpm test:reference-implementation # Reference implementation only
pnpm test:components # Components only
# Postgres-backed integration tests (reference implementation only; needs a running Docker daemon)
# By default the rig starts and tears down its own ephemeral postgres:17-alpine container.
# To point it at an existing database instead, set TEST_DATABASE_URL; the rig refuses a URL
# naming a real environment's database (ri, vckit) unless TEST_DATABASE_ACCEPT_DESTRUCTIVE=true
# is also set, because the suites truncate tables between tests.
cd packages/reference-implementation && pnpm test:integration
# E2E Testing (per-app suites; see packages/<app>/e2e/README.md for details)
# RI open mode
docker compose -f docker-compose.e2e.yml --profile ri up -d --build
pnpm test:e2e:ri:open # or pnpm test:e2e:ri (default)
pnpm test:e2e:ri:open-ui # Interactive UI
docker compose -f docker-compose.e2e.yml --profile ri down -v
# RI closed mode — the closed-mode override MUST be passed to every compose verb
docker compose -f docker-compose.e2e.yml -f docker-compose.e2e-closed.yml --profile ri up -d --build
pnpm test:e2e:ri:closed
docker compose -f docker-compose.e2e.yml -f docker-compose.e2e-closed.yml --profile ri down -v
# Playground
docker compose -f docker-compose.e2e.yml --profile playground up -d --build
pnpm test:e2e:playground
pnpm test:e2e:playground:open-ui
docker compose -f docker-compose.e2e.yml --profile playground down -vcd packages/reference-implementation
pnpm prisma studio # Visual DB editor (localhost:5555)
pnpm prisma migrate dev # Create/apply migrationspnpm lint:check # ESLint across packagespnpm storybook:components # Component library docs
pnpm storybook:reference-implementation # RI component docs
# build-clean removes node_modules and build artifacts from every workspace package
# (including each package's e2e/ subdirectory), but leaves documentation/node_modules
# alone. That install runs under a Docker bind mount, and removing it while the stack
# is running fails with EACCES.
pnpm build-cleantests-untp/
├── packages/
│ ├── services/ # Core logic (TypeScript library)
│ ├── components/ # React component library
│ ├── untp-utils/ # Shared utility primitives library
│ ├── reference-implementation/ # Next.js reference implementation (and e2e/)
│ ├── untp-playground/ # Validation tool (and e2e/)
│ └── untp-test-suite/ # CLI test suite
└── documentation/ # Docusaurus site
The services package uses a type-safe adapter registry for pluggable implementations:
ServiceTypeenum: IDR, STORAGE, VCAdapterTypeenum: VCKIT (extensible)AdapterRegistry: Type-safe mapping of services to adaptersAdapterRegistryEntry: Schema validation + factory function pattern
Example: registry[ServiceType.VC][AdapterType.VCKIT] returns factory for VCKit VC adapter.
DID adapters are resolved via the separate didAdapterRegistry, exported from server.ts.
External integrations use interfaces + implementations:
VerifiableCredentialService→VCKitAdapterStorageService→UncefactStorageAdapterEncryptionService→AesGcmEncryptionAdapterKeyProvider→LocalKeyGenerator
- UNTP credential processing (DPP, DIA, DFR, DCC)
- EPCIS events (object, aggregation, transformation, transaction, association, traceability)
- DID management (create, verify
did:web,did:web+vh) - Encryption/decryption (AES-GCM)
- Identity scheme handling (GS1)
- Database: Prisma ORM. See
packages/reference-implementation/prisma/schema.prismafor the full model list (users, tenants, DIDs, credentials, service instances, identifiers and schemes, organisations, facilities, products, data models, render templates, and conformity records) - API Routes:
/src/app/api/v1/- credentials, cvc, data-models, dids, facilities, identifiers, organisations, products, registrars, render-templates, schemes, services. Auth is unversioned, at/api/auth/[...nextauth] - Auth: Keycloak via NextAuth.js with organization-level branding
- Config: Tenant configuration via database (replacing legacy app-config.json)
- Edit code in
packages/services/src/ - Run
pnpm build:services - Changes auto-imported into the reference implementation (hot reload)
- Edit code in
packages/components/src/ - Run
pnpm build:components - Consumed by the reference implementation (hot reload)
- Edit
packages/reference-implementation/prisma/schema.prisma - Run
cd packages/reference-implementation && pnpm prisma migrate dev - Restart the reference implementation
- Tenant configuration is managed via the database
- Write tests for all new features
- Run
pnpm testbefore committing - Ensure 100% coverage for services package
- E2E tests must pass before merging
Before opening a PR, audit whether any change in the PR touches behaviour, conventions, or topology already recorded in an ADR under docs/adrs/. The creating-adrs skill gate enforces evaluation (was an architectural decision made?); this rule covers synchronisation (does the existing ADR text still match reality?).
Three outcomes, three responses:
- Implementation changed, decision unchanged (e.g. command examples in an ADR body now use
pnpminstead ofyarnafter a package-manager migration, or workflow filenames moved): add a datedUpdate (YYYY-MM-DD)line to the ADR header (underStatus:) pointing at the new state and any relevant PRs / superseding ADRs. Do not rewrite the body. The body is historical record — what was true at the time of the decision. Editing it silently rewrites history. - Decision changed: open or update an ADR via the
creating-adrsskill. If the change supersedes a prior decision, mark the prior ADR's statussupersededwith a forward link, and reference the prior ADR from the new one'sReferencessection. - Decision retired entirely: mark
supersededordeprecatedin the status with a one-line explanation.
ADRs that go stale silently are worse than no ADRs at all — they lock in misleading history and re-litigate settled decisions. When in doubt about which outcome applies, ask before editing the body.
- VCKit API - Verifiable credential operations
- Storage Service - UNTP credential storage
- Identity Resolver Service (IDR) - Identifier resolution
- PostgreSQL (vckit-db, ri-db) - Databases
- Keycloak - OAuth2/OIDC provider
- MinIO - Object storage for IDR service
All packages use @/* → ./src/* in TypeScript
- Services:
packages/services/build/(CommonJS + ESM with.d.ts) - Components:
packages/components/build/
Each package has its own tsconfig.json (no centralized config)
- Base config:
jest.config.base.js - Package-specific:
packages/*/jest.config.{mjs,ts}
- Root
.envfile (use.env.exampleas template) - No changes needed for local development defaults
- Each publishable package releases independently via its own tag-triggered workflow (see
docs/adrs/031-per-package-tag-triggered-npm-release.md) - See
RELEASE_MANAGEMENT_GUIDE.mdfor release process - Use conventional commits (see
CONTRIBUTING.md)
- Mix E2E and standard Docker Compose setups
- Run services without dependent Docker services
- Skip test runs before committing