From 493b5d6ec73c444617c4100058d21acfc82f30e1 Mon Sep 17 00:00:00 2001 From: marsojuji-cmyk Date: Sat, 3 Oct 2026 18:29:52 -0600 Subject: [PATCH] docs: repair executable first-lease quickstart --- docs/guides/quickstart.md | 63 ++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/docs/guides/quickstart.md b/docs/guides/quickstart.md index f333919..c15fb2d 100644 --- a/docs/guides/quickstart.md +++ b/docs/guides/quickstart.md @@ -1,12 +1,35 @@ -# Quickstart: your first lease in five minutes +# Quickstart: your first lease -This is the fastest path from zero to a live lease, a refused permit, and -an auditable ledger. +This is the shortest path from zero to a live lease, a refused permit, and an +auditable ledger. The examples exercise Interlock's admission gate; they do +not sandbox code outside the gate or stop an action that is already running. + +## 0. Install in a supported environment + +Interlock requires Python >=3.11 and its build backend requires +`setuptools>=68`. From a scratch directory, clone the repository, create a +local virtual environment, and install the checked-out package: + +```bash +git clone https://github.com/marsojuji-cmyk/interlock.git +cd interlock +python3.11 -m venv .venv +. .venv/bin/activate +python -m pip install --upgrade "setuptools>=68" +python -m pip install . +``` + +Use the supported interpreter consistently: every Python example below is +intended to run as `python` from this activated environment. No pytest +installation is needed for this guide. ## 1. Create the interlock ```python +from pathlib import Path + from interlock import Interlock +from interlock._errors import EStopEngaged, FaultActive, LeaseExpired, LeaseRevoked ilk = Interlock() ``` @@ -20,8 +43,9 @@ You now hold four components: `.leases` (the lease manager), `.estop` lease = ilk.grant(resource="docs:write", holder="demo-agent", ttl_seconds=300) ``` -Authority now exists, and it expires in 300 seconds. Nothing else in the -system can act on `docs:write` without this lease. +Authority now exists, and this example lease is short-lived. Nothing else in +the system can pass the gate for `docs:write` without this lease. Admission +checks the e-stop, lease state, and resource faults at the time of entry. ## 3. Exercise it through the gate @@ -30,26 +54,45 @@ with ilk.permit(lease, action="write", target="notes.md"): Path("notes.md").write_text("hello") ``` -The gate checks, in order: e-stop clear, lease live, no gating fault on +The gate checks, in order: e-stop clear, lease live, and no gating fault on `docs:write`. If any check fails, the context manager raises instead of yielding — `EStopEngaged`, `LeaseExpired`/`LeaseRevoked`, or `FaultActive`. +The check controls admission to the context; it is not an OS sandbox and it +cannot interrupt an action that has already started. ## 4. Watch a fault refuse ```python -ilk.faults.report(code="DISK_FULL", severity="major", resource="docs:write") +fault = ilk.faults.report( + code="DISK_FULL", severity="major", resource="docs:write" +) + +try: + with ilk.permit(lease, action="write", target="notes.md"): + Path("notes.md").write_text("should not be admitted") +except FaultActive: + pass +else: + raise AssertionError("the active fault must refuse admission") +assert fault.id is not None +ilk.faults.clear(fault.id) with ilk.permit(lease, action="write", target="notes.md"): - ... # raises FaultActive — the refusal is appended to the ledger + Path("notes.md").write_text("resumed") ``` -Clear the fault to resume: `ilk.faults.clear(fault.id)`. +The refused entry is appended to the ledger. Clearing the fault permits a +new admission; it does not retroactively authorize the refused action. ## 5. Halt everything, then release ```python ilk.estop.engage("taking a look") -# every permit now raises EStopEngaged, on every resource +try: + with ilk.permit(lease, action="write", target="notes.md"): + raise AssertionError("e-stop must refuse admission") +except EStopEngaged: + pass ilk.estop.release() # deliberate, never automatic ```