docs · rule format

Rule format.

Every detection rule is a YAML document. The schema is small and stable. Rule packs are tar archives of these YAML files plus a manifest, signed end-to-end with Ed25519. The daemon refuses to load any rule pack whose signature does not verify against the embedded public key.

the schema · a single rule

Example: R1 subcommand-chain bypass.

# rules/v1/R1_subcommand_chain.yaml
id: R1
name: subcommand-chain bypass
version: 1
authors:
  - oknek-core
references:
  - url: https://adversa.ai/blog/claude-code-security-bypass-deny-rules-disabled/
    label: Adversa CC-643 (April 2026)
  - url: https://www.theregister.com/2026/04/01/claude_code_rule_cap_raises/
    label: The Register coverage
attack_techniques:
  - mitre_atlas: AML.T0051
  - owasp_asi: ASI05
kind: exec_observed
match:
  any_of:
    - chain_depth: { gte: 8 }
threshold:
  default: 8
  configurable: true
action:
  default: block
  configurable: true
  options: [ block, warn, allow ]
evidence:
  capture:
    - bash_command
    - chain_depth
    - chain_breakdown
    - agent_identifier
    - parent_pid
  retain_for: 90d

field by field

Schema reference.

FieldTypeRequiredNotes
idstring · R{n}yesGlobally unique. R1–R7 reserved for core pack. Authors use B{n} for business-only or X{n} for custom packs.
namestringyesHuman-readable, lowercase. Appears in oknek logs and the dashboard.
versionintegeryesIncremented on every revision. Daemon refuses to downgrade.
kindenumyesOne of: exec_observed, file_opened, socket_connect, file_changed, baseline_drift, file_scanned, mcp_endpoint. Determines which kernel hook fires the rule.
matchpredicateyesBoolean expression: any_of / all_of / none_of with leaf predicates per kind. See predicates table below.
thresholdobjectnoNumeric default + whether the user can override via /etc/oknek/oknek.yaml.
actionobjectyesWhat to do when the rule matches. Options: block (SIGSTOP the process), warn (log + alert, allow through), allow (effectively disabled but still logged).
evidenceobjectyesWhat to capture when the rule fires. capture lists field names. retain_for sets local retention.
referencesarraynoExternal citations — CVE URLs, research papers, blog posts.
attack_techniquesarraynoMITRE ATLAS and OWASP ASI alignment.

how rule packs are distributed

Pack structure + signing.

oknek-rules-v1.0.5.tar.gz
├── manifest.yaml          # pack metadata · timestamps · rule list · prev pack hash
├── rules/
│   ├── R1_subcommand_chain.yaml
│   ├── R2_settings_json_flip.yaml
│   ├── R3_credential_read.yaml
│   ├── R4_mcp_url_drift.yaml
│   ├── R5_egress_allowlist.yaml
│   ├── R6_claudemd_injection.yaml
│   └── R7_behavioral_drift.yaml
└── signature.ed25519      # Ed25519 sig over sha256(manifest.yaml || all rules)

The public key is embedded in the oknekd binary at build time, never fetched at runtime. To rotate it we ship a new oknekd binary — not a new rule pack. This makes the rule-pack channel non-trust-bootstrappable in a way malicious rule packs cannot hijack.

custom rules · business + enterprise tiers

Writing your own.

Business tier and above can author site-local rules. Drop them in /etc/oknek/rules.d/ with an X-prefixed ID. The daemon picks them up on the next reload (oknek update --reload-local).

# /etc/oknek/rules.d/X100_no_aws_secrets_outside_business_hours.yaml
id: X100
name: aws credential read outside business hours
version: 1
kind: file_opened
match:
  all_of:
    - path: { glob: "~/.aws/credentials" }
    - process_name: { match: "claude" }
    - wall_clock_hour: { not_in: [9, 10, 11, 12, 13, 14, 15, 16, 17] }
    - wall_clock_dow: { not_in: [0, 6] }
action:
  default: warn
evidence:
  capture: [ path, agent_identifier, wall_clock, baseline_distance ]
  retain_for: 30d

Next: architecture.

See how a rule actually fires — the five-stage funnel from agent action to verdict.