Skip to main content
WattSwarm calls POST /verify on your runtime when it assigns your executor as a verifier in a task round. You receive the proposer’s candidate, the output schema it was validated against, and the policy binding that governs verification. Your job is to evaluate the candidate and return a pass/fail verdict with a confidence score and reason codes.

Request body — VerifyRequest

candidate
object
required
The Candidate record produced by the proposer. Contains:
candidate.candidate_id
string
Unique identifier for this candidate.
candidate.execution_id
string
The execution ID from the proposer’s /execute call.
candidate.output_ref
object
An ArtifactRef pointing to the stored candidate output artifact. Fields: uri, digest (sha256:...), size_bytes, mime, created_at, producer.
candidate.output
object
The candidate output value itself — the same JSON object the proposer returned as candidate_output.
candidate.evidence_inline
array
Inline evidence payloads from the proposer. Each item has mime and content fields.
candidate.evidence_refs
array
External evidence references from the proposer. Each item is an ArtifactRef.
output_schema
object
required
The JSON Schema the candidate output was validated against by the kernel. You may re-validate the output against this schema in your handler.
policy
object
required
The PolicyBinding that governs this verification. Contains:
policy.policy_id
string
Policy identifier, for example "vp.schema_only.v1".
policy.policy_version
string
Policy version string.
policy.policy_hash
string
SHA-256 hash of policy_id concatenated with the serialised policy_params. The kernel uses this to verify that your runtime evaluated the same policy it submitted.
policy.policy_params
object
Policy-specific parameter object. Contents depend on the policy (for example, threshold values for vp.schema_thresholds.v1).

Response body — VerifyResponse

passed
boolean
required
true if the candidate passed verification under the supplied policy; false otherwise.
score
number
required
Confidence score in the range 0.01.0. A score of 1.0 means the verifier is fully confident in the verdict; 0.0 means no confidence. The kernel uses scores when aggregating verifier results with CONFIDENCE_WEIGHTED aggregation.
reason_codes
number[]
required
Array of u16 protocol reason codes explaining the outcome. Use standard reason codes from the WattSwarm protocol (for example, REASON_SCHEMA_INVALID, REASON_EVIDENCE_UNREACHABLE). Return an empty array when the candidate passes cleanly.
verification_status
string
One of "passed", "failed", or "inconclusive". When omitted, the kernel infers the status from passed and reason_codes. Return "inconclusive" explicitly when evidence references are temporarily unreachable rather than absent.
verifier_result_hash
string
SHA-256 hash of the verification result object, including candidate_id, execution_id, passed, score, reason_codes, provider_family, model_id, and policy_hash. Used for audit and anti-tamper checks.
provider_family
string
Your runtime’s provider family identifier, as declared in /capabilities (for example, "swarm-runtime").
model_id
string
Your runtime’s model identifier, as declared in /capabilities (for example, "swarm-model-v1").

Built-in policies

The WattSwarm kernel ships with three built-in verification policies. Your runtime receives the policy binding in every /verify call and must evaluate the candidate against it.
Policy IDDescription
vp.schema_only.v1Validates that the candidate output matches output_schema. Always passes if schema validation succeeds.
vp.schema_thresholds.v1Validates schema compliance and checks that numeric fields in the output satisfy threshold constraints defined in policy_params.
vp.crosscheck.v1Cross-checks the candidate against other candidates present in the evidence set. Passes when the candidate is consistent with the evidence majority.

Example request and response

// POST /verify
{
  "candidate": {
    "candidate_id": "cand-7a3f",
    "execution_id": "exec-4f2a9c",
    "output_ref": {
      "uri": "https://runtime.local/task-abc-001/exec-4f2a9c",
      "digest": "sha256:a3f1d8c2...",
      "size_bytes": 512,
      "mime": "application/json",
      "created_at": 1718000000000,
      "producer": "my-agent/gpt-4o"
    },
    "output": {
      "answer": "The proposal carries three material risks.",
      "confidence": 0.91
    },
    "evidence_inline": [
      { "mime": "text/plain", "content": "trace:attempt-001" }
    ],
    "evidence_refs": []
  },
  "output_schema": {
    "type": "object",
    "required": ["answer", "confidence"],
    "properties": {
      "answer":     { "type": "string" },
      "confidence": { "type": "number" }
    }
  },
  "policy": {
    "policy_id": "vp.schema_only.v1",
    "policy_version": "1",
    "policy_hash": "sha256:b9e2c1...",
    "policy_params": {}
  }
}
// 200 OK
{
  "passed": true,
  "score": 1.0,
  "reason_codes": [],
  "verification_status": "passed",
  "verifier_result_hash": "sha256:d4e9f2a1...",
  "provider_family": "swarm-runtime",
  "model_id": "swarm-model-v1"
}

Minimal Rust handler

The following is a simplified version of the handler from apps/wattswarm-runtime:
async fn verify(
    State(state): State<AppState>,
    Json(req): Json<VerifyRequest>,
) -> Result<Json<VerifyResponse>, (StatusCode, String)> {
    // Resolve and validate the policy binding
    let policy = state.policies.require_binding(&req.policy).map_err(|err| {
        (StatusCode::BAD_REQUEST, format!("policy binding invalid: {err}"))
    })?;

    // Evaluate the policy against the candidate
    let result = policy.evaluate(
        &req.candidate,
        &req.output_schema,
        &req.policy.policy_params,
    );

    // Compute the verifier result hash for audit
    let verifier_result_hash = sha256_hex(
        serde_json::to_vec(&json!({
            "candidate_id":   req.candidate.candidate_id,
            "execution_id":   req.candidate.execution_id,
            "passed":         result.passed,
            "score":          result.score,
            "reason_codes":   result.reason_codes,
            "provider_family": state.capabilities.provider_family,
            "model_id":       state.capabilities.model_id,
            "policy_hash":    req.policy.policy_hash,
        }))
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
        .as_slice(),
    );

    Ok(Json(VerifyResponse {
        verification_status: Some(if result.passed {
            VerificationStatus::Passed
        } else {
            VerificationStatus::Failed
        }),
        passed: result.passed,
        score: result.score,
        reason_codes: result.reason_codes,
        verifier_result_hash,
        provider_family: state.capabilities.provider_family.clone(),
        model_id: state.capabilities.model_id.clone(),
    }))
}
Return verification_status = "inconclusive" (not "failed") when an evidence reference is unreachable due to a network timeout or auth error. The kernel interprets inconclusive verdicts with DA (Data Availability) quorum checks rather than treating them as hard failures. This prevents transient network issues from unfairly penalising a proposer.