Imported from Galactica-corp/aztec-zkkyc (
services/guardian-backend/AGENTS.md). Install upstream withnpx skills add Galactica-corp/aztec-zkkyc --skill guardian-backend. Copyright stays with the author.
Guardian Backend Guide
Use this file as the compact source of truth for this package. Read README.md and this file before making substantial changes. Also inherit the root repository guidance from /AGENTS.md.
Spec
Guardian Backend is a NodeJS service that runs in the backend to integrate a frontend where users pass KYC with the Sumsub KYC API and the guardian-aztec-connect SDK. It is a central component in providing a KYC service to users and issue ZK KYC on the Aztec blockchain.
Core features:
- Backend for guardian frontend application
- Integrate Sumsub API for KYC audits and storage
- Call
guardian-aztec-connect(package in this repo) to issue and revoke ZK KYC certificates on-chain
Workspace context:
- package path:
services/guardian-backend - package name:
@galactica-net/guardian-backend - included from the root workspace
package.json
Target shape:
README.mdfor package overviewAGENTS.mdfor compact spec, workflow, and config rules.env.examplefor local testing placeholderssrc/andtest/added incrementally as features land
Design rules:
- keep domain logic separate from Aztec transport and wallet concerns
- expose backend service through REST API and CLI for testing (both share core services instead of duplicated logic)
- prefer explicit typed inputs and outputs at package boundaries
- keep placeholders explicit with
TODO: - use docstrings to document the purpose of exposed functions and how to use them.
- use comments to explain important reasons why the code has been written like this.
- do not comment what the code is obviously doing. Instead use descriptive variable and function names.
Workflow Rules
- implement in small, testable vertical slices
- if behavior is not specified yet, extend the spec here first and then implement it
- keep docs in sync with behavior in the same change
- test everything as you go
- Prefer reusable and modular code. Do not copy paste large code sections.
- After implementing a feature, take appropriate steps to refactor the codebase to keep it well maintained.
Required TDD cycle:
- write the test first
- run it and confirm it fails for the expected reason
- implement the smallest change that should make it pass
- run the relevant tests again and confirm they pass
- update docs, examples, or config notes if needed
- commit only after the change is green
Definition of done:
- behavior is specified
- tests were added first and seen failing
- implementation passes the new and nearby relevant tests
- manual verification steps are documented if needed
- docs remain consistent with the code
Configuration Rules
- load secrets and runtime settings from environment variables
- use local
.envfiles only for testing and manual development - do not commit real
.envfiles or secrets - validate required config early
- do not hardcode private keys, mnemonics, or contract addresses
- do not print sensitive values in logs
Reference Implementation
- This package is a NodeJS port of the reference implementation written in Go, that can be found in
@services/guardian-backend/go-reference-implementation - Our port should keep the REST API of the reference implementation, to be compatible with the same frontend. The reference frontend code it will be working with can be found in
@apps/guardian-frontend-reference. It is a placeholder that will be reworked and rebranded after this package is finished. - There are some features of the go reference implementation, that we do not need. These parts should be dropped and cut out of the migration. These includes the AWS S3 storage. We do not need to store data about the generated ZK certificates. We also do not need ethereum blockchain libraries.
- Our port will use the
guardian-aztec-connectpackage and its JS SDK function to work with the blockchain instead ofgithub.com/galactica-corp/guardians-sdk. It will take care of all the blockchain interaction. The KYC certificate content and API differ a bit. Resolve those differences. - Keep the port simple and effective. You can drop irrelevant or redundant features.
Migration Spec (from Go Reference)
Preserved REST API
The backend must expose exactly three routes compatible with @apps/guardian-frontend-reference:
-
POST /api/v1/access-token
- Request body:
{ "userAddress": string }(Aztec address of the holder). - Response: JSON-encoded string (the Sumsub SDK access token). The frontend expects
response.json()to yield a plain string. - Behavior: generate Sumsub access token for the given Aztec user address; cache optional.
- Request body:
-
POST /api/v1/sumsub-webhook
- Headers:
X-Payload-Digest(hex),X-Payload-Digest-Alg(e.g. HMAC_SHA256_HEX, HMAC_SHA512_HEX). - Body: raw request body for digest verification.
- Behavior: verify HMAC; on
applicantReviewedwithreviewAnswer === "GREEN", run the issuance workflow (fetch applicant, normalize KYC, issue via guardian-aztec-connect, persist result).
- Headers:
Dropped Go-Only Features
Do not port:
- AWS S3 storage for certificate artifacts
- Email delivery of certificate download links
- Ethereum / go-ethereum / guardians-sdk and EVM registry
- Merkle proof service
- Redis Streams as a queue (optional later; first implementation is synchronous webhook-to-issuance in-process)
Internal Processing Record
The backend keeps a correlation record per KYC session to bridge frontend, Sumsub, and Aztec. Recommended fields (first migration after API change):
userAddress(string): Aztec address for issuance; provided by the frontend (see below) and used as Sumsub external user id.applicantId(string): Sumsub applicant idsumsubExternalUserId(string): equalsuserAddressin Sumsubstatus: e.g.accessTokenIssued|applicantLoaded|approved|issuing|issued|failednormalizedKycPayload: provider-agnostic KYC shape for guardian-aztec-connectissuanceResult:{ uniqueId, revocationId, txHash }when status isissuedlastError(string, optional): last failure reasoncreatedAt,updatedAt(timestamps)
User Address Source
The Aztec SDK requires userAddress for issuance. The frontend sends the user's Aztec address to the backend as the only field in the access-token request body (POST /api/v1/access-token with { "userAddress": "..." }). The processing record stores userAddress and uses it as Sumsub external user id so the webhook handler can issue without further frontend round-trips.
Issuance Flow (First Implementation)
- Synchronous: When Sumsub sends
applicantReviewedwith GREEN, the webhook handler runs the full flow in-process: load/create processing record, fetch applicant from Sumsub, normalize to ZkKycInput, call guardian-aztec-connectissueKycCertificate(), persist result. No queue worker in the first milestone. - Idempotency: Duplicate webhooks for the same applicant must not double-issue; use processing record status and stored issuance result to skip or return existing result.
Legacy Certificate Page
The frontend route /certificate/:certificateId and S3-based certificate download are out of scope for the first backend migration milestone. Document this in README; do not reintroduce S3 for compatibility.
Preferred Sources
Prefer sources in this order:
- package code and docs in
services/guardian-backend - Reference implementation in
@services/guardian-backend/go-reference-implementation - Root guidance in
/AGENTS.md - Reference frontend counterpart in
@apps/guardian-frontend-reference - Sumsub API docs https://docs.sumsub.com/reference/about-sumsub-api