logira supports per-run custom detection rules via:
./logira run --rules ./my-rules.yaml -- <command...>Custom rules use the same YAML format as the built-in rules in internal/detect/rules/default_rules.yaml.
- Built-in rules are always active.
--rulesappends your custom rules to the built-in set for that run.- Rule IDs must be unique across the merged set (built-in + custom).
- Invalid YAML / invalid rule schema causes
logira runto fail before the run starts. - File event retention is rule-driven:
- file events are only recorded if they match at least one active file rule
- adding file rules may increase stored file-event volume
rules:
- id: "X001"
title: "Example rule"
type: "exec" # exec | file | net
severity: "low" # info | low | medium | high
when:
exec:
contains_all: ["curl", "|", "sh"]
message: "possible curl pipe to shell: {{exec.filename}}"Each rule entry under rules: supports:
id(required): unique rule ID stringtitle(required): human-readable nametype(required):exec|file|netseverity(required):info|low|medium|highwhen(required): type-specific condition block (when.exec,when.file, orwhen.net)message(required): detection message template
Use when.exec:
when:
exec:
contains_all: ["curl", "|", "sh"]
contains_any: ["bash", "dash"]Fields:
contains_all: all strings must be presentcontains_any: at least one string must be present
Notes:
- Matching is case-insensitive.
- Matching is performed against a combined lowercase string of
filename + " " + argv.... contains_allandcontains_anycan be used together.
Use when.file:
when:
file:
prefix: "$HOME/.ssh/"
op_in: ["create", "modify"]
require_exec_bit: falseSupported fields:
- Path selector (exactly one required):
prefix: directory-prefix match (single)prefix_any: list of directory prefixes (OR)path_in: exact path list (OR)path_regex: regular expression match
op_in(required): allowed file operationsrequire_exec_bit(optional): iftrue, file must have an executable bit set at evaluation time
Allowed op_in values:
createmodifydeleteopenread
Notes:
- Exactly one of
prefix,prefix_any,path_in,path_regexmust be set. $HOMEis supported in file paths andpath_regexand is expanded to the audited user's home directory.path_regexis matched against the normalized absolute path.require_exec_bit: trueuses a best-effortstat(2)at evaluation time; missing files or stat failures do not match.
Use when.net:
when:
net:
op: "connect"
dst_port_in: [443, 8443]
dst_ip_in: ["169.254.169.254"]Supported fields:
op(optional):connect|send|recv(best-effort observed values)dst_port_gte(optional): integer lower bound (0-65535)dst_port_in(optional): exact allowed destination portsdst_ip_in(optional): exact allowed destination IP strings
Notes:
- Fields combine with AND semantics.
- If no
when.netfields are set, the rule matches all net events of typenet. - If destination metadata is unavailable for an event,
dst_ip/dst_portmatching may fail. - Depending on kernel/environment, you may see
send/recvwithout a usefulconnectevent for some traffic (especially localhost demos).
message uses Go text/template under the hood, but logira supports a shorthand namespace style:
{{file.path}}{{net.dst_ip}}{{exec.filename}}
These are normalized internally to Go template form.
Available template fields by rule type:
filepathop
netopprotodst_ipdst_portbytes
execfilenameargvcommcwd
Template behavior:
- Missing keys are treated as zero values (best-effort data may be empty).
- If template rendering fails, logira falls back to the raw
messagestring.
Examples of invalid rules that logira run --rules will reject:
- duplicate rule ID (including collision with built-in rules)
- invalid
type/severity - missing
message,id, ortitle filerule without a path selectorfilerule with more than one path selectorfile.op_inmissing or containing unsupported values- invalid
path_regex net.dst_port_invalues outside0..65535
- Quick trial rules:
examples/rules/quickstart.yaml - Trial commands and notes:
examples/rules/README.md