Policy Engine & Trust Chain

Policy is the root of trust for every enforcement decision: how a rule is authored, how it's signed, how the agent verifies it before trusting it, and how it's evaluated against a live command or request.

Lifecycle

%%{init: {"theme": "base", "themeVariables": {"fontFamily": "-apple-system, Segoe UI, Helvetica, Arial, sans-serif", "primaryColor": "#eef2f7", "primaryTextColor": "#0b0b0b", "primaryBorderColor": "#5b6b7c", "lineColor": "#5b6b7c", "secondaryColor": "#eef4ff", "tertiaryColor": "#f4f3f0", "clusterBkg": "#f9f9f7", "clusterBorder": "#c3c2b7", "edgeLabelBackground": "#fcfcfb", "actorBkg": "#eef2f7", "actorBorder": "#5b6b7c", "actorTextColor": "#0b0b0b", "actorLineColor": "#5b6b7c", "signalColor": "#52514e", "signalTextColor": "#0b0b0b", "labelBoxBkgColor": "#eef4ff", "labelBoxBorderColor": "#5b6b7c", "labelTextColor": "#0b0b0b", "loopTextColor": "#0b0b0b", "noteBkgColor": "#fdf3d9", "noteBorderColor": "#c98500", "noteTextColor": "#3d2f00", "activationBorderColor": "#5b6b7c", "activationBkgColor": "#eef4ff", "sequenceNumberColor": "#ffffff"}}}%% flowchart LR Author["Author a rule\n(Admin UI, no hand-edited JSON)"] --> Validate["Validate draft\n(regex compiles,\nlength bounds consistent,\nrequired fields present)"] Validate --> Simulate["Simulate against\na sample command"] Simulate --> Resolve["Resolve inheritance\n(depth-first, record\norigins/overrides)"] Resolve --> Sign["Sign at publication\n(Ed25519, key ID, fingerprint)"] Sign --> Distribute["Distribute signed snapshot"] Distribute --> Verify["Agent verifies\nbefore trusting"] Verify --> Evaluate["Evaluate against\nlive commands/requests"] Sign -.rollback.-> Author

Authoring

A Security Officer composes rules — match conditions, action, severity, reason code, shell selectors, scope, exceptions — through the Admin UI. Drafts are validated before they can be published: an uncompilable regex, inconsistent length bounds, or a missing required field is rejected at this stage. A draft can be simulated against a sample command line before publishing, returning the action and matched rule ID that would result.

Inheritance is resolved depth-first at publication time: rule origins and overrides are recorded inside the signed composition block itself, and a missing parent policy or an inheritance cycle is rejected.

Signing and integrity verification

Every distributed policy snapshot is an Ed25519-signed artifact, and the agent verifies it in a strict, ordered chain before treating it as active:

%%{init: {"theme": "base", "themeVariables": {"fontFamily": "-apple-system, Segoe UI, Helvetica, Arial, sans-serif", "primaryColor": "#eef2f7", "primaryTextColor": "#0b0b0b", "primaryBorderColor": "#5b6b7c", "lineColor": "#5b6b7c", "secondaryColor": "#eef4ff", "tertiaryColor": "#f4f3f0", "clusterBkg": "#f9f9f7", "clusterBorder": "#c3c2b7", "edgeLabelBackground": "#fcfcfb", "actorBkg": "#eef2f7", "actorBorder": "#5b6b7c", "actorTextColor": "#0b0b0b", "actorLineColor": "#5b6b7c", "signalColor": "#52514e", "signalTextColor": "#0b0b0b", "labelBoxBkgColor": "#eef4ff", "labelBoxBorderColor": "#5b6b7c", "labelTextColor": "#0b0b0b", "loopTextColor": "#0b0b0b", "noteBkgColor": "#fdf3d9", "noteBorderColor": "#c98500", "noteTextColor": "#3d2f00", "activationBorderColor": "#5b6b7c", "activationBkgColor": "#eef4ff", "sequenceNumberColor": "#ffffff"}}}%% flowchart TB S1["1. Schema version check"] --> S2["2. Structural validity"] S2 --> S3["3. Content fingerprint\n(SHA-256 of canonical payload)"] S3 --> S4["4. Ed25519 signature verification\n(against explicitly trusted key set only)"] S4 --> S5["5. Expiry check"] S5 --> Active["Snapshot becomes active"] S1 -.any failure.-> Fail["strict_fallback runtime mode\n+ policy_integrity_failure event\n(severity: high)"] S2 -.any failure.-> Fail S3 -.any failure.-> Fail S4 -.any failure.-> Fail S5 -.any failure.-> Fail style Fail fill:#fbe4e4,stroke:#d03b3b,color:#5c1616

These properties close common footguns in signature-based systems:

  • Trust is restricted to an explicitly configured key set. There is no implicit trust anchor. An empty trust configuration means an empty trust set, which means every snapshot fails verification — the failure mode of "nobody configured trust yet" is closed.
  • The built-in development signing key is never trusted at runtime, even if a misconfiguration explicitly lists it — a hard-coded refusal.
  • The managed trust store must be a regular file (not a symlink), root-owned, with no group- or world-write permission. Non-root ownership requires an explicit, test-only opt-out.
  • Anti-replay: a snapshot is rejected if its policy_version is lower than the highest version already accepted; an identical version+fingerprint pair is accepted idempotently (safe to re-apply). The high-water mark is stored in root-owned local state.
  • Every distributed snapshot must carry an expires_at.
  • The control plane defaults to a production security profile; a development profile requires explicit configuration and logs a warning — it is opt-in. The production profile additionally rejects the development signing key ID, development private key bytes, and a group-readable signing key file outright.
  • Canonicalization for fingerprinting/signing is re-serialization of the parsed struct.

Evaluation

Rules in an active snapshot are evaluated in snapshot order — first match wins. If nothing matches, the outcome is block, with a dedicated fail-closed reason code — there is no implicit "unmatched = allow."

  • Selector dimensions (surface, shell, scope, command match, required findings) are conjunctive across dimensions, disjunctive within a dimension: every populated dimension must match, but any one of several values within a dimension satisfies it. An empty dimension imposes no constraint. Text matching is case-insensitive substring matching unless a dimension specifies regex.
  • Exceptions suppress a matching rule only if every populated dimension of the exception matches the current context — an empty exception can never accidentally apply to everything.
  • A broken regex does not abort evaluation — and the direction of the failure is chosen deliberately per rule type: in a block or mask_output rule, an uncompilable pattern counts as a match (fails closed — better to over-block than silently permit a rule that can't even be checked); in an allow or warn rule, the same broken pattern simply contributes no match (fails open only where "open" still means "no enforcement claim was silently skipped," since allow/warn make no blocking claim in the first place).
  • Each regex pattern is compiled at most once per snapshot — a deliberate hot-path cost control, since evaluation runs on every intercepted command.

Continue to Zero-Knowledge Telemetry & Control Plane.

results matching ""

    No results matching ""