Rules — API Reference
Package: dodil.data.ingest.v1 · Service: IngestService
A rule is a trigger: glob/MIME/size filters that bind a source to a pipeline. When an object matches, K3 spawns an ingest job that runs the bound pipeline. See Core Concepts → Rule.
Nothing ingests until a rule exists. Creating a pipeline — including via
CreateVectorPipeline,CreateTablePipelineorCreateObjectPipeline— writes no ingest rule. The facet creators deliberately leave rule scope to the caller, so a collection or table with no rule bound to it is real, listable, and will never receive a single object.CreateRuleis the step that turns a pipeline on.
| RPC | HTTP |
|---|---|
CreateRule | POST /:bucket/rules |
GetRule | GET /:bucket/rules/:rule_id |
ListRules | GET /:bucket/rules |
UpdateRule | PATCH /:bucket/rules/:rule_id |
DeleteRule | DELETE /:bucket/rules/:rule_id |
gRPC setup —
grpcurl, endpoints, reflection, and field-name casing — is covered once in Conventions → Using gRPC.
CreateRule
Request
dodil data
dodil data ingest add pdf-contracts \
-b kb-prod \
-s src_a1b2... \
-c pipe_a1b2... \
-i 'contracts/**/*.pdf'
-c/--collectiontakes a pipeline id, not a collection id. The CLI assigns it straight toCreateRuleRequest.pipeline_id; the flag name predates the pillar split and was never renamed. Pass the value you would send aspipelineId.The sibling
-p/--pipeline-typeflag (documented asembedding, extraction) is declared but never read — it is dead and setting it changes nothing.
The CLI covers only a subset of the request: it always sends enabled: true and exposes no --exclude, --include-mime, --min-size, --max-size, --priority or --description on add. Those are settable afterwards via dodil data ingest update, or up front over HTTP/gRPC.
Matching is conjunctive across axes (path globs ∧ MIMEs ∧ size). Within an axis, includes OR together; excludes always win. See Core Concepts → Glob pattern syntax for the full grammar (tokens, case-insensitivity, MIME wildcard, evaluation order) — important: patterns are ASCII case-insensitive.
Field semantics worth stating outright:
| Field | Behaviour |
|---|---|
max_size_bytes | 0 means no limit, not “reject everything”. min_size_bytes of 0 likewise means no floor. |
priority | Higher is matched first. It is an int32, so negatives are legal and sort last. |
enabled | A disabled rule stays in the table and keeps its id — it simply stops firing. Prefer enabled: false over DeleteRule when you may want the rule back. |
pipeline_id | Required, and the pipeline must exist in the same bucket. |
recipe_name | Provenance only — a stable recipe.id stamped when the rule came from a recipe install. Empty for hand-installed rules. Do not route on it. |
One more matcher applies that is not a field on this message: ingest rules skip source objects under any object destination’s excluded_prefix. That is what stops an object pipeline re-ingesting its own output forever, and it applies regardless of what your include patterns say.
Response
An IngestRule row — see Core Concepts → Rule.
GetRule
Request
HTTP
curl -sS "https://api.data.dodil.io/kb-prod/rules/rule_a1b2..." \
-H "Authorization: Bearer $DODIL_TOKEN"Response
An IngestRule row with server-derived binding (pipeline name, kind, destination) for display. Always route on pipeline_id — binding is empty if the pipeline was deleted. See Core Concepts → Rule.
message PipelineBinding {
string pipeline_name = 1;
string pipeline_kind = 2; // "vector" | "warehouse" | "free"
string destination_id = 3; // empty for free pipelines
string destination_kind = 4;
string destination_name = 5; // collection or table name
}
PipelineBinding.pipeline_kindreports the stored vocabulary — a table pipeline reads"warehouse", not"table". That differs from the?facet=query value onListPipelines, which istableand explicitly rejectswarehouse. Note also thatIngestJob.pipeline_kindadds"object"to the same set, while this message’s comment lists only three.
ListRules
Filter by source_id and/or pipeline_id.
ListRulesdoes not paginate over HTTP. The handler reads onlysource_idandpipeline_idand sendspagination: None, so?page_size=and?page_token=are ignored and the response carries every matching rule. Pagination is reachable over gRPC only.
Request
dodil data
dodil data ingest list -b kb-prod
dodil data ingest list -b kb-prod -s src_a1b2...
dodil data ingest list -b kb-prod -p pipe_a1b2...Rules are the dodil data ingest command group: add, get, list, update, delete.
Response
HTTP
{
"rules": [
{
"ruleId": "rule_a1b2...",
"bucket": "kb-prod",
"sourceId": "src_a1b2...",
"name": "pdf-contracts",
"includePatterns": ["contracts/**/*.pdf"],
"enabled": true,
"priority": 100,
"pipelineId": "pipe_a1b2..."
}
],
"pagination": { "nextPageToken": "", "totalCount": "1" }
}UpdateRule
Patch-style. Repeated fields use the StringList wrapper from dodil.data.common.v1 so absent / empty / non-empty have distinct meanings.
Request
HTTP
Disable a rule without deleting it:
curl -sS -X PATCH "https://api.data.dodil.io/kb-prod/rules/rule_a1b2..." \
-H "Authorization: Bearer $DODIL_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "bucket": "kb-prod", "ruleId": "rule_a1b2...", "enabled": false }'Tighten the include set:
curl -sS -X PATCH "https://api.data.dodil.io/kb-prod/rules/rule_a1b2..." \
-H "Authorization: Bearer $DODIL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"bucket": "kb-prod",
"ruleId": "rule_a1b2...",
"includePatterns": { "values": ["contracts/2026/*.pdf"] }
}'Re-bind to a new pipeline:
curl -sS -X PATCH "https://api.data.dodil.io/kb-prod/rules/rule_a1b2..." \
-H "Authorization: Bearer $DODIL_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "bucket": "kb-prod", "ruleId": "rule_a1b2...", "pipelineId": "pipe_new..." }'Response
An IngestRule row — see Core Concepts → Rule.
DeleteRule
Request
HTTP
curl -sS -X DELETE "https://api.data.dodil.io/kb-prod/rules/rule_a1b2..." \
-H "Authorization: Bearer $DODIL_TOKEN"Response
Empty (DeleteRuleResponse {}).
Deleting a rule stops new ingests under it. Existing jobs stay in the system with their rule_id pointing at the deleted row.
See also
- Sync — trigger discovery and ingestion on a source
- Jobs — one-shot ingest of a single object
- Core Concepts → Rule — full
IngestRule+PipelineBindingtypes grpcurlreference — full flag set + reflection-disabled fallbacks