Harbor predicate DSL (API reference)
This document describes the predicate_dsl field on Harbor intent creation and how evaluation results appear on evidence submission. It is aimed at API consumers, SDK authors, and operators integrating with harbor-intent-escrow.
Normative implementation: paybond-predicate-vm (evaluate, validate_predicate_document). Task context: tasks/PAYBOND-004-predicate-vm-and-safe-mvp-dsl.md.
Managed policy registry (V1-006): Approved templates, tenant versions, preview, and lifecycle APIs are described in the mergeable OpenAPI fragment harbor-policy-openapi.yaml (Harbor /policy/v1/*; gateway adds the /harbor prefix).
Where this appears
| HTTP | Field / body | Role |
|---|---|---|
POST /intents | predicate_dsl (JSON object, required) | Stored on the intent; validated at creation (structure, version, fuel limits). Digested for principal signing (v2). |
POST /intents/{id}/evidence | Request payload | Evaluated as evidence JSON against the intent’s predicate_dsl. |
POST /intents/{id}/evidence | Response predicate_evaluation | Structured PredicateReport (passed and trace). |
Harbor rejects malformed or oversized predicate documents at intent creation with 400 Bad Request. Some evaluation-time failures (for example schema shape problems) may surface as 422 Unprocessable Entity when they are classified as evaluation errors rather than a simple passed: false.
Tenant isolation
Evaluation uses only data from the current intent row in the authenticated tenant context: submitted evidence payload, intent amount_cents, and intent evidence_schema. It performs no network I/O and does not read other tenants or external systems. See Tenant scoping.
Document envelope
Every predicate document must include:
| Field | Type | Description |
|---|---|---|
version | integer | DSL revision. Only 1 is supported today. |
root | object | Root clause (see below). Must contain string field op. |
{
"version": 1,
"root": { "op": "true" }
}Unsupported version values are rejected.
Clauses (op tag)
All clauses are JSON objects with op in snake_case. Paths are arrays of string keys into the evidence JSON object (nested objects only).
Constants and composition
op | Fields | Semantics |
|---|---|---|
true | — | Always satisfied. |
and | clauses: array of clause objects | All clauses must pass. |
or | clauses: array of clause objects | At least one clause must pass. |
not | clause: single clause object | Negates inner result. |
Evidence comparisons
op | Fields | Semantics |
|---|---|---|
eq | path: string[], value: any | Evidence at path must equal value (JSON equality). |
completion | path: string[], value: any | Same as eq; preferred for audit readability (e.g. status completed). |
lte | path: string[], limit_source: string | Integer at path must be <= intent budget. Only limit_source "amount_cents" is supported (matches intent amount_cents). |
budget_cap | path: string[] | Shorthand: same as lte with amount_cents. |
Schema-aware check
op | Fields | Semantics |
|---|---|---|
schema_field | field: string (non-empty) | Top-level evidence key field must exist, and its JSON value’s type must match the intent’s evidence_schema.properties[field].type. |
evidence_schema should follow a minimal JSON Schema style, for example:
{
"type": "object",
"properties": {
"status": { "type": "string" },
"cost": { "type": "integer" }
}
}Supported type strings (same as common JSON Schema usage): null, boolean, string, integer, number, array, object. An array of strings is allowed for multi-type fields.
Limits (abuse prevention)
These limits apply to validation (create) and evaluation (evidence). Exceeding them returns an error and does not run unbounded work.
| Limit | Value | Applies to |
|---|---|---|
Max nesting depth (and / or / not) | 24 | Clause tree |
| Max fuel (AST nodes visited) | 256 | Whole document |
| Max path segments | 16 | path on eq, completion, lte, budget_cap |
Max clauses length in and / or | 32 | Per operator |
Evaluation response (predicate_evaluation)
On successful evidence submission, Harbor includes a predicate_evaluation object shaped like:
{
"passed": true,
"trace": [
{
"kind": "completion",
"detail": "value matched",
"data": { "path": "status", "passed": true, "expected": "completed", "observed": "completed" }
}
]
}| Field | Description |
|---|---|
passed | If true, Harbor’s MVP flow treats the predicate as satisfied for release vs refund transitions (see escrow lifecycle). |
trace | Ordered steps for audits and support (kind, detail, data). |
Logical failures (for example eq mismatch) yield passed: false with a trace explaining the failure; the HTTP status may still be 202 Accepted if evidence and signatures were valid.
Principal signing (v2)
Intent creation signing uses bincode v2 payload IntentCreationSignV2 (see signing.rs): it commits to BLAKE3 digests of canonical JSON for budget, evidence_schema, and predicate_dsl, plus amount, currency, deadline, tenant, DIDs, and predicate_ref. When implementing an offline signer, use the same canonical JSON rules as Harbor (json_value_digest / sorted object keys) or call canonical_intent_creation_message from the harbor_intent_escrow crate in tests/tools.
Examples
Always release when evidence is submitted (smoke / tests):
{
"version": 1,
"root": { "op": "true" }
}Completed status and cost under budget:
{
"version": 1,
"root": {
"op": "and",
"clauses": [
{ "op": "completion", "path": ["status"], "value": "completed" },
{ "op": "budget_cap", "path": ["cost"] }
]
}
}Evidence might be { "status": "completed", "cost": 5000 } with amount_cents >= 5000.
Require typed field per schema:
{
"version": 1,
"root": { "op": "schema_field", "field": "invoice_id" }
}Evidence must include top-level invoice_id, and evidence_schema.properties.invoice_id.type must describe its JSON type.
Changelog (document-level)
predicate_dsl.version | Meaning |
|---|---|
| 1 | Current wire format (this page). |
Future versions will be introduced with new task/spec entries; clients should treat unknown version as unsupported.