Documentation Index
Fetch the complete documentation index at: https://docs.usecompassai.com/llms.txt
Use this file to discover all available pages before exploring further.
The agent does not hold the user’s USDC. It holds a session key — an additional signer the Diamond authorizes to call a specific set of functions under a specific set of policy constraints. Every call the session key signs is validated on-chain before USDC moves.This page covers the session key mechanism. For the broader policy model the session key enforces, see Policy engine. For how the session key fits into the Diamond, see Diamond account.
What a session is
A session is a record stored on the user’s Diamond. Each user’s Diamond has its own session table; there is no global agent registry.| Field | Type | Meaning |
|---|---|---|
agent | address | The session-key signer. |
expires_at | uint64 | Unix timestamp. Session is dead after this time. |
allowed_selectors | bytes4[] | Function selectors the agent may call. |
validateUserOp call.
Registration — owner-only
A session is created by the owner calling the Security facet:- Owner-only. Only the user’s EOA can grant or modify a session. The
agent’s own session key has no permission to call
registerSession. - Bounded expiry.
expiresAtis required. There is no “permanent” session — every session naturally expires and must be renewed by the owner. - Selector whitelist.
allowedSelectorsenumerates which functions the agent may call from the Diamond. Selectors not in this list are unreachable by the agent.
Validation — every UserOp
Every ERC-4337 UserOp the agent submits passes throughvalidateUserOp on
the Account4337 facet. The validator checks the call against two layers:
the session key whitelist (this page) and the on-chain policy
(policy engine).
The full check sequence for an agent-signed UserOp:
- Signature recovery. Recover the signer address from the UserOp signature.
- Owner short-circuit. If the signer is the owner EOA, allow anything.
- Session lookup. Look up the signer in the session table:
- Reject if no session exists.
- Reject if
block.timestamp >= expires_at. - Reject if the UserOp’s entry selector is not in
allowed_selectors.
- Policy check. Decode the call arguments and run them against the
on-chain policy stored on the Diamond:
- Is the
(target_protocol, target_chain)pair inprotocol_whitelist? - Is the target chain in
chain_whitelist? - Does the target protocol’s risk score satisfy the user’s
risk_band? - Does the amount fit
per_route_cap_usdc? - Does the rolling 24-hour total stay under
daily_cap_usdc?
- Is the
- EntryPoint prefund. Pay the EntryPoint gas prefund from the Diamond if any is owed.
On-chain and off-chain — same rules, two places
The policy attached to the user’s Diamond is the single source of truth. Both the off-chain deterministic loop and the on-chain session key validation read from it:- Off-chain (deterministic loop). Reads a cached copy of the policy from the indexed Diamond storage. Used to filter out plans before they ever become UserOps. This is a performance optimization — it saves gas and gives the dashboard structured rejection messages.
- On-chain (session key). Reads the policy directly from Diamond storage on every UserOp. This is the actual safety boundary. Even if the off-chain layer is compromised or bypassed, the on-chain check is what physically prevents an out-of-policy call from executing.
validateUserOp rather than execute.
What the session key cannot do
| Action | Why it’s impossible |
|---|---|
| Withdraw to a non-owner address. | Transfer selectors that move USDC to external addresses are never included in allowed_selectors. Only the owner can sign for them. |
| Grant itself more permissions. | registerSession and all other Security facet mutations are onlyOwner. |
Survive past expires_at. | Every UserOp re-checks expiry against block.timestamp. A session is dead the moment the timestamp passes. |
| Reach a non-whitelisted protocol. | Two gates: (1) the protocol’s facet has to be registered on the Diamond, and (2) the session has to include that facet’s selectors. Both are owner-only. |
| Exceed a policy parameter. | Even calls to whitelisted selectors are checked against risk_band, caps, and whitelists on every UserOp. |
| Upgrade the Diamond. | DiamondCut selectors are never in allowed_selectors. Upgrades go through the separate authority model. |
supply function with any amount, on any
chain. The on-chain policy check is what makes the whitelist meaningful:
it ties each call to the user’s specific rules.
Revocation — the kill switch
The owner can revoke a session at any time with a single transaction:- The agent’s session is removed from the session table.
- All future UserOps signed by that agent address fail at step 3 of validation.
- The Diamond’s USDC and existing positions are untouched. Revocation affects future calls only.
Two independent kill switches
Compass exposes two ways to stop an agent from acting on your account:| Layer | Effect | Recovery |
|---|---|---|
| Off-chain pause | The deterministic loop’s scheduler skips your account; no new plans are dispatched. | Resume from the dashboard. |
On-chain revokeSession | The session key is invalidated at the contract level. UserOps revert at validation. | Owner registers a new session. |
Next steps
Policy engine
The full rule set every UserOp is checked against.
Authority & upgrade model
The separate path for shipping new facets — and how to revoke it.
Diamond account
The smart account architecture this session key lives on.
Trust & security model
The big picture in plain language.