Policy as code
Keep your agent’s preflight policy in your repository as crawlcheck-policy.json: defaults, rules per action, and tests that say what decision you expect. It gets a content id, preflight uses it by id, and every receipt names the policy that decided.
1. Write the file
{
"kind": "crawlcheck-policy",
"v": 1,
"name": "Shopping agent, production",
"description": "Cite and read freely on fresh readings; a person approves every purchase; never touch accounts.",
"defaults": {
"max_age_hours": 168
},
"actions": {
"cite": {
"require_entity": true,
"on_warn": "require_confirmation"
},
"connect": {
"max_age_hours": 72,
"on_warn": "require_confirmation"
},
"transact": {
"max_age_hours": 24,
"on_warn": "block",
"human_approval": [
"transact"
]
},
"administer": {
"on_warn": "block",
"deny": [
"example.net"
]
}
},
"tests": [
{
"domain": "crawlcheck.io",
"action": "read",
"expect": [
"allow",
"warn"
],
"note": "our own site reads cleanly"
},
{
"domain": "crawlcheck.io",
"action": "transact",
"expect": [
"require_confirmation",
"block",
"unsupported"
],
"note": "a purchase always needs a person"
}
]
}Fields per action: max_age_hours, on_warn (warn, require_confirmation or block), require_entity, human_approval, allow, deny. defaults apply to every action; actions.<action> overrides them. A policy can only make a decision stricter, never looser. JSON Schema: /schemas/policy.json.
2. Check and test it
Download crawlcheck-policy.mjs (zero dependencies, Node 20+):
node crawlcheck-policy.mjs lint crawlcheck-policy.json # errors, and the policy id node crawlcheck-policy.mjs test crawlcheck-policy.json # runs tests[] against today's answers; exit 1 on a failure node crawlcheck-policy.mjs publish crawlcheck-policy.json # stores it under its id
In CI (GitHub Actions):
- run: curl -fsSO https://crawlcheck.io/crawlcheck-policy.mjs && node crawlcheck-policy.mjs test crawlcheck-policy.json
3. Use it by id
POST /api/v1/preflight
{"domain": "shop.example", "action": "transact", "policy_id": "pol1:<sha256>"}The id is sha256 over the canonical normalised file, so the same rules always carry the same id, and the script re-checks the id the server returns. Stored policies never change; GET /api/v1/policy?id=… returns the exact file. A template or inline policy sent with policy_id combines with it, and the strictest value of each field wins.
API: POST /api/v1/policy · POST /api/v1/policy/test · preflight