Skip to Content
ConceptsCore ProtocolSystem Design

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

LayerResponsibility
Micro FrontendsIndependently deployed React apps, one per audience
API GatewaySingle ingress: TLS termination, JWT auth, rate limiting, routing
Core EngineWorkflow orchestration, anti-corruption layer, audit logging
MicroservicesBounded business domains (gRPC, internal only)
Data TierPostgreSQL 18 (multi-schema) + Redis 8.6 (cache, rate limiting)

Frontends

MFEDirectoryDev portPersona
Landingfrontends/landing5177Public marketing site — not behind the gateway
Attest Authority (Issuer)frontends/issuer-portal5173Issuance, bulk signing, org branding
Attest Credentials (Holder)frontends/credential-wallet5176Wallet, access history, share controls
Attest Verify (Verifier)frontends/verifier-portal5174Instant validation
Attest Custodianfrontends/custodian-portal5178Bulk document intake, review, attestation
Admin Consolefrontends/admin-console5175Registry oversight, catalog admin, system health

Services

ServiceRESTgRPCSchemaResponsibility
API Gateway8000——TLS, JWT, rate limiting, routing
Core Engine8080— (client only)coreOrchestration, audit logging, error aggregation
Credential Issuance808150051walletIssue W3C VCs, batch issuance, revocation
Verification Engine808250052— (Redis only, stateless)Ed25519 signature validation, status checks
Issuer Registry808350053authorityDID registry, public keys, key rotation
Admin Console (backend)8084—consolePlatform admin REST API, own module
Program Catalog8085 (actuator only)50054catalogJurisdictions, frameworks, catalog entries
Learner Records8086 (actuator only)50055learnerLearner identity, enrolments, outcomes
Custodian Registry808750056custodianDID 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.

SchemaOwner
authorityIssuer Registry
walletCredential Issuance
coreCore Engine
consoleAdmin Console
catalogProgram Catalog
learnerLearner Records
custodianCustodian Registry

Core API endpoints

Full endpoint-by-endpoint detail lives in API & gRPC Reference. Shape:

OperationMethod + pathAuth
Register issuerPOST /api/v1/issuer/registerPublic — self-service, lands PENDING_APPROVAL
Approve / reject issuerPOST /api/v1/issuers/{did}/approve | /rejectAdmin
Issue credentialPOST /api/v1/credentials/issueIssuer auth, or BYOK signature
Batch issuePOST /api/v1/credentials/batchIssuer auth or per-record BYOK signature
Get credentialGET /api/v1/credentials/{id}Public
Revoke credentialPUT /api/v1/portal/me/credentials/{id}/revokeIssuer portal session
Verify credentialPOST /api/v1/credentials/verifyPublic
Verify by share linkGET /api/v1/verify/{share_token}Public

Security & key management

Key custody

Target designCurrent implementation
Custodial signing keysHSM/KMS (AWS KMS or CloudHSM); signing via KMS API, key never leaves the HSMAES-256-GCM-encrypted at the application layer (Issuer Registry’s SigningKeyEncryptionService) — a deliberate, documented pilot-only stopgap
BYOK keysNever held by Attest IDSame today — Attest ID never holds a BYOK private key
Key rotationVersion-tracked in DID RegistryBuilt, 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.

StatusSign-inWhat the session can reach
ACTIVEYesEverything the role allows
PENDING_APPROVAL, REJECTEDYesHolding-bay page only
SUSPENDED, REVOKEDNo — 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).