Constraint types
The real constraint types AgentGuard, Smart Sessions, and mandates enforce - what each binds and how it's set.
Botanary has three separate places authority gets bound, each with its own constraint vocabulary. This page is the reference for all three: your account's own rules (AgentGuard), a v1 delegation's policy (Smart Sessions), and a mandate's policy (the mandate executor).
Account rules (AgentGuard)
These are the constraint types available on your own account, set through AgentGuard's owner-lane setters.
| Constraint | Binds | Set with |
|---|---|---|
| Frozen | the whole account | setFrozen(bool) |
| Spending cap | a token, over a rolling period | setCap(token, limit, periodSeconds) |
| Per-action max | a token, on a single action | setPerActionMax(token, max) |
| Allowed contracts | a contract address | addAllowedContract / removeAllowedContract |
| Denied contracts | a contract address | addDeniedContract / removeDeniedContract |
| Recipients | a payee address | addRecipient / removeRecipient |
| Permitted stablecoins | a token | addPermittedStablecoin / removePermittedStablecoin |
| Safe harbor | a recovery address | addSafeHarbor / removeSafeHarbor |
A denied contract or recipient always wins over an allowed one. An empty allow or deny list is unrestricted, not locked down - you tighten these over time, starting from open. A safe-harbor address stays reachable even while the account is frozen, so freezing never traps your own recovery path.
Spending caps run over one of three fixed periods:
| Period | Length |
|---|---|
day | 24 hours |
week | 7 days |
month | 30 days |
Unlike a delegation's spend meter, an account cap resets at the end of its period rather than just expiring - each cap tracks its own limit, what's been spent, what's remaining, and when it next resets.
A v1 delegation's policy (Smart Sessions)
A delegation's bounds are checked in ERC-4337 validation, before the operation is ever included on-chain:
| Constraint | Binds | Notes |
|---|---|---|
budgets | total spend per token | lifetime cumulative, never a rate |
perActionMax | max per single action | optional |
maxActions | total number of actions | lifetime count, never a rate |
recipientAllowlist | which addresses can receive funds | empty means unbounded |
allowedActions | which (contract, function) pairs are callable | each needs at least one function selector |
expiresAt | when the whole grant lapses |
See Granting a delegation for how each of these gets set and what their real limits are (like the 16-recipient ceiling).
A mandate's policy (the mandate executor)
A mandate binds a different, fail-closed vocabulary, checked in execution rather than validation:
| Constraint | Binds | Notes |
|---|---|---|
| Venues | which (contract, function) pairs are callable | empty denies every call |
| Argument rules | a max value or a required exact value, at a specific argument position | e.g. capping a job's budget, or pinning a provider address |
| Recipients | which addresses can receive funds | empty denies every payee |
| Per-venue max | a spend cap on a specific venue | unset is read as refuse, not unlimited |
Where a v1 delegation's empty allowlist means "allow all," a mandate's empty venue or recipient list means "deny all" - the two shapes fail in opposite directions on purpose. See Granting a delegation for how a mandate gets built.
Three enforcement points, three vocabularies: AgentGuard binds your own actions, Smart Sessions binds a v1 delegation, and the mandate executor binds a mandate. A rule set in one place has no effect in another.