FPL language reference
FPL Syntax Reference
This is the complete language reference for FPL (Faramesh Policy Language). It documents every keyword, operator, block type, and syntax construct. FPL files use the .fpl extension.
File structure
An FPL file contains one or more agent blocks. Each agent block defines a complete policy for a single agent identity. Comments start with # and extend to the end of the line.
# This is a comment
agent my-agent {
# ... blocks and rules go here
}
agent another-agent {
# multiple agents in one file are allowed
}Keywords
All reserved keywords in FPL. Keywords are case-sensitive and always lowercase (except deny!).
KEYWORDCONTEXTMEANING
agenttop-levelDeclares an agent policy block. Takes a name identifier.
defaultagentSets the default verdict when no rule matches. Value: permit or deny.
modelagentConstrains which LLM model this agent may use. Value: string.
frameworkagentDeclares the expected agent framework. Value: string.
budgetagentOpens a budget block. Qualifier: session or daily.
maxbudgetMaximum cost limit. Value: dollar amount (e.g. $500).
dailybudgetDaily rolling cost limit. Value: dollar amount.
max_callsbudgetMaximum number of tool calls. Value: integer.
on_exceedbudgetWhat happens when budget is exceeded. Value: deny or defer.
phaseagentOpens a workflow phase block. Takes a phase name.
rulesagentOpens the global rules block.
permitrules/phaseAllow a tool call. Takes a tool pattern.
denyrules/phaseBlock a tool call. Takes a tool pattern.
deny!rules/phaseMandatory deny. Compile-time enforced, cannot be overridden.
deferrules/phaseHold a tool call for human approval. Takes a tool pattern.
whenruleConditional clause. Followed by a condition expression.
reason:ruleHuman-readable reason string attached to the verdict.
notify:ruleWho to notify on defer. Value: string (team/channel name).
reeval:ruleWhether to re-evaluate after approval. Value: true or false.
delegateagentOpens a delegation block. Takes a delegate name.
scopedelegateWhich tools the delegate may use. Value: tool pattern or quoted string.
ceilingdelegateMaximum budget the delegate may use. Value: inherited or number.
ttldelegateTime-to-live for the delegation token. Value: duration (e.g. 1h, 30m).
ambientagentOpens the ambient credential stripping block.
stripambientAn environment variable to remove from the agent process.
selectoragentOpens a selector block for external data binding.
sourceselectorURL of the external data source.
cacheselectorCache TTL for selector data. Value: duration.
bindselectorVariable name to bind the result to in policy context.
credentialagentOpens a credential broker binding block. Takes a credential name.
backendcredentialSecret backend to use. Value: vault, aws, gcp, azure, 1password, infisical.
pathcredentialPath to the secret in the backend.
fieldcredentialSpecific field within the secret to extract.
Tool patterns
Tool patterns match tool call names. They support exact match and wildcard (*) matching.
PATTERNMATCHES
shell/runExact match: only shell/run.
shell/*Wildcard: shell/run, shell/exec, shell/anything.
stripe/refundExact match: only stripe/refund.
stripe/*Wildcard: stripe/refund, stripe/charge, stripe/anything.
*Matches all tools. Use in default or catch-all rules.
api/v2/*Wildcard on nested paths: api/v2/users, api/v2/orders.
Condition expressions
The when clause accepts condition expressions. Conditions reference tool call arguments and policy context variables.
OPERATOREXAMPLEDESCRIPTION
>amount > 500Greater than. Numeric comparison.
<amount < 100Less than. Numeric comparison.
>=amount >= 500Greater than or equal.
<=amount <= 100Less than or equal.
==status == "active"Equality. Works with strings and numbers.
!=status != "blocked"Not equal.
matchescmd matches "rm -rf|DROP"Regex match against a pipe-delimited pattern.
andamount > 500 and risk > 0.8Logical AND. Both conditions must be true.
oramount > 1000 or risk > 0.9Logical OR. Either condition must be true.
Conditions can reference these built-in variables:
VARIABLEDESCRIPTION
amountNumeric argument named 'amount' from the tool call.
cmdString argument named 'cmd' from the tool call.
recipientsNumeric argument (e.g. number of email recipients).
riskNumeric risk score, typically from a selector binding.
pathFile path argument from the tool call.
tool_nameThe name of the tool being called.
session.sum(tool, window)Aggregate function: sum of a field across calls within a time window.
session.count(tool, window)Aggregate function: count of calls within a time window.
selectors.<name>.<field>External data bound via a selector block.
Effects (verdicts)
Every rule produces one of four effects. Rules are evaluated top to bottom , first match wins.
EFFECTBEHAVIOR
permitThe action is allowed. If a credential is needed, it is issued from the broker.
denyThe action is blocked. The agent receives a DENY verdict with the reason string.
deny!Mandatory deny. Same as deny but enforced at compile time , no child policy, extends, or priority can override it. This is the strongest guarantee in FPL.
deferThe action is held for human approval. The agent pauses. Nothing runs until someone approves or denies.
Rule syntax
Rules follow this structure. All clauses after the tool pattern are optional.
# Minimal rule — no condition
permit search/*
# Rule with condition
deny shell/run when cmd matches "rm -rf"
# Rule with condition + reason
deny! shell/run
when cmd matches "rm -rf|DROP TABLE"
reason: "destructive command blocked"
# Defer with condition + notify + reason
defer payment/transfer
when amount > 1000
notify: "finance-team"
reason: "high value transfer"
reeval: true
# Aggregate condition
deny stripe/refund
when session.sum(stripe/refund.amount, window=1h) > 2000
reason: "aggregate refund limit exceeded"
agent block
The top-level container. Everything else lives inside it.
agent <name> {
default deny # or permit
model "gpt-4o" # optional
framework "langgraph" # optional
# ... budget, phase, rules, delegate, ambient, selector, credential blocks
}budget block
Sets spending and call limits for the session. When exceeded, all subsequent calls are denied (or deferred, depending on on_exceed).
budget session {
max $500 # max cost per session
daily $2000 # daily rolling limit
max_calls 100 # max tool calls per session
on_exceed deny # deny or defer
}phase block
Defines a workflow phase with its own permit/deny/defer rules. Only one phase is active at a time. The agent or operator transitions between phases.
phase intake {
permit read_customer
permit get_order
permit search_kb
}
phase resolve {
permit issue_credit when amount <= 25
defer issue_credit when amount > 25
notify: "support-lead"
}rules block
Global rules applied regardless of the active phase. Evaluated top to bottom , first match wins.
rules {
deny! shell/* reason: "no shell access"
deny! fs/write when path matches "/etc|/usr|/bin"
defer payment/* when amount > 500
notify: "finance"
permit api/*
permit search/*
}delegate block
Grants a sub-agent a subset of permissions. Monotonic narrowing: the delegate can never exceed the delegator's scope.
delegate research-worker {
scope "search/*" # tools the delegate may call
ttl 1h # delegation expires after 1 hour
ceiling inherited # budget ceiling from parent
}ambient block
Lists environment variables to strip from the agent process before it starts. The agent never sees these values.
ambient {
strip OPENAI_API_KEY
strip STRIPE_API_KEY
strip AWS_SECRET_ACCESS_KEY
}selector block
Binds external data (risk scores, feature flags, user attributes) into the policy context. The data is fetched at decision time and cached.
selector fraud-score {
source "https://risk.internal/api/score"
cache 30s
bind ctx.fraud_score
}Use in rules: when selectors.fraud-score.score > 0.8
credential block
Binds a named credential to a secret backend. The broker issues the credential only when the policy permits the tool call.
credential stripe {
backend vault # vault | aws | gcp | azure | 1password | infisical
path secret/data/stripe/live
field api_key # specific field in the secret
ttl 15m # credential lease duration
}Duration values
Durations are used in ttl, cache, and window fields.
SUFFIXMEANING
sSeconds. Example: 30s
mMinutes. Example: 15m
hHours. Example: 1h, 24h
dDays. Example: 7d, 30d
Dollar values
Cost fields accept dollar-prefixed values. The $ is part of the syntax.
max $500 # integer
daily $2000 # integer
max_cost $50.00 # decimal
Evaluation order
Understanding rule evaluation order is essential to writing correct policies.
1The daemon receives a tool call and identifies the agent.
2If a kill switch is active for the agent, the call is denied immediately.
3If the session budget is exhausted, the call is denied.
4If the current phase has rules for this tool, those are evaluated first. First match wins.
5If no phase rule matched, the global rules block is evaluated. First match wins.
6If no rule matched anywhere, the default effect applies (permit or deny).
7deny! rules are enforced across all of the above , they cannot be overridden by any other rule.
CLI commands for FPL
COMMANDWHAT IT DOES
faramesh policy validate agent.fplCheck for syntax and semantic errors. Exit code 0 if valid.
faramesh policy inspect agent.fplPrint a summary of all rules, phases, and blocks.
faramesh policy compile "deny shell"Compile natural language to FPL.
faramesh policy fpl decompile policy.yamlConvert YAML back to FPL.
faramesh policy diff a.fpl b.fplShow differences between two FPL files.
faramesh policy backtest agent.fplReplay policy against historical DPR records.
faramesh policy suite policy.yaml --fixtures suite.yamlRun a policy test suite.
faramesh policy cover agent.fplShow coverage: which rules matched during backtesting.
Complete example
A production-ready FPL policy using all major constructs.
agent payment-bot {
default deny
model "gpt-4o"
framework "langgraph"
budget session {
max $500
daily $2000
max_calls 100
on_exceed deny
}
phase intake {
permit read_customer
permit get_order
}
phase resolve {
permit stripe/charge when amount <= 500
defer stripe/refund when amount > 500
notify: "finance"
reason: "high value refund"
}
rules {
deny! shell/* reason: "no shell access"
deny! fs/write when path matches "/etc|/usr"
deny stripe/refund
when session.sum(stripe/refund.amount, window=1h) > 2000
reason: "aggregate refund limit"
}
delegate finance-reviewer {
scope "stripe/refund"
ceiling inherited
ttl 4h
}
ambient {
strip OPENAI_API_KEY
strip STRIPE_API_KEY
}
selector risk {
source "https://risk.internal/api/score"
cache 30s
bind ctx.risk_score
}
credential stripe {
backend vault
path secret/data/stripe/live
field api_key
ttl 15m
}
}Save this as policy.fpl and run faramesh policy validate policy.fpl to check for errors. Every keyword and block in this example is documented above.