Anatomy & philosophy
The anatomy and philosophy of the single security.yaml config file.
.raxit/security.yaml is the only file you author. Everything raxit does — which tool calls
are allowed, which need a human, which are denied outright — comes from this one document.
raxit init generates a first draft from a real scan of your repo; you edit it, raxit plan
shows you the diff, raxit apply makes it live.
What raxit init writes
Running raxit init against a 3-tool LangChain agent produces:
✓ Framework detected: unknown
✓ Tools discovered: 3 — calc, post_to_webhook, read_customer_db
✓ .raxit/security.yaml written
✓ Agent id: default-agent (use --agent default-agent, or edit agent: in security.yaml)
✓ Policy compiler ready (Cedar stays internal)
Next steps:
edit .raxit/security.yaml tune the generated rules
raxit plan preview the decision changes
raxit apply make them live (enforce mode)"Framework detected: unknown" is normal even for a real LangChain script — tool discovery
(AST-walking @tool-decorated functions) and framework labeling are separate passes with
independent confidence. A correctly-discovered tool list doesn't require a correctly-labeled
framework.
and the .raxit/security.yaml it writes:
apiVersion: raxit.security/v1
agent: default-agent
default: block
rules:
- effect: allow
resource: calc
- effect: allow
resource: post_to_webhook
- effect: allow
resource: read_customer_db
tests:
- name: calc is permitted
action: calc
expect: permit
- name: unknown_tool is denied (default-deny)
action: unknown_tool
expect: denyEvery tool the scan finds becomes a rules[] entry: sensitive tools (as flagged by the scan)
get effect: review, everything else gets effect: allow, and anything the scan explicitly
marks forbidden gets effect: block. raxit init --demo payments-bot shows all three effects
plus the implicit default-deny in one file:
apiVersion: raxit.security/v1
agent: payments-bot
default: block
rules:
- effect: allow
resource: read_file
- effect: allow
resource: search_docs
- effect: allow
resource: send_email
- effect: review
resource: stripe/refund
- effect: block
resource: stripe/payouts
tests:
- name: read_file is permitted
action: read_file
expect: permit
- name: stripe/refund defers (sensitive tool)
action: stripe/refund
expect: defer
- name: stripe/payouts is denied (forbidden)
action: stripe/payouts
expect: deny
- name: unknown_tool is denied (default-deny)
action: unknown_tool
expect: denyraxit init always appends tests: entries for you: the first allow rule expects permit,
the first review rule expects defer, the first block rule expects deny, and a final
unknown_tool case always expects deny — asserting the default-deny floor from day one. See
The tests: block for the full shape.
Deny-by-default, always
default: accepts observe, audit, enforce (and the legacy alias block, which means the
same thing as enforce). allow is explicitly rejected — there is no "default: allow" escape
hatch:
security.yaml: default allow is not supported; use enforceTwo deny-by-default behaviors are worth internalizing:
- Unmatched calls deny. If no
rules[]entry covers a resource, the call is denied (deny.default_or_forbid) — there is no implicit fallthrough-to-allow. - Empty discovery is an explicit deny-all, not an empty file. If a repo scan finds zero
tools,
raxit initdoes not write an empty or invalidrules:block — it writes a single explicit{effect: block, resource: "*"}wildcard-deny rule instead, so the generated file "reads as deny-all rather than being an accidentally-empty file."
That second point is raxit's stance on the fail-open surprise: a config that looks like it does nothing should never behave like it does nothing. An agent with no discovered tools gets a policy that visibly and loudly denies everything, not a policy that silently permits everything because no rule happened to match.
The agent: field must match how you run
agent: is a required, freeform string — it becomes the Cedar principal (Agent::"<value>")
and the identity every rule is matched against. It has no format constraint beyond
non-blank, but it must match the identity the agent actually presents at runtime: either
--agent <id> on raxit run, or RAXIT_AGENT_ID in the SDK/environment.
If the attested principal doesn't match agent: in any rule, every call structurally denies —
not because a rule matched and blocked it, but because no rule could ever reference that
principal. This is deny.principal_mismatch, and it's common enough on a first run that it has
its own troubleshooting entry: see
Common issues → wrong agent id.
What's next
- Rules & effects — every rule field, exhaustively.
- The tests: block — asserting policy behavior before
applyships it. - Budgets, tenants, egress & sandbox — everything else the schema supports.

