> ## Documentation Index
> Fetch the complete documentation index at: https://docs.altheia.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Audit trail

> Every decision emits a Move event. Allowed actions index from chain; denials are recorded by the agent.

Every policy decision emits an on-chain event from `altheia::audit`. Each event carries the policy version at decision time, for incident replay. Allowed actions and lifecycle changes index straight from chain. Denials never land on-chain, so the agent records them itself.

<Frame>
  <img src="https://mintcdn.com/altheia/FaUWnizMhLhPg3t5/images/chronicle.png?fit=max&auto=format&n=FaUWnizMhLhPg3t5&q=85&s=b8f758b8286e4b02c1fb8b9b3de4e5cf" alt="Chronicle: every action allowed or denied, settled on-chain" width="3024" height="1674" data-path="images/chronicle.png" />
</Frame>

## On-chain events

| Event                | Emitted when                                                                                               |
| -------------------- | ---------------------------------------------------------------------------------------------------------- |
| `AllowedAction`      | An action passed policy. Carries `agent_id`, `policy_version`, `amount`, `target_package`, `timestamp_ms`. |
| `PolicyRevoked`      | The owner revoked the agent.                                                                               |
| `PolicyUpdated`      | Caps, scope, actions, or pause state changed. Carries version before and after.                            |
| `WithdrawalAttested` | A withdrawal closed its receipt. Carries `amount_in`, `amount_out`, `recipient`.                           |

Read them with `readAuditLog(client, corePkg, limit)`, which queries the `audit` module's events in descending order and returns the event type, transaction digest, timestamp, and parsed payload.

## Denials are recorded off-chain

A denied action aborts inside `check_and_consume` and emits nothing, so no chain query can find it. To make denials visible, the agent dry-runs the transaction first; if the policy aborts, it posts a `denied` row with the decoded reason and skips submitting. This is what `guardedSubmit` does, and it is how a denial reaches the dashboard Chronicle even though it never lands on-chain.

```ts theme={null}
const r = await guardedSubmit(client, exec, agentAddr, buildSwap(refs, amount), {
  baseUrl, token, agent_id, action_type: "deepbook_swap", amount: 0.05, asset: "SUI",
});
// r.ok === false, r.reason_code === "over_per_tx_cap"  — logged, never submitted
```

## How to read the trail

| Surface                 | What you see                                                     | Auth                     |
| ----------------------- | ---------------------------------------------------------------- | ------------------------ |
| **Dashboard Chronicle** | Every action, allowed or denied, with reason and digest.         | Operator wallet.         |
| **`readAuditLog`**      | The raw on-chain events from `corePkg`.                          | None. Public chain data. |
| **A Sui explorer**      | The same events under the package, plus the transaction digests. | None. Public chain data. |

The on-chain events are the source of truth for allowed actions and lifecycle changes. An auditor can read them directly without trusting the backend.

<Card title="Provision an agent" icon="arrow-right" href="/guides/provision-agent" horizontal>
  From the dashboard form to a funded, signable agent key.
</Card>
