Fastify + Zod microservice for DPG scoring.
- health endpoints
- readiness endpoint
- AI-backed
match-scoremodule - HMAC API key auth for protected routes
GET /GET /healthGET /readyPOST /api/v1/scores/matchGET /docsGET /docs/json
Create local secret files from the example files first.
cp config/auth.keys.example.json config/auth.keys.json
cp config/ai.providers.example.json config/ai.providers.json
docker compose up -d redis
pnpm install
pnpm devRedis host port comes from REDIS_PORT in your environment or .env file.
Format the codebase with:
pnpm format
pnpm format:checkCreate local secret files from the example files first.
cp config/auth.keys.example.json config/auth.keys.json
cp config/ai.providers.example.json config/ai.providers.jsonThen start the full stack:
docker compose up --build -dThis starts:
dpg-scoring-apponhttp://localhost:3000dpg-scoring-redisonlocalhost:${REDIS_PORT:-6379}
The app container:
- loads non-secret settings from
.env - mounts
config/auth.keys.jsonandconfig/ai.providers.jsonread-only at runtime - connects to Redis using the Compose service name via
REDIS_URL=redis://dpg-scoring-redis:6379 - waits for Redis to become healthy before starting
Healthchecks:
dpg-scoring-redisusesredis-cli pingdpg-scoring-appchecksGET /readyinside the container
Useful commands:
docker compose logs -f dpg-scoring-app
docker compose ps
docker compose downOpen the API docs at:
http://localhost:3000/docs
If you update .env, rebuild and restart the app:
docker compose up --build -d dpg-scoring-appOpenAPI docs are available at:
http://localhost:3000/docshttp://localhost:3000/docs/json
Swagger UI is the primary docs surface for this service. The generated OpenAPI spec should remain the source of truth so the UI can be swapped later if needed.
Store local API keys in config/auth.keys.json.
Example shape:
{
"keys": [
{
"keyId": "match-score-client",
"secret": "replace-with-a-strong-secret-1"
},
{
"keyId": "admin-dashboard",
"secret": "replace-with-a-strong-secret-3"
}
]
}This file is git-ignored.
Store local provider keys in config/ai.providers.json.
Example shape:
{
"providers": {
"gemini": {
"apiKey": "replace-with-your-gemini-api-key"
},
"openai": {
"apiKey": "replace-with-your-openai-api-key"
}
}
}This file is also git-ignored.
Redis is used for:
- shared nonce replay protection for HMAC auth
- match-score response caching
Default local config:
REDIS_ENABLED=true
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_URL=redis://localhost:6379
MATCH_SCORE_CACHE_ENABLED=true
MATCH_SCORE_CACHE_TTL_SECONDS=600Start Redis locally:
docker compose up -d redisStop Redis:
docker compose downIf you want to run the service without Redis temporarily:
REDIS_ENABLED=false
MATCH_SCORE_CACHE_ENABLED=falseIn that mode:
- auth nonce protection falls back to in-memory storage
- match-score caching is disabled
Protected routes require these headers:
x-dpg-keyx-dpg-timestampx-dpg-noncex-dpg-signature
Signature base string:
METHOD
PATH
TIMESTAMP
NONCE
Signature format:
sha256=<hex-hmac>
Swagger documents these headers as required security headers for protected /api/v1 endpoints.
curl -X POST http://localhost:3000/api/v1/scores/match \
-H "x-dpg-key: match-score-client" \
-H "x-dpg-timestamp: 1710000000" \
-H "x-dpg-nonce: abc123" \
-H "x-dpg-signature: sha256=<signature>" \
-H "content-type: application/json" \
-d '{
"version": "v1",
"promptVersion": "match-compatibility-v1",
"itemA": {
"item_network": "yellow_dot",
"item_domain": "student",
"item_type": "profile_1.0",
"item_id": "5413356c-2ced-4892-8050-bb19dfdf9928",
"item_instance_url": "https://ubi-backend.onest.dhiway.net",
"item_schema_url": "https://ubi-backend.onest.dhiway.net/api/v1/network/schema/yellow_dot/student/profile_1.0",
"item_state": {
"city": "Secunderabad",
"name": "Student Example",
"grade": "10",
"preferred_subject": "maths, Physics"
},
"item_latitude": null,
"item_longitude": null
},
"itemB": {
"item_network": "yellow_dot",
"item_domain": "tutor",
"item_type": "profile_1.0",
"item_id": "f7f124fe-6746-45bb-8323-5b7ec95f5d07",
"item_instance_url": "https://ubi-backend.onest.dhiway.net",
"item_schema_url": "https://ubi-backend.onest.dhiway.net/api/v1/network/schema/yellow_dot/tutor/profile_1.0",
"item_state": {
"name": "Tutor Example",
"subjects": ["English"],
"teaching_mode": "online",
"experience_years": 2
},
"item_latitude": null,
"item_longitude": null
}
}'- API version defaults to
v1 - prompt version defaults to
match-compatibility-v1 promptVersionis optional on the request- API
v1can later run with newer prompt versions without changing the route contract - provider selection is configurable and currently defaults to Gemini
- Redis-backed caching is configurable and enabled by default for local development
Use the included helper to sign and send a sample request:
pnpm call:match-scoreOptional flags:
pnpm call:match-score -- --url http://localhost:3000/api/v1/scores/match
pnpm call:match-score -- --key-id match-score-client
pnpm call:match-score -- --payload ./scripts/match-score.sample.jsonIf --payload is not provided, the script sends a built-in sample student/tutor payload.
Generate auth headers only:
pnpm auth:headers
pnpm auth:headers -- --format curl
pnpm auth:headers -- --method GET --url http://localhost:3000/api/v1/scores/match