Harbor uses predicate_dsl to define the release rule attached to an intent. When evidence is later submitted, Harbor evaluates that evidence against the stored rule and returns a pass or fail result with an audit trace.
This format is intentionally narrow. It supports deterministic checks on the submitted evidence and on the intent budget, but it does not run arbitrary code, call external services, or read data from another tenant.
Use predicate_dsl when you want to send the rule inline on POST /intents. Use predicate_ref when you want a reusable, versioned tenant policy managed centrally. For the surrounding endpoints, see the Harbor API reference.
Where this appears
| HTTP | Field / body | Role |
|---|---|---|
POST /intents | predicate_dsl (JSON object, used when predicate_ref is not provided) | Stored on the intent and validated when the intent is created. |
POST /intents/{id}/evidence | Request payload | Evaluated as evidence JSON against the intent's stored predicate. |
POST /intents/{id}/evidence | Response predicate_evaluation | Returns the evaluation result (passed and trace). |
Harbor rejects malformed or oversized predicate documents at intent creation with 400 Bad Request. During evidence submission, business-rule mismatches usually return passed: false, while evidence-shape problems can return 422 Unprocessable Entity.
Product guarantees
- Deterministic evaluation: the same predicate and evidence produce the same result.
- Bounded execution: Harbor enforces fixed depth, path, and fuel limits so evaluation remains predictable.
- Tenant-scoped inputs only: evaluation uses only the current intent, its declared evidence schema, and the submitted evidence payload.
- Stable release rule: Harbor binds the predicate to the intent at creation time so the rule evaluated later is the same rule the parties approved.
For the broader isolation model, see Tenant model.
Document envelope
Every predicate document must include:
| Field | Type | Description |
|---|---|---|
version | integer | DSL revision. Only 1 is supported today. |
root | object | Root clause. Must include string field op. |
{
"version": 1,
"root": { "op": "true" }
}
Treat unknown version values as unsupported.
Evidence paths
Paths are arrays of string keys into the submitted evidence JSON object. They support nested objects only.
Example:
{ "path": ["status"] }
reads payload.status, while:
{ "path": ["job", "result", "status"] }
reads payload.job.result.status.
Clauses (op)
All clauses are JSON objects with an op field in snake_case.
Boolean 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 the inner result. |
Evidence checks
op | Fields | Semantics |
|---|---|---|
eq | path: string[], value: any | Evidence at path must equal value using JSON equality. |
completion | path: string[], value: any | Same as eq, but intended for completion-style checks such as status = completed. |
lte | path: string[], limit_source: string | Integer at path must be less than or equal to a server-owned limit. Today the only supported value is "amount_cents", which compares against the intent amount. |
budget_cap | path: string[] | Shorthand for checking that an integer field is less than or equal to 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 type must match the type declared in evidence_schema.properties[field].type. |
This is useful when you want to require a field without hard-coding one exact value. evidence_schema follows a minimal JSON Schema-style contract, for example:
{
"type": "object",
"properties": {
"status": { "type": "string" },
"cost": { "type": "integer" }
}
}
Supported type values are null, boolean, string, integer, number, array, and object. An array of type strings is also allowed for multi-type fields.
Limits
These limits apply at both intent creation and evidence evaluation.
| 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 result (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 | Whether the evidence satisfied the predicate. |
trace | Ordered evaluation steps that explain why Harbor passed or failed the rule. |
Logical failures such as an eq mismatch return passed: false with a trace explaining what failed. That is different from a malformed request or invalid evidence shape.
Direct DSL vs managed policies
- Use
predicate_dslwhen each intent carries its own inline rule. - Use
predicate_refwhen you want a reusable tenant policy with versioning, preview, and controlled rollout. - Managed policy lifecycle endpoints are part of the Harbor API reference.
Examples
Accept any evidence (useful for smoke tests):
{
"version": 1,
"root": { "op": "true" }
}
Require a completed status and keep cost within 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 a declared field from the evidence 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.
Version support
predicate_dsl.version | Meaning |
|---|---|
| 1 | Current wire format (this page). |