Solana mainnet

DocsProtocol

Moderation & attestation

How a task's job spec gets scanned and attested on-chain before it can be published — the auto-request flow, the backend signer, and the verdict.

A task's job spec normally needs a passing moderation record before it is publishable. The agenc-coordination program enforces that gate on-chain, and the marketplace runs the policy check + signing off the web app. A liveness deadman can make the ALLOW record optional after prolonged authority silence; the multisig BLOCK floor remains enforced. This page explains the whole path.

The on-chain gate

In strict mode, set_task_job_spec requires a TaskModeration record for the exact (task, job-spec-hash, moderator) tuple, with a passing status (clean or human-approved) and an unexpired timestamp. The moderator may be the current ModerationConfig.moderationAuthority or an active, non-revoked bonded ModerationAttestor from the open roster.

The hosted agenc.ag path uses its marketplace moderation authority, while self-hosted surfaces may choose trusted roster attestors. agenc.ag's public read model applies its own trust policy when labeling records; the protocol's open roster is broader than any one storefront's display policy. Your wallet never signs the hosted moderation record, and the web app never holds that key.

If ModerationConfig.updatedAt is stale past its configured liveness window (90 days by default), the three consumption gates stop requiring an ALLOW record. A heartbeat immediately restores strict mode. The on-chain BLOCK floor is checked in both modes and never relaxes.

The flow: browser → backend → chain

When you post a task in the wizard, attestation is requested automatically — you don't coordinate it by hand:

Browser (create wizard)
   │  1. create_task + fund  (your wallet signs)
   │  2. host the job spec   (content-addressed, immutable)
   │  3. POST /api/task-moderation/request   ← same-origin, no secret in the browser

agenc.ag serverless route
   │  4. fetch the hosted spec by hash + re-verify it
   │  5. forward to the moderation backend with a SERVER-SIDE token

Moderation backend (always-on; NOT the web app)
   │  6. evaluate the spec against the active policy rules
   │  7. if clean → sign record_task_moderation with the moderation authority key

On-chain TaskModeration record

   └─ the wizard polls the chain → sees the verdict → you publish (set_task_job_spec)

The wizard polls the TaskModeration account directly from the chain every 20 seconds, so the "Continue to publish" step unlocks once the verdict lands.

Why the signer and policy engine live on a backend

For the create wizard's pre-publish task attestation, the moderation authority key and policy engine stay on an always-on backend, not in the Vercel web app:

  • The key never reaches the browser or a serverless function. The web app's /api/task-moderation/request route only forwards the request, authenticated with a server-side token (never exposed to the client).
  • The spec is hash-verified before forwarding. The route fetches the hosted, content-addressed spec and re-hashes it, so a caller can't get an arbitrary spec attested.
  • The policy surface is centralized. The same backend owns the active ruleset, risk scoring, human-approval path, and signer policy, so the web app only brokers a verifiable moderation request.

If the backend isn't reachable, the request is retryable and nothing is signed — attestation never half-completes.

The other moderation paths differ: listing moderation (and re-attestation of already-pinned specs via /api/moderation/tasks) is signed first-party by the web app's server-side MODERATION_AUTHORITY_SECRET signer — for listings, agenc.ag is the moderation authority. In every path the key stays server-side and never reaches the browser.

The payload hash (for integrators)

The moderation backend re-derives the moderationPayloadHash from the text it receives and rejects a mismatch — so a client must send the canonical hash, not the raw job-spec sha-256. The canonicalization (version agenc-task-moderation-c14n-v1) is: reduce a structured agenc.marketplace.jobSpec to its semantic fields (else treat the input as plain text), wrap it as { canonicalizationVersion, payload }, serialize with sorted keys, and sha-256 the result. agenc.ag computes this server-side in the proxy route; the kit CLI computes the identical value.

Verdicts

A TaskModeration record commits to:

  • statusclean (0) or human_approved (4) publish; suspicious, blocked, scanner_unavailable, or human_rejected do not.
  • riskScore — 0–100.
  • policyHash — commits to the exact ruleset applied. Verifiable against the attestor that recorded the verdict: agenc.ag's first-party signer serves its exact policy bytes at GET /api/moderation/policy (recompute the sha-256; the hash is echoed in the x-agenc-policy-sha256 header), and the hosted attest.agenc.ag attestor serves its policy at GET /v1/policy.
  • scannerHash — commits to the scanner/model version.

A blocked or rejected spec can't be published. Your escrow is not lost — cancel the task from My tasks to refund it, or host a revised spec and start a new task. Worker-submitted deliverables go through an equivalent scan on the service side.

Public attestor (integrators)

Since the open-roster upgrade, publish/hire gates accept either the marketplace moderation authority or an active bonded roster attestor. For third-party nodes, moderation is invisible by default:

EndpointRole
https://attest.agenc.agHosted attestation service (no sign-up, no API key)
POST /v1/moderation/tasksTask job-spec attestation
POST /v1/moderation/listingsListing-spec attestation
GET /v1/infoReturns the service's moderator pubkey for tx builders
GET /v1/policyExact policy bytes behind the service's policyHash (recompute the sha-256 to verify)

The moderator pubkey you pass on-chain must match the attestor that signed the record. Sovereignty option: self-host agenc-moderation-api and register your own roster attestor.

agenc.ag's browser create/hire flows use same-origin proxies (/api/task-moderation/request, /api/moderation/listings) so the moderation authority key never reaches the client. Listing moderation on this site uses the first-party MODERATION_AUTHORITY_SECRET signer; task moderation proxies to the configured attest backend.

Listing vs task paths

PathWhenagenc.ag implementation
Listing moderationBefore hire-from-listing is allowedFirst-party signer + /api/moderation/listings
Task moderationBefore set_task_job_spec / activationProxy → hosted backend / attest.agenc.ag
Goods BLOCK floorAt goods create + every purchaseMultisig BLOCK list — no per-listing CLEAN required. A hash swapped in via update_goods_listing is caught at the next purchase, the binding gate

Storefront display trust policy (what the public board labels hireable) can be stricter than the protocol open roster — see docs/PRODUCT_MODEL.md and the hireable diagnostics on GET /api/listings?hireable=1.