Skip to main content

Guard config

The agent hooks read ~/.config/jes/config.json to decide which guards run. jes login writes it the first time. Edit it by hand after that. A library Guard doesn't read this file; you pass policies to it in Python.

If XDG_CONFIG_HOME is set, the file is $XDG_CONFIG_HOME/jes/config.json.

The default file​

injection, indirect_injection, and hazards are on at 0.5. Every other guard is listed but off, with its fields filled in so you can see them. model is the decision model the judgments ask, jev-latest by default.

~/.config/jes/config.json
{
"model": "jev-latest",
"guards": {
"injection": {
"enabled": true,
"threshold": 0.5
},
"indirect_injection": {
"enabled": true,
"threshold": 0.5
},
"hazards": {
"enabled": true,
"threshold": 0.5
},
"toxicity": {
"enabled": false,
"threshold": 0.5
},
"topics": {
"enabled": false,
"threshold": 0.5,
"deny": []
},
"invisible_text": {
"enabled": false,
"mode": "targeted",
"block": false
},
"allowed_tools": {
"enabled": false,
"names": []
},
"tool_safety": {
"enabled": false,
"threshold": 0.5
},
"canary": {
"enabled": false,
"token": ""
},
"regex": {
"enabled": false,
"patterns": [],
"action": "block",
"match": "search",
"require": false,
"fold": false,
"timeout_ms": 50
},
"substrings": {
"enabled": false,
"terms": [],
"action": "block",
"whole_words": false,
"fold": true
},
"token_limit": {
"enabled": false,
"limit": 8000,
"encoding": "cl100k_base",
"mode": "block"
},
"pii": {
"enabled": false,
"entities": [
"EMAIL_ADDRESS",
"PHONE_NUMBER",
"CREDIT_CARD",
"US_SSN",
"IBAN_CODE",
"CRYPTO"
],
"input_mode": "redact",
"untrusted_mode": "mask",
"output_mode": "flag",
"tool_call_mode": "block",
"restore": true
},
"secrets": {
"enabled": false,
"redact": "all"
}
}
}

0.5 is a starting point, not a measured recommendation. See Choosing a threshold.

Rules​

  • The file is one object with two keys: guards, and an optional model. Any other key is an error.
  • model is a model id on the TypeSafe API, such as jev-latest (the default) or a pinned Jev release like jev-1.13.0.
  • Each guard has enabled. "enabled": false skips it.
  • An unknown guard, an unknown field, or a wrong type is an error, and the hook blocks until you fix it.
  • A threshold is a number from 0 to 1. It's required when a judgment guard is enabled.
  • stages and name are fixed at each policy's defaults. A custom judge question can't go in this file. Use the Python library for those.

Judgments​

These send text to the decision model in model. Each takes threshold.

GuardOther fieldsChecks
injectionThe user's prompt, or a tool call's arguments, tries to override the agent's instructions.
indirect_injectionA tool result carries instructions aimed at the agent.
hazardscategories: a list such as ["S1", "S2"]. Omit for all of S1–S14. Each category is its own question.Harmful requests and replies. See hazards.
toxicitylabels: any of toxicity, severe_toxicity, obscene, threat, insult, identity_attack, sexual_explicit. Omit for all.Toxic text.
topicsdeny: the topics to block. Required and non-empty when enabled.Text about a topic you name.
tool_safetyThe tool call is destructive, exfiltrates data, changes privileges, or goes beyond what the user asked.

Transforms​

These run inside the hook process. They don't call the model.

GuardFields
allowed_toolsnames: the tools that may run. Required and non-empty when enabled. Every other tool call is blocked.
invisible_textmode: targeted (default) or all. block: block instead of stripping (default false).
canarytoken: a marker that must never appear in a reply or a tool call. Required when enabled.
secretsredact: all (default), partial, or hmac. key: base64 for at least 32 bytes, required with hmac and not allowed otherwise.
piientities: omit for EMAIL_ADDRESS, PHONE_NUMBER, CREDIT_CARD, US_SSN, IBAN_CODE, and CRYPTO. PERSON, UUID, IP_ADDRESS, and US_BANK_NUMBER are opt-in. input_mode: redact (default), mask, or block. untrusted_mode: mask (default), redact, or block. output_mode: flag (default), redact, or block. tool_call_mode: block (default) or flag. restore: default true.
regexpatterns (required and non-empty when enabled), action (block or redact), match (search or fullmatch), require (default false), fold (default false), timeout_ms (default 50).
substringsterms (required and non-empty when enabled), action (block or redact), whole_words (default false), fold (default true).
token_limitlimit (required), encoding (default cl100k_base), mode (block or truncate).

secrets, regex, and token_limit need an extra: jes[secrets], jes[regex], and jes[tokens]. With uvx, add it to the hook command, for example uvx --from 'jes[secrets]==0.0.1' jes claude-hook. Without it the hook fails with that policy's error.

pii with the default entities is pattern-based and needs no extra. The hook default leaves out PERSON, because uvx installs no spaCy model. Listing PERSON in entities needs jes[pii] and a spaCy English model, and the hook fails without them; see Install.

The fields match the Python factories. Policies has the full behavior of each one.

Example: lock down tools​

Allow only read-only tools, and judge the rest of each call against the user's request:

{
"guards": {
"injection": {"enabled": true, "threshold": 0.5},
"indirect_injection": {"enabled": true, "threshold": 0.5},
"allowed_tools": {"enabled": true, "names": ["Read", "Grep", "Glob"]},
"tool_safety": {"enabled": true, "threshold": 0.5},
"secrets": {"enabled": true, "redact": "all"}
}
}

Guards you leave out are off.