System Design
Design principles
- Open standards only — W3C Verifiable Credentials, DID, Ed25519 (RFC 8037); no proprietary formats.
- No vendor lock-in — Kubernetes-native; runs anywhere.
- Independent deployability — each service owns its schema, API, and release cycle.
- Single responsibility — orchestration lives in Core Engine, not the Gateway or services.
- Holder agency — holders own their credentials and control access through transparent logging.
Layers
| Layer | Responsibility |
|---|---|
| Micro Frontends | Independently deployed React apps, one per audience |
| API Gateway | Single ingress: TLS termination, JWT auth, rate limiting, routing |
| Core Engine | Workflow orchestration, anti-corruption layer, audit logging |
| Microservices | Bounded business domains (gRPC, internal only) |
| Data Tier | PostgreSQL 18 (multi-schema) + Redis 8.6 (cache, rate limiting) |
Frontends
| MFE | Directory | Dev port | Persona |
|---|---|---|---|
| Landing | frontends/landing | 5177 | Public marketing site — not behind the gateway |
| Attest Authority (Issuer) | frontends/issuer-portal | 5173 | Issuance, bulk signing, org branding |
| Attest Credentials (Holder) | frontends/credential-wallet | 5176 | Wallet, access history, share controls |
| Attest Verify (Verifier) | frontends/verifier-portal | 5174 | Instant validation |
| Attest Custodian | frontends/custodian-portal | 5178 | Bulk document intake, review, attestation |
| Admin Console | frontends/admin-console | 5175 | Registry oversight, catalog admin, system health |
Services
| Service | REST | gRPC | Schema | Responsibility |
|---|---|---|---|---|
| API Gateway | 8000 | — | — | TLS, JWT, rate limiting, routing |
| Core Engine | 8080 | — (client only) | core | Orchestration, audit logging, error aggregation |
| Credential Issuance | 8081 | 50051 | wallet | Issue W3C VCs, batch issuance, revocation |
| Verification Engine | 8082 | 50052 | — (Redis only, stateless) | Ed25519 signature validation, status checks |
| Issuer Registry | 8083 | 50053 | authority | DID registry, public keys, key rotation |
| Admin Console (backend) | 8084 | — | console | Platform admin REST API, own module |
| Program Catalog | 8085 (actuator only) | 50054 | catalog | Jurisdictions, frameworks, catalog entries |
| Learner Records | 8086 (actuator only) | 50055 | learner | Learner identity, enrolments, outcomes |
| Custodian Registry | 8087 | 50056 | custodian | DID registry + key custody for Custodians |
Program Catalog and Learner Records have no business REST API of their own — all access goes through Core Engine’s REST controllers, which proxy via gRPC.
Core credential lifecycle
Data model
Each service owns its own PostgreSQL schema with a dedicated least-privilege database user — see Database Schema for the authoritative table-by-table list.
| Schema | Owner |
|---|---|
authority | Issuer Registry |
wallet | Credential Issuance |
core | Core Engine |
console | Admin Console |
catalog | Program Catalog |
learner | Learner Records |
custodian | Custodian Registry |
Core API endpoints
Full endpoint-by-endpoint detail lives in API & gRPC Reference. Shape:
| Operation | Method + path | Auth |
|---|---|---|
| Register issuer | POST /api/v1/issuer/register | Public — self-service, lands PENDING_APPROVAL |
| Approve / reject issuer | POST /api/v1/issuers/{did}/approve | /reject | Admin |
| Issue credential | POST /api/v1/credentials/issue | Issuer auth, or BYOK signature |
| Batch issue | POST /api/v1/credentials/batch | Issuer auth or per-record BYOK signature |
| Get credential | GET /api/v1/credentials/{id} | Public |
| Revoke credential | PUT /api/v1/portal/me/credentials/{id}/revoke | Issuer portal session |
| Verify credential | POST /api/v1/credentials/verify | Public |
| Verify by share link | GET /api/v1/verify/{share_token} | Public |
Security & key management
Key custody
| Target design | Current implementation | |
|---|---|---|
| Custodial signing keys | HSM/KMS (AWS KMS or CloudHSM); signing via KMS API, key never leaves the HSM | AES-256-GCM-encrypted at the application layer (Issuer Registry’s SigningKeyEncryptionService) — a deliberate, documented pilot-only stopgap |
| BYOK keys | Never held by Attest ID | Same today — Attest ID never holds a BYOK private key |
| Key rotation | Version-tracked in DID Registry | Built, regardless of custody model |
Migrate custodial signing to a real HSM/KMS before onboarding an external issuer whose key custody isn’t otherwise trusted to this application layer. See BYOK Signing Spec for the bring-your-own-key contract, and Issuer API Keys for headless custodial-issuer auth.
Internal transport
- Every internal gRPC connection (server and client) requires mutual TLS — no plaintext fallback. See Internal gRPC mTLS.
- External traffic: HTTPS / TLS 1.3 end to end.
Registration approval & the holding bay
Self-service registration never activates anyone. An issuer or custodian lands
PENDING_APPROVAL and can sign in — proving control of its mailbox or IdP — but only reaches a
holding bay page until an admin approves it.
| Status | Sign-in | What the session can reach |
|---|---|---|
ACTIVE | Yes | Everything the role allows |
PENDING_APPROVAL, REJECTED | Yes | Holding-bay page only |
SUSPENDED, REVOKED | No — indistinguishable from “no such account” | Nothing |
Status is always read live from the registry, never cached on the session — enforced at the
gateway for issuers (JWT token_scope) and inside Core Engine for custodians (no gateway JWT
filter on custodian routes).
Infrastructure
- Containerization — Docker.
- Orchestration — Kubernetes.
- CI/CD — GitLab CI (parent/child pipeline, per-service selective builds) → deploy.
- Monitoring — Prometheus + Grafana (target; see CLAUDE.md Roadmap Context for current status).