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

# Try it in your browser

> Run ctrlrun demo here, with no install: Python and the released ctrlrun wheel load into this tab and the five scenarios run in front of you.

Press the button and `ctrlrun demo` runs in this tab: real Python, the released `ctrlrun` wheel
from PyPI, and the same five scenarios you would get from `pip install ctrlrun && ctrlrun demo`.
Nothing is sent anywhere. The demo is in-process, writes its evidence to an in-memory
filesystem, and opens no socket.

<div id="ctrlrun-browser-demo">
  <button
    type="button"
    style={{
  background: "#F5A623",
  color: "#14161b",
  border: "1px solid #B8730A",
  borderRadius: "8px",
  padding: "10px 18px",
  fontSize: "15px",
  cursor: "pointer",
}}
  >
    Run ctrlrun demo
  </button>

  <pre
    style={{
  background: "#14161b",
  color: "#c9ccd3",
  borderRadius: "8px",
  padding: "16px",
  marginTop: "12px",
  overflowX: "auto",
  fontSize: "13px",
  lineHeight: "1.5",
  minHeight: "180px",
  whiteSpace: "pre",
}}
  >
    Press the button. The first run downloads about 10 MB of Python; after that the browser caches it.
  </pre>
</div>

## What you should see

All five scenarios run in the browser. This is the output from the verification run described
below, with the generated ids masked:

```text theme={null}
ctrlrun 0.5.0 on Python 3.14.2

CTRLRun demo — five ways an agent action goes wrong, and what stops it.
Policy: refunds up to €1,000 are autonomous, up to €10,000 need a human, above that are denied.

1. Duplicate effect after a lost response

   refund €500  →  remote commits  →  response lost  →  effect: AMBIGUOUS
   agent retries the same refund
   ✗ BLOCKED — effect may already have committed; blind retry refused
   remote refund calls: 1
   only a human moves it on:  ctrlrun resolve refund:txn_1 --committed|--failed

2. Approval mutation

   agent proposes refund €2,000  →  human approves apr_… (bound to the action hash)
   agent executes refund €5,000  →
   ✗ BLOCKED — approved action ≠ requested action (mismatch)

3. Concurrent agents, same effect

   Agent A  reserve refund:txn_123  →  ACQUIRED  →  executes
   Agent B  reserve refund:txn_123  →
   ✗ BLOCKED — already reserved (in_progress)

4. Approval replay

   approval apr_… used once  →  consumed
   same approval presented again                            →
   ✗ BLOCKED — single-use approval already consumed

5. Authority escalation

   human €100,000 delegable  →  finance agent €25,000  →  support agent €2,000
   support agent's grant: dlg_…
   support agent requests €50,000  →
   ✗ BLOCKED — outside the delegated grant (authority_constraint)
   remote refund calls: 0
   finance agent tries to delegate €50,000 under its own €25,000  →  refused (containment: constraints)
   support agent requests €1,500  →  authority permits it, and the policy asks a human (apr_…)
   two axes, and an action needs both: the stricter of the pair wins
```

Scenario 1 is the one to read twice: **`remote refund calls: 1`**. The refund committed at the
fake remote, the reply was lost, the agent retried, and the retry was refused. The customer was
refunded once.

## What is running

|         |                                                                                                                           |
| ------- | ------------------------------------------------------------------------------------------------------------------------- |
| Runtime | [Pyodide](https://pyodide.org/) 314.0.6, CPython 3.14.2 compiled to WebAssembly, from the jsDelivr CDN                    |
| Package | `ctrlrun`, installed from PyPI by micropip: the released version, the same wheel `pip install ctrlrun` gives you          |
| Storage | SQLite, bundled into Pyodide, writing to an in-memory filesystem that disappears when you close the tab                   |
| Network | the CDN and PyPI, to load Python and the wheel. The demo itself opens no socket: its remote is a fake in the same process |

The version line in the output is the released version, so it can be behind `main`. Everything
else on this site describes the current release.

## Verified, and how to check

The sequence this page runs was verified under Node against the same Pyodide build on
**2026-09-06**: Pyodide 314.0.6, Python 3.14.2, SQLite 3.39.0, `ctrlrun` 0.5.0 from PyPI, all
five scenarios. Two harnesses are committed, and you can run both:

```bash theme={null}
npm install pyodide jsdom
node docs/assets/verify-browser-demo.mjs    # Python, the wheel, and the five scenarios
node docs/assets/verify-browser-wiring.mjs  # this page's button, against a real DOM
```

[`verify-browser-demo.mjs`](https://github.com/CTRLRun/ctrlrun/blob/main/docs/assets/verify-browser-demo.mjs)
is the one that proved the demo runs.
[`verify-browser-wiring.mjs`](https://github.com/CTRLRun/ctrlrun/blob/main/docs/assets/verify-browser-wiring.mjs)
is the one that proves the button on this page gets wired: this site is a single-page
application, and the first version of the script looked for its container before the page had
rendered it, so the button did nothing.

## If it does not run here

The page tells you what failed and gives you the command that always works:

```bash theme={null}
pip install ctrlrun && ctrlrun demo
```

A browser can fail this for reasons that have nothing to do with CTRLRun: a blocked CDN, an
extension that stops WebAssembly, a very old browser, or a tab with no memory to spare. That is
this page failing, not the library.

## Next

* [60-second quickstart](/get-started/quickstart): protect one function for real.
* [Outcomes and AMBIGUOUS](/concepts/outcomes-and-ambiguous): what scenario 1 means.
* [Get started](/get-started/install) · [Why](/why).


## Related topics

- [CTRLRun](/index.md)
- [OpenAI Agents SDK tool approval](/cookbook/openai-agents-tool-approval.md)
- [Authority and delegation](/authority.md)
- [An IAM agent that can grant read but never admin](/cookbook/iam-agent.md)
- [Use the docs from your editor](/mcp/use-the-docs-from-your-editor.md)
