Skip to main content
Decorate the function that acts, name the action and the consequence, and put the call inside a context that names who is acting. From then on every call is decided, reserved, executed and recorded, and a refusal is an exception raised before the function body runs. Prerequisites: pip install ctrlrun, Python 3.11 or later, an empty directory. Every block below runs offline; the deploy tool is a stand-in that records calls.
1

Write the policy

Three actions across two domains: a rollout restart that is cheap to undo, a namespace delete that needs a human, and a role grant that is autonomous for readers and needs a human for anything else.
runnable
2

Decorate the functions

effect= is a template over the function’s own parameters. It names the consequence, so the same delete proposed twice, by a retry or by a second worker, is one effect.
runnable file=infra.py
Run it with python infra.py:
Two calls reached the stand-in: the restart and the reader grant. The delete and the admin grant are waiting for a person, and the second restart was refused because restart:prod-eu:checkout had already committed.
3

Read what happened

runnable
Every call that reaches a decision has a receipt, denied ones included, and every effect key has a state. A call waiting on a human has an approval request and no receipt yet. The pending approvals are what ctrlrun approve <request id> answers; the quickstart walks through presenting one.

The decorator’s arguments

Template syntax is checked at decoration time, so a typo fails at import rather than mid-run. A protected function may not take *args or **kwargs, and may not name a parameter after a reserved subject.

If it didn’t work

  • ActionDenied: ... no principal is available: the call is outside ctrlrun.context(...).
  • ActionDenied: ... unknown_action: the decorator’s name is not a key under actions:.
  • EffectKeyError: ... {cluster}: the template names a parameter the function does not have, or the argument was None.
  • PolicyError: ... could not be read: no ctrlrun.yaml in the working directory and $CTRLRUN_CONFIG is unset.

Next