Config Format
The config format is shared across all five implementations. Same JSON/YAML structure, same semantics.
MatcherConfig
Top-level config for a matcher:
{
"matchers": [ ... ],
"on_no_match": { ... }
}
| Field | Type | Required | Description |
|---|---|---|---|
matchers | array of FieldMatcherConfig | Yes | Field matchers evaluated in order |
on_no_match | OnMatchConfig | No | Fallback when no field matcher matches |
FieldMatcherConfig
A single rule: predicate + action:
{
"predicate": { ... },
"on_match": { ... }
}
| Field | Type | Required | Description |
|---|---|---|---|
predicate | PredicateConfig | Yes | Condition to evaluate |
on_match | OnMatchConfig | Yes | What to do when predicate matches |
PredicateConfig
Boolean logic over conditions. Discriminated by type:
single
Extract a value and match it:
{
"type": "single",
"input": { "type_url": "xuma.test.v1.StringInput", "config": { "key": "method" } },
"value_match": { "Exact": "GET" }
}
| Field | Type | Required | Description |
|---|---|---|---|
type | "single" | Yes | Discriminator |
input | TypedConfig | Yes | Data input reference (resolved via registry) |
value_match | ValueMatch | One of | Built-in string match |
custom_match | TypedConfig | One of | Custom matcher via registry |
Exactly one of value_match or custom_match must be set.
and
All child predicates must match:
{
"type": "and",
"predicates": [ { "type": "single", ... }, { "type": "single", ... } ]
}
or
Any child predicate must match:
{
"type": "or",
"predicates": [ { "type": "single", ... }, { "type": "single", ... } ]
}
not
Negate a predicate:
{
"type": "not",
"predicate": { "type": "single", ... }
}
OnMatchConfig
Either a terminal action or a nested matcher. Discriminated by type:
action
Return a value:
{ "type": "action", "action": "route-get" }
The action field can be any JSON value – string, number, object. The engine doesn’t interpret it.
matcher
Continue evaluation with a nested matcher:
{
"type": "matcher",
"matcher": {
"matchers": [ ... ],
"on_no_match": { ... }
}
}
Action XOR matcher – never both. This enforces OnMatch exclusivity from the xDS spec.
TypedConfig
Reference to a registered type:
{ "type_url": "xuma.test.v1.StringInput", "config": { "key": "method" } }
| Field | Type | Required | Description |
|---|---|---|---|
type_url | string | Yes | Registered type identifier |
config | object | No (defaults to {}) | Type-specific configuration |
The type_url is resolved at load time via the Registry. Unknown type URLs produce an error listing available types.
ValueMatch
Built-in string matchers:
{ "Exact": "hello" }
{ "Prefix": "/api" }
{ "Suffix": ".json" }
{ "Contains": "admin" }
{ "Regex": "^Bearer .+$" }
| Variant | Matches |
|---|---|
Exact | Exact string equality |
Prefix | String starts with value |
Suffix | String ends with value |
Contains | String contains value |
Regex | RE2 regex pattern (linear time) |
Type URL Reference
Core (all domains)
Registered by register_core_matchers() in all implementations:
| Type URL | Type | Config |
|---|---|---|
xuma.core.v1.StringMatcher | InputMatcher | StringMatchSpec |
xuma.core.v1.BoolMatcher | InputMatcher | { "value": true } |
Test Domain
| Type URL | Config | Extracts |
|---|---|---|
xuma.test.v1.StringInput | { "key": "method" } | Value for key from test context |
HTTP Domain
| Type URL | Config | Extracts |
|---|---|---|
xuma.http.v1.PathInput | {} | Request path |
xuma.http.v1.MethodInput | {} | HTTP method |
xuma.http.v1.HeaderInput | { "name": "content-type" } | Header value by name |
xuma.http.v1.QueryParamInput | { "name": "page" } | Query parameter by name |
Claude Domain
| Type URL | Config | Extracts |
|---|---|---|
xuma.claude.v1.EventInput | {} | Hook event name (e.g. PreToolUse) |
xuma.claude.v1.ToolNameInput | {} | Tool name (e.g. Bash) |
xuma.claude.v1.ArgumentInput | { "name": "command" } | Tool argument by name |
xuma.claude.v1.SessionIdInput | {} | Session ID |
xuma.claude.v1.CwdInput | {} | Working directory |
xuma.claude.v1.GitBranchInput | {} | Git branch |
Full Examples
HTTP Route Matching
matchers:
- predicate:
type: and
predicates:
- type: single
input: { type_url: "xuma.http.v1.PathInput", config: {} }
value_match: { Prefix: "/api" }
- type: single
input: { type_url: "xuma.http.v1.MethodInput", config: {} }
value_match: { Exact: "GET" }
on_match: { type: action, action: "api_read" }
- predicate:
type: single
input: { type_url: "xuma.http.v1.HeaderInput", config: { name: "content-type" } }
value_match: { Exact: "application/json" }
on_match: { type: action, action: "json_handler" }
on_no_match: { type: action, action: "not_found" }
Claude Code Hook Policy
matchers:
- predicate:
type: and
predicates:
- type: single
input: { type_url: "xuma.claude.v1.EventInput", config: {} }
value_match: { Exact: "PreToolUse" }
- type: single
input: { type_url: "xuma.claude.v1.ToolNameInput", config: {} }
value_match: { Exact: "Bash" }
- type: single
input: { type_url: "xuma.claude.v1.ArgumentInput", config: { name: "command" } }
value_match: { Contains: "rm -rf" }
on_match: { type: action, action: "block" }
- predicate:
type: single
input: { type_url: "xuma.claude.v1.EventInput", config: {} }
value_match: { Exact: "PreToolUse" }
on_match: { type: action, action: "allow" }
on_no_match: { type: action, action: "allow" }
Test Domain (key-value)
matchers:
- predicate:
type: and
predicates:
- type: single
input: { type_url: "xuma.test.v1.StringInput", config: { key: "method" } }
value_match: { Exact: "GET" }
- type: single
input: { type_url: "xuma.test.v1.StringInput", config: { key: "path" } }
value_match: { Prefix: "/api" }
on_match: { type: action, action: "api_get" }
- predicate:
type: single
input: { type_url: "xuma.test.v1.StringInput", config: { key: "path" } }
value_match: { Exact: "/health" }
on_match: { type: action, action: "health" }
on_no_match: { type: action, action: "not_found" }
Validation Limits
Configs are validated at load time:
| Limit | Value | Error |
|---|---|---|
| Max nesting depth | 32 levels | DepthExceeded |
| Max field matchers per matcher | 256 | TooManyFieldMatchers |
| Max predicates per AND/OR | 256 | TooManyPredicates |
| Max pattern length | 8192 chars | PatternTooLong |
| Max regex pattern length | 4096 chars | PatternTooLong |
If a config loads successfully, the resulting matcher is guaranteed to be structurally valid. Parse, don’t validate.