Policy authoring
A Kratex policy is a single JSON file, kratex.policy.json, that says what each package is allowed to do. This page is how to write one. For examples ready to paste, see Recipes; for the exhaustive field-by-field schema, see Policy reference.
Anatomy of a rule
Every rule answers four questions: who is acting, what they are trying to do, when it applies, and what to do about it.
subject: who is calling: a package, or a source class likethird-party. Omit to match any caller.target: what they are trying to do: a file read, a network call, a spawn. Required.when: optional conditions that must hold, such as a lifecycle phase or CI only.effect: what happens:allow,audit, orblock. Required.
Every rule also carries an id, a unique name in org.description form.
Your first policy
Start with one rule. This blocks any third-party package from making a network call:
{
"id": "my-app.block-third-party-network",
"subject": { "source": "third-party" },
"target": { "kind": "network" },
"effect": { "action": "block" }
}
Read it as a sentence: for any third-party package (subject), on any network call (target), block it (effect).
A rule lives inside a policy file. The minimum is a version and a rules array:
{
"version": 1,
"rules": [
{
"id": "my-app.block-third-party-network",
"subject": { "source": "third-party" },
"target": { "kind": "network" },
"effect": { "action": "block" }
}
]
}
Save that as kratex.policy.json at your project root and it is active on the next kratex run. A policy that omits mode runs in enforce (blocking) mode; set "mode": "audit" to record violations without blocking while you take inventory.
Now add a rule that carves out an exception. Rules match most-specific-first, so a narrow allow beats the broad block:
{
"version": 1,
"rules": [
{
"id": "my-app.allow-stripe",
"subject": { "source": "third-party" },
"target": { "kind": "network", "host": "api.stripe.com" },
"effect": { "action": "allow" }
},
{
"id": "my-app.block-third-party-network",
"subject": { "source": "third-party" },
"target": { "kind": "network" },
"effect": { "action": "block" }
}
]
}
Any third-party package can now reach api.stripe.com, and nothing else over the network. The rest of this page is the vocabulary for the four rule parts.
The policy file
The CLI looks for a single file named kratex.policy.json, starting in the working directory and walking up to the filesystem root, so committing it at your project root covers every subdirectory. It is parsed as strict JSON: no comments, no trailing commas, no YAML.
The fields you will use most:
| Field | Description |
|---|---|
version | Required. Always 1. |
mode | "audit" or "enforce". Defaults to "enforce" when omitted. |
rules | Your rules. Omit to run with built-in rules only. |
groups | Reusable named sets. See Groups. |
sensitivity | Patterns that label data as sensitive. See Sensitivity and data classes. |
telemetry | Where events are routed. See Telemetry. |
root, metadata, and a few others round out the schema; the reference has the complete list.
Subject: who is calling
The subject matches the package that made the call, identified by caller-chain attribution.
{ "source": "third-party" } // any third-party package
{ "package": "prisma" } // exactly this package
{ "source": "first-party" } // your own code
sourceclassifies the caller by origin:first-party,third-party,unknown, orany.packagepins an exact package name, e.g."prisma"or"@scope/pkg". Apackagematch is more specific than asourcematch, so it wins ties.packageGroupreferences a named set fromgroups.packages.
Omit subject entirely to match any caller. Full subject fields: reference.
Target: what they are doing
Every target has a kind. The other fields narrow it; omit them to match the whole kind.
fs: file reads and writes
{ "kind": "fs", "operation": "read", "path": "~/.aws/credentials" }
{ "kind": "fs", "operation": "write", "path": "**/node_modules/**" }
{ "kind": "fs" } // any file operation
Match on operation (read or write) and path, a glob where ~ expands to your home directory and ** matches across directories.
network: outbound connections
{ "kind": "network", "host": "api.example.com" } // any connection to a host
{ "kind": "network", "host": "*.internal" } // host globs are allowed
{ "kind": "network", "port": 443 } // by port
Narrow by host (globs allowed), port, or protocol. The full operation list (dns, unix-socket, and more) is in the reference.
process: subprocess spawns
{ "kind": "process", "command": "curl" }
{ "kind": "process", "command": "npm", "args": ["publish"], "argsMatch": "prefix" }
Match on command (a binary name or absolute path) and args. argsMatch controls how args compare: exact (default), prefix, or any.
env: environment variables
{ "kind": "env", "operation": "read", "name": "OPENAI_API_KEY" }
{ "kind": "env" } // any env operation
Match on operation (read, write, delete, or enumerate) and name.
runtime: Node capability gating
{ "kind": "runtime", "capability": "workers" }
Gates a specific Node capability, such as workers, vm, ffi, or native-addons. Enforced on every supported Node version; the ffi label specifically needs Node 22.15+ (see Node.js compatibility). Full capability list: reference.
install: lifecycle scripts
{ "kind": "install", "operation": "lifecycle-script", "package": "some-pkg" }
Allows or blocks a named package’s lifecycle scripts during kratex install and kratex ci.
Effect: what happens
{ "action": "allow" }
{ "action": "audit", "severity": "high" }
{ "action": "block", "severity": "critical" }
action is required and is one of:
allow: permit it silently. Visibility events on permitted operations occur only when no rule matched and the target carries a data class.audit: permit it and always emit an event.block: what happens depends onmode:
| Mode | block does |
|---|---|
enforce (default) | Throws KratexPolicyViolation in the guarded process; the operation never completes. |
audit | Permits the operation but emits a would-block event. |
severity (info through critical) is optional and carried through to events for filtering. Set "mode": "audit" while taking inventory, then switch to enforce once your allow rules cover the calls you intend to keep.
Conditions: when a rule applies
The optional when block adds conditions that must all hold (logical AND).
{ "when": { "phase": "install" } } // only during install / ci
{ "when": { "ci": true } } // only in CI
{ "when": { "exposed": "*" } } // caller previously touched sensitive data
The conditions you will reach for most:
phase: the lifecycle phase:runtime,install,build,test,dev, orci.ci:truematches only in CI, detected from standard CI environment variables.exposed: matches callers that previously acquired an exposure label, for example after reading a credential."*"matches any exposure.targetClasses: data classes the target carries:secrets,credentials,pii,wallet, and so on.
exposed and targetClasses work with data classes, covered in Sensitivity and data classes. The full condition list (platform, nodeVersion, targetOutsideProjectRoot, and more) is in the reference.
Groups
Define a named set once and reference it from many rules:
{
"groups": {
"paths": {
"credential-dirs": { "include": ["~/.aws/**", "~/.ssh/**", "~/.kube/**"] }
}
},
"rules": [
{
"id": "my-app.block-credential-reads",
"subject": { "source": "third-party" },
"target": { "kind": "fs", "operation": "read", "pathGroup": "credential-dirs" },
"effect": { "action": "block" }
}
]
}
Group types: packages, extensions, paths, hosts, env, and commands. Each entry has include (at least one item) and an optional exclude. Reference a group from the matching field: packageGroup, pathGroup, hostGroup, and so on.
How rules are selected
When more than one rule matches an operation, Kratex keeps the single best match. The intuition:
- More specific wins. A named
packagebeats asourceclass; a literal path or host beats a glob; morewhenconditions beat fewer. - Immutable built-ins always win. A handful of structural built-in rules outrank any user rule, regardless of specificity (see Built-in rules).
- Your rules beat built-ins at equal specificity, and
blockbeatsallowwhen two package-pinned rules disagree. - Ties go to the later rule in the array.
If nothing matches, the operation is allowed. The full ordering with worked examples is in the policy reference.
Once you know the vocabulary, Recipes collects rules ready to paste for the common cases.