Hi everyone,
I’d like to share a new developer update on PactAgent and get feedback from the Nervos CKB community.
PactAgent has moved from being a single end-user agreement/payment app to a reusable, app-scoped agreement and escrow infrastructure layer, exposed via a versioned /v1 API, API keys, signed webhooks, workers, and a developer console. The original product still exists as a legacy surface (/api), so nothing was thrown away – it’s just no longer the core.
Why this change
PactAgent originally behaved as its own product: wallet auth, agreement pages, grant/bounty imports, profiles, proof flows, reviewer interactions, and settlement UI were all tightly coupled to a built-in interface and legacy /api routes. External systems were never really treated as customers of the backend.
The goal of this transition was to turn that backend into neutral infrastructure that other applications can build on top of — without forcing them to inherit PactAgent’s original UI, marketplace, DAO, bounty, or reputation behavior.
1. Architecture: from app to platform
-
Every external application is now represented as an App/Project tenant.
-
API keys resolve each request to a single
appId, which scopes all data and actions. -
Agreements, milestones, escrows, proofs, reviews, disputes, transactions, events, webhooks, idempotency keys, and audit logs are all owned by that tenant boundary.
-
Neutral primitives replaced assumptions baked in for a specific marketplace/DAO/bounty product.
2. Tenant boundary (Apps/Projects)
-
Apps have identity, slug, owner, environment, status, default currency/network.
-
Sandbox and production environments are separated.
-
appIdis enforced at the database layer via compound keys/foreign keys – cross-tenant access is blocked structurally, not just in application logic, and covered by negative tests. -
A pre-migration tenant-integrity audit was added for legacy data.
-
Genuinely global access requires separate internal admin authorization, not app credentials.
3. Auth: API keys for integrators
-
Sandbox keys:
pa_test_prefix. Production keys:pa_live_. -
Raw keys are shown once, at creation. Only a hash + display-safe prefix are stored afterward.
-
Keys carry scopes (RBAC) restricting which operations they can perform, including privileged proof-review actions.
-
Supports
X-API-Keyheader and bearer-token auth. -
Internal
/v1/adminauth is fully separated from app API keys, with allowlisting and audit logging.
4. The versioned /v1 API
-
New dedicated entrypoint, modular routing, standard JSON envelopes, structured error codes, request IDs/tracing, validation middleware, cursor/limit pagination.
-
Full OpenAPI spec at
/openapi.json, interactive docs at/docs. -
/healthand/readyendpoints, plus an API lifecycle/compatibility policy for integrators.
Covers: apps & keys, agreements & milestones, escrows & transactions, proofs & reviews, disputes, lifecycle events, webhook endpoints & deliveries, audit logs, and internal admin monitoring.
5. Modular backend refactor
Domain logic moved into modules under server/src/modules (apps, keys, auth, agreements, milestones, escrows, transactions, proofs, reviews, disputes, events, webhooks, audit logs, admin, docs, health), each split into controllers/services/repositories/models/validation/events/routes/state machines. Legacy services were kept only where still needed. This is documented as a modular monolith – not prematurely split into microservices.
6. Agreements & milestones
-
App-scoped creation/listing/lookup, acceptance, funding transitions, cancellation.
-
External reference IDs + metadata for integrator correlation.
-
Explicit lifecycle states with transition validation.
-
Milestone totals are enforced against agreement totals.
-
Amounts use integer-string handling to avoid floating-point errors, with exact rounding/conversion rules and quote timestamps.
-
Funded agreement/milestone terms are bound via digests to on-chain CKB escrow data and become immutable – no inserting, deleting, or materially changing terms after funding begins.
7. Escrow & settlement
-
New escrow adapter interface decouples lifecycle orchestration from any single settlement backend.
-
Sandbox/mock adapter for testing, manual adapter for controlled ops, and a CKB adapter boundary retaining the real on-chain escrow lock.
-
Escrow state machine with guards against invalid terminal transitions (no release after refund or vice versa).
-
Release requires approved proof or an appropriate dispute resolution.
-
Multi-milestone settlement fixed so one released/refunded escrow no longer incorrectly terminates the whole agreement.
-
Release/refund reworked into reserve → external execution → finalization stages, with external calls kept outside long DB transactions, plus idempotency controls against duplicate settlement.
-
Failed adapter operations are recorded as auditable states, with explicit settlement readiness/reconciliation reporting.
8. Proofs, reviews & disputes
-
App-scoped proof submission/listing/lookup with evidence-link validation.
-
Approved / rejected / needs-changes review decisions, tied to milestone and agreement state, gated by dedicated key scopes.
-
Disputes: creation, listing, lookup, and resolution via release, refund, split, or cancellation, with their own state machine, events, and audit trail. Settlement is blocked while a dispute is unresolved.
-
A 5-day manual-review inactivity deadline was added, with
reviewStartedAt/reviewDeadlineAttracked and surfaced in the API/UI. -
A worker auto-approves after the deadline only if proof is ready and the client hasn’t messaged, requested info, or opened a dispute – and this is still bounded by the deployed CKB lock’s own signature rules, so app-level approval can’t bypass the contract.
9. Events & webhooks
-
Queryable, app-scoped event stream across agreements, milestones, escrows, proofs, reviews, disputes, and deliveries.
-
Full webhook endpoint management plus async delivery records (pending/delivered/failed, with response snippets, attempt counts, retry scheduling).
-
HMAC SHA-256 signatures via
PactAgent-Event-Id,PactAgent-Timestamp,PactAgent-Signatureheaders, signed payload =timestamp + "." + rawBody. -
Webhook secrets: encrypted, hashed, copy-once reveal, rotation support.
-
SSRF protections: localhost/loopback/private networks, metadata addresses, unsafe protocols, DNS rebinding, mixed DNS results, redirects, delivery-time revalidation, plus production HTTPS/egress requirements.
10. Idempotency & concurrency
-
App-scoped idempotency keys on agreement creation, escrow creation, proof submission, release, refund, and dispute resolution.
-
Switched from check-then-act to reservation before side effects.
-
Same key + same body → replayed response. Same key + different body → conflict.
-
DB constraints, conditional writes, and configurable transaction timeouts back this up, with concurrency tests across creation, settlement, retries, and worker processing.
11. Audit, security & abuse protection
-
App-scoped audit logs with chain verification and retention/archival.
-
Security headers, production CORS validation.
-
Rate limits by IP, tenant, API key, high-risk action, and WebSocket, with explicit fail behavior if Redis is unavailable.
-
Size limits on untrusted inputs, sensitive-field redaction in logs/telemetry, sanitized public config (no leaking secrets, treasury details, node URLs, lock metadata).
-
Provider timeout/retry/concurrency/circuit-breaker controls.
-
Production environment validation fails closed if required security config is missing.
-
Tests confirming public events don’t leak private agreement data.
12. Worker & runtime separation
-
Background automation is no longer an embedded side effect of the API server – it now runs as independent worker processes with selectable roles (settlement, webhooks, AI processing).
-
Atomic job claiming via DB locks, leases with renewal/expiry, execution deadlines, bounded concurrency, retry backoff, graceful shutdown, stale-lock recovery, dead-letter listing with audited replay, heartbeats, and metrics.
13. Developer console
-
New console at
web/src/app/console: project/environment selection, API key management (create, scope, copy-once reveal, masking, revocation), and app-scoped views for everything (agreements, milestones, escrows, transactions, proofs, reviews, disputes, events, audit logs, webhooks, deliveries). -
Sandbox workbench for creating agreements/milestones, funding mock escrow, submitting proof, reviewing, and releasing/refunding – all without touching real funds.
-
Frontend fully updated for
/v1(API helpers, scopes, event names, signing headers, routing to docs/OpenAPI/health).
14. Documentation
-
Docs rewritten around PactAgent as programmable agreement infrastructure: apps, keys, scopes, agreements, milestones, escrow, proofs, reviews, disputes, events, webhooks, idempotency, sandbox mode, security.
-
Full OpenAPI spec, integrator quickstart, changelog, and API lifecycle policy.
-
Architecture decision records (tenancy, modular boundaries, durable jobs, settlement state, DB enforcement) plus production runbooks (config, secrets, audit retention, resilience, providers, observability, DR, releases, SLOs, workers, supply chain).
15. Deployment, observability & CI
-
Root workspace automation for build/lint/test/schema/OpenAPI/audit checks.
-
Docker images, dev + production Compose stacks, Kubernetes manifests (migrations, API, workers, web, dependencies) with resource limits, security contexts, probes, and graceful termination.
-
Commit-addressed container delivery replacing mutable host deploys, migration-before-rollout sequencing, smoke tests, health-gated rollback.
-
Structured logs/traces/request IDs, metrics across API/DB/queue/worker/settlement/provider/webhook/WebSocket, OpenTelemetry collector config, version-controlled dashboards, Prometheus alerts.
-
Bounded/resumable retention for idempotency records, webhook data, events, jobs, and audit archives, plus backup/restore verification and disaster-recovery exercises.
-
CI gates across build/lint/tests/migrations/OpenAPI/contracts/audits/deployment/observability assets, secret & container scanning, Dependabot, license policy enforcement, deterministic lockfiles, and
CODEOWNERS.
16. Legacy product separation
-
Infrastructure lives under
/v1; the original product stays under/api, gated byENABLE_LEGACY_PRODUCT_APIand required to be disabled in staging/production. -
Primary UI and docs now point integrators to the console and
/v1API.
17. Verification
Extensive test coverage was added: state machines, scopes, API keys, signing, retries, security rules, amounts, resilience, redaction, tenancy, rate limits, deadlines, retention, workers; DB-level tenant isolation, migration compatibility, idempotency, concurrency, escrow settlement, multi-milestone behavior, adapter failures; webhook SSRF/redirect/DNS-rebinding/signature/retry tests; WebSocket auth/origin/disclosure/heartbeat tests; health/readiness tests; plus deployment, dependency, license, and backup/restore validators.
Result
PactAgent now provides:
-
A strict App/Project tenant boundary
-
Scoped, revocable API keys
-
A versioned, documented
/v1API -
Agreement, milestone, proof, review, dispute, escrow, and transaction primitives
-
Lifecycle state machines, events, and signed webhooks
-
Idempotent, concurrency-safe settlement
-
Sandbox adapters alongside a real CKB settlement boundary
-
A developer console and sandbox workbench
-
Separate API and worker runtimes
-
Security, audit, observability, deployment, retention, and recovery controls
-
Clean isolation of legacy end-user product behavior
In short: PactAgent can now serve as backend infrastructure for multiple agreement-driven products, not just the original PactAgent experience.



