CEL engine
Understand DefenseClaw's typed ActionFacts projection, CEL admission rules, bounded evaluation, semantic ownership, and fallback behavior.
DefenseClaw embeds CEL as a bounded semantic matcher, not as a general-purpose
policy runtime. The server establishes a trusted tool-action boundary,
normalizes the action into typed ActionFacts, and evaluates one immutable
Boolean program per semantic rule owner.
Use Authoring CEL rules for the rule schema, field reference, enums, and examples. This page defines what the compiler and runtime accept and how a match becomes detection or enforcement evidence.
Evaluation pipeline
ActionFacts and raw tool arguments stay private to the in-process analysis
path. They are not exposed as a public CEL endpoint, logged as CEL input, or
written to the durable ordered-state ledger.
Admission and activation
The rule-pack loader compiles every supplied expression, including disabled
rules. Each expression must be Boolean, contain no surrounding whitespace, and
use only the admitted surface below. Its rule must also contain a valid bounded
pattern fallback.
Catalog categories merge before compilation:
- A unique custom category adds to the compiled baseline.
- A category with the same name as a bundled category replaces that entire category.
- Any schema, regex, type, admission, enum, or cost failure rejects the complete candidate generation.
A rejected candidate is not published. If the gateway already has an active in-process generation, that generation remains unchanged; offline validation simply reports the candidate failure.
Supported CEL subset
DefenseClaw admits a small, reviewable CEL surface.
| Supported | Examples and constraints |
|---|---|
| Boolean logic | &&, ` |
| Scalar comparison | ==, !=, and in for bounded Boolean, integer, and string values |
| Collection predicates | Boolean exists comprehensions; nesting is limited to two levels |
| String predicates | startsWith, endsWith, contains, and member-form matches |
| Literals | Boolean, integer, string, and scalar lists |
| Field selection | Typed fields reachable from f using protobuf snake-case names |
Arithmetic, ordering comparisons, indexing, ternaries, maps, has, null,
floating-point/unsigned/byte literals, arbitrary functions, message equality,
and list equality are rejected. DefenseClaw adds no custom CEL helper
functions. matches must use a literal Go/RE2 pattern of at most 512 bytes;
dynamic regexes and global-form matches(value, pattern) are rejected.
Resource limits
| Limit | Maximum |
|---|---|
| Expression size | 16 KiB in bytes and runes |
| Checked AST | 4,096 nodes; depth 64 |
| Comprehension nesting | 2 |
| CEL regex literal | 512 bytes |
| Fallback regex | 2,048 bytes |
| Static cost per rule | 6,000,000 |
| Runtime cost per rule | 1,250,000 |
| Semantic rules per effective catalog | 256 |
| Enabled catalog static cost | 32,000,000 |
| Trusted dispatch budget | 50 ms and 24,000,000 total cost |
These bounds apply before connector action mapping. Exceeding an admission limit rejects the candidate; exceeding a runtime limit takes that owner to its fallback without stopping unrelated rules.
Runtime results
| Result | Owner behavior |
|---|---|
| Compile or admission failure | Reject the candidate generation; do not publish it. |
| Projection is unsupported, partial, ambiguous, invalid, or uncertain | Run this owner's required regex fallback; a raw pattern match remains detection-only. |
CEL evaluates false | Produce no owner finding and suppress only this owner's claimed fallback IDs on the structured route. |
| CEL matches full facts but not the execute-only projection | Create detection-only evidence; preview behavior cannot synchronously block. |
| CEL matches both projections on an enforcement-eligible, synchronous surface | Create an enforcement candidate from the YAML metadata; the final same-rule proof gate must still authorize it. |
| CEL matches but the connector surface is not block-capable or enforcement-eligible | Create detection-only evidence. |
| CEL errors, times out, or exceeds runtime cost | Run this owner's regex fallback; unrelated rules continue and raw matches remain detection-only. |
Every trusted-action candidate then crosses a final, code-owned proof gate. It can contribute to enforcement only when its proof is complete and pinned to the same rule ID: authoritative ActionFacts semantic evidence, an exact code-owned built-in CodeGuard result from a complete scan, or a bounded exact fallback result. Raw regex matches, custom CodeGuard matches, parser-shadow evidence, partial or invalid projections, and unpinned results remain visible as detection-only findings. The gate never removes a finding; it only prevents incomplete evidence from authorizing a block.
Exact-boundary examples
| Family | Enforcement-eligible example | Advisory or quiet control |
|---|---|---|
| HTTP destination hostname | Static ASCII DNS bytes from generated authority, HTTP CONNECT, remote-resolved SOCKS4a/5h destination fields, plaintext HTTP Host or canonical HTTPS SNI observed after a SOCKS handshake, or canonical HTTPS origin SNI; GNU Wget generated authority/origin SNI additionally requires --no-config, and every component is bound to the exact external origin or proxy | A local-only route or numeric hostname, non-ASCII IDN spelling, dynamic or config-derived value, wrapper/pipeline/shell redirection, promptable authentication, unresolved file/TLS setup, a modeled eagerly checked compression/TLS/authentication capability option or modeled final enabled capability toggle, conflicting pre-wire option, direct plaintext HTTP Host override with no remaining proxy-visible authority, target routed through an HTTPS proxy without authenticated feature facts, unsupported multi-hop proxy chain, or bare PowerShell curl/wget alias |
| HAProxy client-IP preamble | None until #770 supplies an authenticated fact for the exact curl executable's version and build support | Every --haproxy-clientip form remains LOW and detection-only, including direct HTTP(S), proxy/SOCKS/preproxy, --noproxy, multiple targets or --next, static/dynamic/oversized values, setup-preempted commands, aliases, and any executable-path spelling |
| FTP control metadata | POSIX or structured-argv static curl FTP account or alternative bytes bound to one external FTP(S) origin or proven plaintext proxy observer | Dynamic/config-driven values, raw Windows shell input, a local-only destination, or an unproved route |
| SOCKS authentication | Static SOCKS4 username or SOCKS5 username/password bound to the exact external proxy peer | Disabled/bypassed proxy, unsupported chain, ambiguous credentials, or pre-connect failure |
| Telnet metadata | POSIX or structured-argv static USER, TTYPE, XDISPLOC, or NEW_ENV bytes bound to one direct external Telnet peer | Raw Windows shell input, capability-dependent, proxied, malformed, or local-only negotiation; invalid WS/BINARY settings |
| Static stdin upload | Portable literal echo or format-only printf bytes flowing into one exact external curl stdin upload | Shell options/escapes, expansion, wrappers, intermediate transforms, or local upload |
| Minix formatting | Exact mkfs.minix grammar targeting a raw device | Image target, help/version, invalid block count, near-miss executable, or untrusted path |
For the modeled curl compression/TLS/authentication capability controls, an eagerly validated positive option, or the final enabled state of a final-toggle option, closes every exact transmission projector, not just the hostname lane. A disabled negative form can remain eligible only when no earlier eager validation failed, every transfer group ends with the modeled final-toggle capability disabled, and every other prerequisite is exact. Other optional features remain within the narrower authenticated-capability boundary tracked in the follow-up linked from this change.
DefenseClaw has no current HAProxy client-IP enforcement lane. Curl 8.7.1
primary-source and loopback-wire evidence shows that a capable direct build
writes the PROXY preamble before the HTTP request and, for HTTPS, before TLS.
That audit also supplies future-projector constraints: the existing
no-ambient-defaults boundary, a maximum 1976-byte operand, exact receiver
binding, and rejection of --ipv4 with a literal IPv6 peer before any write.
It does not prove the executable that will run. The option is absent before
curl 8.2.0 and is compiled out with CURL_DISABLE_PROXY; native name, path,
dialect, and operating system do not authenticate either property. #770
owns the resolved executable identity, version/build capabilities, provenance,
and invalidation rules required before any of these forms can enforce.
Raw CMD/PowerShell curl gains exact ordinary HTTP(S) headers, origin
credentials, inline/body and file-upload projection, plus supported direct
proxy/SOCKS credentials. Exact hostname projection on PowerShell requires the
explicit native curl.exe or wget.exe; the unqualified names belong to
PowerShell aliases and remain detection-only. FTP account/alternative control
fields, SMTP envelope fields, and Telnet USER/TTYPE/XDISPLOC/NEW_ENV
remain detection-only on raw Windows shell input; their closed projectors still
require POSIX or structured argv.
Custom rules own their own rule ID. DefenseClaw may define internal alias, prerequisite, and safe-negative contracts for bundled owners, but those maps are not configurable through CEL or YAML. A fallback match and authoritative CEL result never produce duplicate findings for the same owner.
The stateful connector lifecycle determines which native events are synchronous, block-capable, outcome-paired, or eligible to advance durable ordered state.
Single-call CEL and ordered state
Single-call CEL findings can run on trusted inspect calls and authenticated native pre-tool hooks. Durable chains are narrower: only authenticated native connector hook events with the required session and invocation identity can advance the SQLite state ledger. Inspect, proxy, router, and OTLP traffic never advance a chain.
The 26 ordered chains are a fixed compiled catalog. Adding a CEL/YAML rule does not define a new sequence; custom CEL rules remain single-action matchers. Twenty-two chains are alert-only and four can contribute enforcement-safe proof, still subject to connector capability, profile posture, and the same-rule proof gate. See the deterministic detection reference for the canonical inventory and Stateful connector lifecycle for the pending-to-success contract and connector matrix.
Test engine behavior
defenseclaw guardrail validate-pack is the public authority for schema,
regex, CEL typing and admission, and aggregate-cost checks. It does not inject
synthetic facts or prove that a connector projects the fields an expression
expects.
Repository contributors should add focused Go fixtures and run:
go test ./internal/guardrail/semantic ./internal/guardrail ./internal/gatewayWhen the compiled default catalog changes, also run
make generate-guardrail-catalog and make check-guardrail-catalog.
Authoring CEL rules
Write portable DefenseClaw CEL expressions against typed ActionFacts, validate them, and test their connector coverage safely.
Stateful connector lifecycle
How authenticated connector hooks prove outcomes, commit ordered tool-call state, and preserve conservative enforcement guarantees.