Lean TypeScript validator service for Agent Bonds disputes.
This repository is intentionally focused on v1.1 essentials:
- deterministic rules-first evaluation
- manual review fallback for non-machine-checkable disputes
- EIP-712
ValidatorCommitmentsigning - KMS-backed signing only (AWS KMS secp256k1)
- task definition hash verification
- evidence trust-level gating
- queue-based ValidationRegistry request/response submission
- signed audit export for public dispute traceability
- Fastify API server with typed request validation (
zod) POST /v1/commitments/sign(EIP-712 signature generation)POST /v1/utils/task-definition-hashPOST /v1/utils/request-hashPOST /v1/disputes/enqueueGET /v1/disputes/:jobIdPOST /v1/disputes/:jobId/manual-decisionPOST /v1/disputes/:jobId/respondedPOST /v1/disputes/:jobId/resolvedGET /v1/disputes/:jobId/audit-exportGET /v1/metrics/resolver- Deterministic evaluator with trust-aware checks
- Postgres-backed dispute store (default and required runtime backend)
- Queue worker with retry and deadline-aware escalation
- Manual-review queue guardrails with optional pager webhook alerts
- KMS-backed ValidationRegistry writer (
VALIDATION_WRITER_ENABLED=true) - Automatic
resolveDispute(taskId)keeper step after response submission - Canonical
requestHashverification from structuredrequest_context - Optional on-chain
taskHashverification during dispute enqueue (DISPUTE_CHAIN_RPC_URL) - Authorization failures are marked
blocked(no blind retries) - Response payloads include structured
criterionOutcomesandevidenceSummarytraces - Resolver metrics surface (
resolved_count,resolve_failures,resolve_latency_ms) - Signed/public dispute audit export
- Unit/integration tests via Vitest
npm install
npm run devServer defaults to http://0.0.0.0:8080.
See config.example.env for supported variables.
Important:
- JWT auth is required for all dispute, audit, metrics, and mutation endpoints. Configure
API_AUTH_JWT_ALG,API_AUTH_JWT_ISSUER, andAPI_AUTH_JWT_AUDIENCE. - Configure one key source:
API_AUTH_JWT_PUBLIC_KEY(static PEM) orAPI_AUTH_JWT_JWKS_URL(kid-based JWKS rotation). If both are set, JWKS is used. - In JWKS mode, validator fetches remote keys and supports rotation without restart; tune refresh behavior with
API_AUTH_JWT_JWKS_COOLDOWN_MSandAPI_AUTH_JWT_JWKS_CACHE_MAX_AGE_MS. - Requests must pass
Authorization: Bearer <jwt>; tokens are verified against the selected key source and checked for endpoint scopes. - Default required scopes are:
validator.utils.hashforPOST /v1/utils/task-definition-hashandPOST /v1/utils/request-hashvalidator.commitment.signforPOST /v1/commitments/signvalidator.dispute.enqueueforPOST /v1/disputes/enqueuevalidator.dispute.readforGET /v1/disputes/:jobIdvalidator.dispute.audit_exportforGET /v1/disputes/:jobId/audit-exportvalidator.dispute.manual_decisionforPOST /v1/disputes/:jobId/manual-decisionvalidator.dispute.respondedforPOST /v1/disputes/:jobId/respondedvalidator.dispute.resolvedforPOST /v1/disputes/:jobId/resolvedvalidator.metrics.readforGET /v1/metrics/resolver- Prefix wildcards (for example
validator.dispute.*orvalidator.*) are accepted. - Global wildcard
*is rejected.
AWS_REGION+AWS_KMS_KEY_IDare required (service is KMS-only)- optional
AWS_ACCESS_KEY_ID+AWS_SECRET_ACCESS_KEY(+AWS_SESSION_TOKEN) can override credential resolution - signer/writer enforce boundary allowlists via
ALLOWED_CHAIN_IDS,ALLOWED_BOND_MANAGER_ADDRESSES,ALLOWED_VALIDATION_REGISTRY_ADDRESSES PUBLIC_RPC_URLis required only when nonce is not provided inPOST /v1/commitments/sign.NONCE_RESYNC_INTERVAL_MScontrols periodic on-chain nonce re-sync to recover cursor drift- set
DISPUTE_CHAIN_RPC_URLto verifytask_definition_hashandrequest_context.task_hashagainst on-chain task data at enqueue-time (defaults toWRITER_RPC_URL, thenPUBLIC_RPC_URLwhen unset) DATABASE_URLis required (Postgres is the only runtime store backend)- enable writer with
VALIDATION_WRITER_ENABLED=trueandWRITER_RPC_URL - writer always uses the same KMS identity (
AWS_REGION+AWS_KMS_KEY_ID) - writer is strict EIP-1559 (no legacy transaction fallback)
- writer retries nonce-conflict submission errors with fresh pending nonce reads; for strict horizontal scale, run a single active writer or add an external nonce manager
- queue guardrail thresholds can be tuned with
MANUAL_QUEUE_*variables; setPAGER_WEBHOOK_URLfor alert delivery /v1/disputes/enqueueverifiesrequest_hashagainst canonicalrequest_contextAUTO_BINARY_SCORINGdefaults tofalseto avoid full-slash outcomes on partial-pass auto scorestask_definition.requiredEvidencesupports optionalminCountandminUniqueIssuersguards to prevent duplicate evidence from satisfying quantity/diversity requirements
npm run checkThis runs:
- eslint
- typecheck
- tests
- tests use a Postgres-backed harness; set
TEST_DATABASE_URLif your local Postgres is notpostgresql://127.0.0.1:5432/postgres
POST /v1/commitments/sign
{
"chain_id": 84532,
"bond_manager": "0x0000000000000000000000000000000000000001",
"agent_id": "77",
"client": "0x0000000000000000000000000000000000000002",
"task_hash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"fee_amount": "1000000",
"deadline": "1738800000",
"nonce": "1"
}POST /v1/disputes/enqueue
{
"chain_id": 84532,
"task_id": "123",
"agent_id": "77",
"bond_manager_address": "0x0000000000000000000000000000000000000001",
"task_type": "api_integration",
"ruleset_id": "api-integration-core",
"ruleset_version": "1.0.0",
"validator_address": "0x0000000000000000000000000000000000000009",
"validation_registry_address": "0x000000000000000000000000000000000000000A",
"request_hash": "0x67802f22aa0d69c66a1206c4f49c7b602f736009f9bebc7263c636d8cfbf89e0",
"request_context": {
"client": "0x0000000000000000000000000000000000000005",
"agent_recipient": "0x0000000000000000000000000000000000000006",
"client_recipient": "0x0000000000000000000000000000000000000007",
"payment": "0",
"task_hash": "0x3333333333333333333333333333333333333333333333333333333333333333"
},
"task_definition_uri": "ipfs://task-definition-cid",
"task_definition_hash": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"task_definition": {
"taskType": "api_integration",
"rulesetId": "api-integration-core",
"rulesetVersion": "1.0.0",
"acceptanceCriteria": [
{
"id": "ac-1",
"description": "All required endpoints return HTTP 200",
"weightBps": 10000
}
],
"requiredEvidence": [
{
"type": "api_test_results",
"required": true,
"minTrustLevel": "third_party",
"minCount": 2,
"minUniqueIssuers": 2
}
]
},
"evidence_items": [
{
"type": "api_test_results",
"uri": "ipfs://evidence-cid",
"sha256": "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"trustLevel": "third_party",
"issuer": "qa-lab-alpha"
},
{
"type": "api_test_results",
"uri": "ipfs://evidence-cid-2",
"sha256": "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
"trustLevel": "third_party",
"issuer": "qa-lab-beta"
}
],
"criterion_results": [
{
"criterionId": "ac-1",
"passed": true,
"evidenceTypes": ["api_test_results"]
}
]
}- integrate durable object storage for large request/response evidence payloads
- add worker metrics and Prometheus-compatible telemetry export
- add multi-reviewer assignment policy and SLA dashboards