pywrit 0.2.3__tar.gz → 0.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pywrit-0.2.4/PKG-INFO ADDED
@@ -0,0 +1,196 @@
1
+ Metadata-Version: 2.4
2
+ Name: pywrit
3
+ Version: 0.2.4
4
+ Summary: Python client + CLI for Writ — the gate before the write
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/AvenueDAdmin/pywrit
7
+ Project-URL: Documentation, https://withwrit.com/docs
8
+ Keywords: ai-agents,policy,audit-log,guardrails
9
+ Requires-Python: >=3.9
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: httpx>=0.27
13
+ Dynamic: license-file
14
+
15
+ # pywrit
16
+
17
+ **Find the dangerous writes in your Python agent's code, then gate it.** `pywrit` is the Python
18
+ client and `writ` CLI for [Writ](https://withwrit.com): an allow/deny gate that sits
19
+ in front of your agent's consequential writes (database, HTTP, files, email, queues,
20
+ AWS) and records a hash-chained receipt for every decision.
21
+
22
+ ![writ scan finds 4 write sites in a Python agent (0/4 gated); writ scan --apply inserts gates; a re-scan shows 4/4 gated](https://raw.githubusercontent.com/AvenueDAdmin/pywrit/main/docs/assets/scan-python.gif)
23
+
24
+ `writ scan` is **local, deterministic, and free**: it parses your code with Python's
25
+ `ast` module, makes no network calls, and needs no API key.
26
+
27
+ ## Install
28
+
29
+ ```bash
30
+ pip install pywrit
31
+ ```
32
+
33
+ Requires Python 3.9+. Installs the `writ` command and the `pywrit` Python client.
34
+
35
+ ## 60-second quickstart: scan → apply → gate
36
+
37
+ **1. Scan.** See which functions write, and how many of them are gated.
38
+
39
+ ```bash
40
+ writ scan .
41
+ ```
42
+
43
+ ```text
44
+ writ scan: /path/to/support-agent
45
+ files scanned: 4 skipped: 0
46
+ write sites: 4 in 3 function(s)
47
+ gated: 0/4 (0%)
48
+ verbs discovered: 3
49
+ crm.update
50
+ payments.refund
51
+ tickets.close
52
+ ```
53
+
54
+ It also prints a **risk report** (0-100, weighted by risk tier), writes the discovered
55
+ verbs to `writ-policy.json`, and shows the instrumentation it would add as a unified
56
+ diff. Nothing in your code changes yet. `writ scan . --score` prints just the risk report.
57
+
58
+ **2. Apply.** Insert a gate at the top of each writing function.
59
+
60
+ ```bash
61
+ writ scan . --apply # shows the diff, then asks before writing
62
+ writ scan . --apply --yes # no prompt (e.g. in CI)
63
+ ```
64
+
65
+ Each gated function now asks Writ before it writes, and fails closed:
66
+
67
+ ```python
68
+ def issue_refund(charge_id, amount_cents):
69
+ if _writ_check("payments.refund") != "ALLOW":
70
+ raise PermissionError("writ denied payments.refund")
71
+ ...
72
+ ```
73
+
74
+ Re-run `writ scan .` and you'll see `gated: 4/4 (100%)`.
75
+
76
+ **3. Gate.** Get a free API key, load the discovered policy, and run your agent.
77
+
78
+ ```bash
79
+ writ key --email you@example.com # free API key + tenant
80
+ export WRIT_API_KEY=writ_... # read by the inserted gate
81
+ export WRIT_SPONSOR=acme WRIT_AGENT=support-agent # optional: who is acting
82
+ writ scan . --push-policy --key "$WRIT_API_KEY" # upload the discovered verb policy
83
+ ```
84
+
85
+ Every gated write now gets `ALLOW`, `DENY`, or `STEP_UP` (a human sponsor must
86
+ approve), and every decision is written to your tenant's tamper-evident audit log:
87
+
88
+ ```bash
89
+ writ receipts --key "$WRIT_API_KEY" # latest receipts
90
+ writ verify-chain --key "$WRIT_API_KEY" # verify the receipt hash chain
91
+ writ stream --key "$WRIT_API_KEY" # tail decisions live
92
+ writ report --key "$WRIT_API_KEY" # Agent Action Report
93
+ ```
94
+
95
+ ## What `writ scan` detects
96
+
97
+ Python (`.py`) files, parsed with the stdlib `ast` (no extra dependencies):
98
+
99
+ | Category | Examples |
100
+ |---|---|
101
+ | Database | `cursor.execute(...)` / `executemany` / `executescript` (write SQL only; `SELECT`/`WITH`/... skipped), `session.add` / `commit` / `delete` / `merge` / `flush` |
102
+ | HTTP | `requests.post` / `put` / `patch`, `client.delete(...)`, `session.request(...)` |
103
+ | Files | `open(..., "w"/"a"/"x"/"+")`, `Path.write_text` / `write_bytes` / `unlink` / `rename`, `os.remove` / `rename` / `makedirs`, `shutil.rmtree` / `move` / `copy` |
104
+ | Email | `sendmail`, `send_message`, `send_email` |
105
+ | Queues | `publish`, `produce`, `enqueue`, `queue.send(...)` |
106
+ | AWS SDK | `put_object`, `put_item`, `delete_item`, `upload_file`, `send_message`, `start_execution`, ... |
107
+
108
+ - Each write is mapped to a verb such as `payments.refund` or `crm.update`, inferred
109
+ from the file path and function name.
110
+ - A function that already calls `writ_check(...)` / `_writ_check(...)` counts as gated.
111
+ - Skipped: tests, hidden directories, virtualenvs, `node_modules`, `dist`, `build`.
112
+ Use `--exclude SUBSTR` (repeatable) to skip more.
113
+ - Not covered (review by hand): writes behind dynamically built SQL, third-party SDK
114
+ calls such as `stripe.Refund.create(...)`, deferred task queues, or shared clients
115
+ several layers down. The risk report lists these gaps every time.
116
+
117
+ ### TypeScript / JavaScript: coming soon (not yet released)
118
+
119
+ > **Not available in `pywrit` 0.2.2 or on PyPI yet.** The current release scans
120
+ > Python only. The preview below shows scan-only TS/JS support that is still in
121
+ > development; the command it uses is not available yet.
122
+
123
+ ![Preview, not yet released: writ scan on a TypeScript agent finds 5 write sites, 1 of 5 gated](https://raw.githubusercontent.com/AvenueDAdmin/pywrit/main/docs/assets/scan-tsjs.gif)
124
+
125
+ ## Python client
126
+
127
+ ```python
128
+ from pywrit import WritClient
129
+
130
+ client = WritClient(api_key="writ_...")
131
+
132
+ result = client.check(
133
+ sponsor_id="acme",
134
+ agent_id="agent-7",
135
+ verb="db.write",
136
+ target="prod.customers",
137
+ purpose="backfill region field",
138
+ )
139
+
140
+ if result.decision == "ALLOW":
141
+ # result.auth_token is a short-lived token bound to this exact write
142
+ perform_write(...)
143
+ elif result.decision == "STEP_UP":
144
+ # a human sponsor must approve first: client.grant(...), then re-check
145
+ ...
146
+ else:
147
+ # DENY
148
+ ...
149
+ ```
150
+
151
+ No API key yet? Try the keyless sandbox:
152
+
153
+ ```python
154
+ client = WritClient()
155
+ client.sandbox({
156
+ "sponsorId": "acme",
157
+ "agentId": "agent-7",
158
+ "verb": "demo_write", # sandbox only allows demo_write ...
159
+ "target": "demo-customers", # ... on targets starting with demo-
160
+ "purpose": "trying the gate",
161
+ })
162
+ ```
163
+
164
+ What's covered:
165
+
166
+ - `check(...)`: the gate. `ALLOW` / `DENY` / `STEP_UP`, plus a receipt every time
167
+ - `verify_token(...)`: validate an `ALLOW` auth token (catches purpose drift)
168
+ - `grant(...)`: human-sponsor approval for the `STEP_UP` path
169
+ - `get_policy()` / `set_policy(...)`: manage the tenant policy
170
+ - `revoke(...)` / `reinstate(...)` / `revoked()`: the kill switch
171
+ - `receipts()` / `receipt(id)` / `verify_chain()` / `stream_receipts()`: the audit log
172
+ - `sandbox(...)`: keyless trial, no API key required
173
+
174
+ ## CLI reference
175
+
176
+ ```bash
177
+ writ check --key writ_... --sponsor acme --agent agent-7 \
178
+ --verb db.write --target prod.customers --purpose "backfill region field"
179
+ writ policy --key writ_... --set payments.refund require_grant
180
+ writ revoke --sponsor acme --agent agent-7 --reason "runaway loop" # kill switch (sponsor token)
181
+ writ grant --sponsor acme --agent agent-7 --verb payments.refund \
182
+ --target ch_123 --purpose "approved refund" # STEP_UP approval (sponsor token)
183
+ ```
184
+
185
+ Sponsor-token commands (`revoke`, `reinstate`, `revoked`, `grant`) read
186
+ `--sponsor-token` or `WRIT_SPONSOR_TOKEN`. Run `writ --help` for the full command list.
187
+
188
+ ## Docs
189
+
190
+ Full docs: [docs.withwrit.com](https://docs.withwrit.com) ·
191
+ Quickstart: [docs.withwrit.com/quickstart](https://docs.withwrit.com/quickstart) ·
192
+ Site: [withwrit.com](https://withwrit.com)
193
+
194
+ ## License
195
+
196
+ MIT
pywrit-0.2.4/README.md ADDED
@@ -0,0 +1,182 @@
1
+ # pywrit
2
+
3
+ **Find the dangerous writes in your Python agent's code, then gate it.** `pywrit` is the Python
4
+ client and `writ` CLI for [Writ](https://withwrit.com): an allow/deny gate that sits
5
+ in front of your agent's consequential writes (database, HTTP, files, email, queues,
6
+ AWS) and records a hash-chained receipt for every decision.
7
+
8
+ ![writ scan finds 4 write sites in a Python agent (0/4 gated); writ scan --apply inserts gates; a re-scan shows 4/4 gated](https://raw.githubusercontent.com/AvenueDAdmin/pywrit/main/docs/assets/scan-python.gif)
9
+
10
+ `writ scan` is **local, deterministic, and free**: it parses your code with Python's
11
+ `ast` module, makes no network calls, and needs no API key.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ pip install pywrit
17
+ ```
18
+
19
+ Requires Python 3.9+. Installs the `writ` command and the `pywrit` Python client.
20
+
21
+ ## 60-second quickstart: scan → apply → gate
22
+
23
+ **1. Scan.** See which functions write, and how many of them are gated.
24
+
25
+ ```bash
26
+ writ scan .
27
+ ```
28
+
29
+ ```text
30
+ writ scan: /path/to/support-agent
31
+ files scanned: 4 skipped: 0
32
+ write sites: 4 in 3 function(s)
33
+ gated: 0/4 (0%)
34
+ verbs discovered: 3
35
+ crm.update
36
+ payments.refund
37
+ tickets.close
38
+ ```
39
+
40
+ It also prints a **risk report** (0-100, weighted by risk tier), writes the discovered
41
+ verbs to `writ-policy.json`, and shows the instrumentation it would add as a unified
42
+ diff. Nothing in your code changes yet. `writ scan . --score` prints just the risk report.
43
+
44
+ **2. Apply.** Insert a gate at the top of each writing function.
45
+
46
+ ```bash
47
+ writ scan . --apply # shows the diff, then asks before writing
48
+ writ scan . --apply --yes # no prompt (e.g. in CI)
49
+ ```
50
+
51
+ Each gated function now asks Writ before it writes, and fails closed:
52
+
53
+ ```python
54
+ def issue_refund(charge_id, amount_cents):
55
+ if _writ_check("payments.refund") != "ALLOW":
56
+ raise PermissionError("writ denied payments.refund")
57
+ ...
58
+ ```
59
+
60
+ Re-run `writ scan .` and you'll see `gated: 4/4 (100%)`.
61
+
62
+ **3. Gate.** Get a free API key, load the discovered policy, and run your agent.
63
+
64
+ ```bash
65
+ writ key --email you@example.com # free API key + tenant
66
+ export WRIT_API_KEY=writ_... # read by the inserted gate
67
+ export WRIT_SPONSOR=acme WRIT_AGENT=support-agent # optional: who is acting
68
+ writ scan . --push-policy --key "$WRIT_API_KEY" # upload the discovered verb policy
69
+ ```
70
+
71
+ Every gated write now gets `ALLOW`, `DENY`, or `STEP_UP` (a human sponsor must
72
+ approve), and every decision is written to your tenant's tamper-evident audit log:
73
+
74
+ ```bash
75
+ writ receipts --key "$WRIT_API_KEY" # latest receipts
76
+ writ verify-chain --key "$WRIT_API_KEY" # verify the receipt hash chain
77
+ writ stream --key "$WRIT_API_KEY" # tail decisions live
78
+ writ report --key "$WRIT_API_KEY" # Agent Action Report
79
+ ```
80
+
81
+ ## What `writ scan` detects
82
+
83
+ Python (`.py`) files, parsed with the stdlib `ast` (no extra dependencies):
84
+
85
+ | Category | Examples |
86
+ |---|---|
87
+ | Database | `cursor.execute(...)` / `executemany` / `executescript` (write SQL only; `SELECT`/`WITH`/... skipped), `session.add` / `commit` / `delete` / `merge` / `flush` |
88
+ | HTTP | `requests.post` / `put` / `patch`, `client.delete(...)`, `session.request(...)` |
89
+ | Files | `open(..., "w"/"a"/"x"/"+")`, `Path.write_text` / `write_bytes` / `unlink` / `rename`, `os.remove` / `rename` / `makedirs`, `shutil.rmtree` / `move` / `copy` |
90
+ | Email | `sendmail`, `send_message`, `send_email` |
91
+ | Queues | `publish`, `produce`, `enqueue`, `queue.send(...)` |
92
+ | AWS SDK | `put_object`, `put_item`, `delete_item`, `upload_file`, `send_message`, `start_execution`, ... |
93
+
94
+ - Each write is mapped to a verb such as `payments.refund` or `crm.update`, inferred
95
+ from the file path and function name.
96
+ - A function that already calls `writ_check(...)` / `_writ_check(...)` counts as gated.
97
+ - Skipped: tests, hidden directories, virtualenvs, `node_modules`, `dist`, `build`.
98
+ Use `--exclude SUBSTR` (repeatable) to skip more.
99
+ - Not covered (review by hand): writes behind dynamically built SQL, third-party SDK
100
+ calls such as `stripe.Refund.create(...)`, deferred task queues, or shared clients
101
+ several layers down. The risk report lists these gaps every time.
102
+
103
+ ### TypeScript / JavaScript: coming soon (not yet released)
104
+
105
+ > **Not available in `pywrit` 0.2.2 or on PyPI yet.** The current release scans
106
+ > Python only. The preview below shows scan-only TS/JS support that is still in
107
+ > development; the command it uses is not available yet.
108
+
109
+ ![Preview, not yet released: writ scan on a TypeScript agent finds 5 write sites, 1 of 5 gated](https://raw.githubusercontent.com/AvenueDAdmin/pywrit/main/docs/assets/scan-tsjs.gif)
110
+
111
+ ## Python client
112
+
113
+ ```python
114
+ from pywrit import WritClient
115
+
116
+ client = WritClient(api_key="writ_...")
117
+
118
+ result = client.check(
119
+ sponsor_id="acme",
120
+ agent_id="agent-7",
121
+ verb="db.write",
122
+ target="prod.customers",
123
+ purpose="backfill region field",
124
+ )
125
+
126
+ if result.decision == "ALLOW":
127
+ # result.auth_token is a short-lived token bound to this exact write
128
+ perform_write(...)
129
+ elif result.decision == "STEP_UP":
130
+ # a human sponsor must approve first: client.grant(...), then re-check
131
+ ...
132
+ else:
133
+ # DENY
134
+ ...
135
+ ```
136
+
137
+ No API key yet? Try the keyless sandbox:
138
+
139
+ ```python
140
+ client = WritClient()
141
+ client.sandbox({
142
+ "sponsorId": "acme",
143
+ "agentId": "agent-7",
144
+ "verb": "demo_write", # sandbox only allows demo_write ...
145
+ "target": "demo-customers", # ... on targets starting with demo-
146
+ "purpose": "trying the gate",
147
+ })
148
+ ```
149
+
150
+ What's covered:
151
+
152
+ - `check(...)`: the gate. `ALLOW` / `DENY` / `STEP_UP`, plus a receipt every time
153
+ - `verify_token(...)`: validate an `ALLOW` auth token (catches purpose drift)
154
+ - `grant(...)`: human-sponsor approval for the `STEP_UP` path
155
+ - `get_policy()` / `set_policy(...)`: manage the tenant policy
156
+ - `revoke(...)` / `reinstate(...)` / `revoked()`: the kill switch
157
+ - `receipts()` / `receipt(id)` / `verify_chain()` / `stream_receipts()`: the audit log
158
+ - `sandbox(...)`: keyless trial, no API key required
159
+
160
+ ## CLI reference
161
+
162
+ ```bash
163
+ writ check --key writ_... --sponsor acme --agent agent-7 \
164
+ --verb db.write --target prod.customers --purpose "backfill region field"
165
+ writ policy --key writ_... --set payments.refund require_grant
166
+ writ revoke --sponsor acme --agent agent-7 --reason "runaway loop" # kill switch (sponsor token)
167
+ writ grant --sponsor acme --agent agent-7 --verb payments.refund \
168
+ --target ch_123 --purpose "approved refund" # STEP_UP approval (sponsor token)
169
+ ```
170
+
171
+ Sponsor-token commands (`revoke`, `reinstate`, `revoked`, `grant`) read
172
+ `--sponsor-token` or `WRIT_SPONSOR_TOKEN`. Run `writ --help` for the full command list.
173
+
174
+ ## Docs
175
+
176
+ Full docs: [docs.withwrit.com](https://docs.withwrit.com) ·
177
+ Quickstart: [docs.withwrit.com/quickstart](https://docs.withwrit.com/quickstart) ·
178
+ Site: [withwrit.com](https://withwrit.com)
179
+
180
+ ## License
181
+
182
+ MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pywrit"
7
- version = "0.2.3"
7
+ version = "0.2.4"
8
8
  description = "Python client + CLI for Writ — the gate before the write"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -27,7 +27,7 @@ from .errors import (
27
27
  WritError,
28
28
  )
29
29
 
30
- __version__ = "0.2.3"
30
+ __version__ = "0.2.4"
31
31
 
32
32
  __all__ = [
33
33
  "WritClient",
@@ -13,7 +13,7 @@ from datetime import datetime, timezone
13
13
 
14
14
  BASE = os.environ.get(
15
15
  "WRIT_BASE",
16
- "https://j72ckh66ukck2kcbq3oiwxaalm0olxwb.lambda-url.us-east-1.on.aws",
16
+ "https://api.withwrit.com",
17
17
  )
18
18
 
19
19
 
@@ -99,7 +99,7 @@ import urllib.request as _writ_urllib
99
99
 
100
100
  _WRIT_BASE = _writ_os.environ.get(
101
101
  "WRIT_BASE",
102
- "https://j72ckh66ukck2kcbq3oiwxaalm0olxwb.lambda-url.us-east-1.on.aws",
102
+ "https://api.withwrit.com",
103
103
  )
104
104
  _WRIT_KEY = _writ_os.environ.get("WRIT_API_KEY", "")
105
105
  _WRIT_SPONSOR = _writ_os.environ.get("WRIT_SPONSOR", "app")
@@ -15,7 +15,7 @@ from .errors import (
15
15
  WritError,
16
16
  )
17
17
 
18
- DEFAULT_BASE_URL = "https://j72ckh66ukck2kcbq3oiwxaalm0olxwb.lambda-url.us-east-1.on.aws"
18
+ DEFAULT_BASE_URL = "https://api.withwrit.com"
19
19
 
20
20
 
21
21
  @dataclass
@@ -0,0 +1,196 @@
1
+ Metadata-Version: 2.4
2
+ Name: pywrit
3
+ Version: 0.2.4
4
+ Summary: Python client + CLI for Writ — the gate before the write
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/AvenueDAdmin/pywrit
7
+ Project-URL: Documentation, https://withwrit.com/docs
8
+ Keywords: ai-agents,policy,audit-log,guardrails
9
+ Requires-Python: >=3.9
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: httpx>=0.27
13
+ Dynamic: license-file
14
+
15
+ # pywrit
16
+
17
+ **Find the dangerous writes in your Python agent's code, then gate it.** `pywrit` is the Python
18
+ client and `writ` CLI for [Writ](https://withwrit.com): an allow/deny gate that sits
19
+ in front of your agent's consequential writes (database, HTTP, files, email, queues,
20
+ AWS) and records a hash-chained receipt for every decision.
21
+
22
+ ![writ scan finds 4 write sites in a Python agent (0/4 gated); writ scan --apply inserts gates; a re-scan shows 4/4 gated](https://raw.githubusercontent.com/AvenueDAdmin/pywrit/main/docs/assets/scan-python.gif)
23
+
24
+ `writ scan` is **local, deterministic, and free**: it parses your code with Python's
25
+ `ast` module, makes no network calls, and needs no API key.
26
+
27
+ ## Install
28
+
29
+ ```bash
30
+ pip install pywrit
31
+ ```
32
+
33
+ Requires Python 3.9+. Installs the `writ` command and the `pywrit` Python client.
34
+
35
+ ## 60-second quickstart: scan → apply → gate
36
+
37
+ **1. Scan.** See which functions write, and how many of them are gated.
38
+
39
+ ```bash
40
+ writ scan .
41
+ ```
42
+
43
+ ```text
44
+ writ scan: /path/to/support-agent
45
+ files scanned: 4 skipped: 0
46
+ write sites: 4 in 3 function(s)
47
+ gated: 0/4 (0%)
48
+ verbs discovered: 3
49
+ crm.update
50
+ payments.refund
51
+ tickets.close
52
+ ```
53
+
54
+ It also prints a **risk report** (0-100, weighted by risk tier), writes the discovered
55
+ verbs to `writ-policy.json`, and shows the instrumentation it would add as a unified
56
+ diff. Nothing in your code changes yet. `writ scan . --score` prints just the risk report.
57
+
58
+ **2. Apply.** Insert a gate at the top of each writing function.
59
+
60
+ ```bash
61
+ writ scan . --apply # shows the diff, then asks before writing
62
+ writ scan . --apply --yes # no prompt (e.g. in CI)
63
+ ```
64
+
65
+ Each gated function now asks Writ before it writes, and fails closed:
66
+
67
+ ```python
68
+ def issue_refund(charge_id, amount_cents):
69
+ if _writ_check("payments.refund") != "ALLOW":
70
+ raise PermissionError("writ denied payments.refund")
71
+ ...
72
+ ```
73
+
74
+ Re-run `writ scan .` and you'll see `gated: 4/4 (100%)`.
75
+
76
+ **3. Gate.** Get a free API key, load the discovered policy, and run your agent.
77
+
78
+ ```bash
79
+ writ key --email you@example.com # free API key + tenant
80
+ export WRIT_API_KEY=writ_... # read by the inserted gate
81
+ export WRIT_SPONSOR=acme WRIT_AGENT=support-agent # optional: who is acting
82
+ writ scan . --push-policy --key "$WRIT_API_KEY" # upload the discovered verb policy
83
+ ```
84
+
85
+ Every gated write now gets `ALLOW`, `DENY`, or `STEP_UP` (a human sponsor must
86
+ approve), and every decision is written to your tenant's tamper-evident audit log:
87
+
88
+ ```bash
89
+ writ receipts --key "$WRIT_API_KEY" # latest receipts
90
+ writ verify-chain --key "$WRIT_API_KEY" # verify the receipt hash chain
91
+ writ stream --key "$WRIT_API_KEY" # tail decisions live
92
+ writ report --key "$WRIT_API_KEY" # Agent Action Report
93
+ ```
94
+
95
+ ## What `writ scan` detects
96
+
97
+ Python (`.py`) files, parsed with the stdlib `ast` (no extra dependencies):
98
+
99
+ | Category | Examples |
100
+ |---|---|
101
+ | Database | `cursor.execute(...)` / `executemany` / `executescript` (write SQL only; `SELECT`/`WITH`/... skipped), `session.add` / `commit` / `delete` / `merge` / `flush` |
102
+ | HTTP | `requests.post` / `put` / `patch`, `client.delete(...)`, `session.request(...)` |
103
+ | Files | `open(..., "w"/"a"/"x"/"+")`, `Path.write_text` / `write_bytes` / `unlink` / `rename`, `os.remove` / `rename` / `makedirs`, `shutil.rmtree` / `move` / `copy` |
104
+ | Email | `sendmail`, `send_message`, `send_email` |
105
+ | Queues | `publish`, `produce`, `enqueue`, `queue.send(...)` |
106
+ | AWS SDK | `put_object`, `put_item`, `delete_item`, `upload_file`, `send_message`, `start_execution`, ... |
107
+
108
+ - Each write is mapped to a verb such as `payments.refund` or `crm.update`, inferred
109
+ from the file path and function name.
110
+ - A function that already calls `writ_check(...)` / `_writ_check(...)` counts as gated.
111
+ - Skipped: tests, hidden directories, virtualenvs, `node_modules`, `dist`, `build`.
112
+ Use `--exclude SUBSTR` (repeatable) to skip more.
113
+ - Not covered (review by hand): writes behind dynamically built SQL, third-party SDK
114
+ calls such as `stripe.Refund.create(...)`, deferred task queues, or shared clients
115
+ several layers down. The risk report lists these gaps every time.
116
+
117
+ ### TypeScript / JavaScript: coming soon (not yet released)
118
+
119
+ > **Not available in `pywrit` 0.2.2 or on PyPI yet.** The current release scans
120
+ > Python only. The preview below shows scan-only TS/JS support that is still in
121
+ > development; the command it uses is not available yet.
122
+
123
+ ![Preview, not yet released: writ scan on a TypeScript agent finds 5 write sites, 1 of 5 gated](https://raw.githubusercontent.com/AvenueDAdmin/pywrit/main/docs/assets/scan-tsjs.gif)
124
+
125
+ ## Python client
126
+
127
+ ```python
128
+ from pywrit import WritClient
129
+
130
+ client = WritClient(api_key="writ_...")
131
+
132
+ result = client.check(
133
+ sponsor_id="acme",
134
+ agent_id="agent-7",
135
+ verb="db.write",
136
+ target="prod.customers",
137
+ purpose="backfill region field",
138
+ )
139
+
140
+ if result.decision == "ALLOW":
141
+ # result.auth_token is a short-lived token bound to this exact write
142
+ perform_write(...)
143
+ elif result.decision == "STEP_UP":
144
+ # a human sponsor must approve first: client.grant(...), then re-check
145
+ ...
146
+ else:
147
+ # DENY
148
+ ...
149
+ ```
150
+
151
+ No API key yet? Try the keyless sandbox:
152
+
153
+ ```python
154
+ client = WritClient()
155
+ client.sandbox({
156
+ "sponsorId": "acme",
157
+ "agentId": "agent-7",
158
+ "verb": "demo_write", # sandbox only allows demo_write ...
159
+ "target": "demo-customers", # ... on targets starting with demo-
160
+ "purpose": "trying the gate",
161
+ })
162
+ ```
163
+
164
+ What's covered:
165
+
166
+ - `check(...)`: the gate. `ALLOW` / `DENY` / `STEP_UP`, plus a receipt every time
167
+ - `verify_token(...)`: validate an `ALLOW` auth token (catches purpose drift)
168
+ - `grant(...)`: human-sponsor approval for the `STEP_UP` path
169
+ - `get_policy()` / `set_policy(...)`: manage the tenant policy
170
+ - `revoke(...)` / `reinstate(...)` / `revoked()`: the kill switch
171
+ - `receipts()` / `receipt(id)` / `verify_chain()` / `stream_receipts()`: the audit log
172
+ - `sandbox(...)`: keyless trial, no API key required
173
+
174
+ ## CLI reference
175
+
176
+ ```bash
177
+ writ check --key writ_... --sponsor acme --agent agent-7 \
178
+ --verb db.write --target prod.customers --purpose "backfill region field"
179
+ writ policy --key writ_... --set payments.refund require_grant
180
+ writ revoke --sponsor acme --agent agent-7 --reason "runaway loop" # kill switch (sponsor token)
181
+ writ grant --sponsor acme --agent agent-7 --verb payments.refund \
182
+ --target ch_123 --purpose "approved refund" # STEP_UP approval (sponsor token)
183
+ ```
184
+
185
+ Sponsor-token commands (`revoke`, `reinstate`, `revoked`, `grant`) read
186
+ `--sponsor-token` or `WRIT_SPONSOR_TOKEN`. Run `writ --help` for the full command list.
187
+
188
+ ## Docs
189
+
190
+ Full docs: [docs.withwrit.com](https://docs.withwrit.com) ·
191
+ Quickstart: [docs.withwrit.com/quickstart](https://docs.withwrit.com/quickstart) ·
192
+ Site: [withwrit.com](https://withwrit.com)
193
+
194
+ ## License
195
+
196
+ MIT
pywrit-0.2.3/PKG-INFO DELETED
@@ -1,97 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: pywrit
3
- Version: 0.2.3
4
- Summary: Python client + CLI for Writ — the gate before the write
5
- License: MIT
6
- Project-URL: Homepage, https://github.com/AvenueDAdmin/pywrit
7
- Project-URL: Documentation, https://withwrit.com/docs
8
- Keywords: ai-agents,policy,audit-log,guardrails
9
- Requires-Python: >=3.9
10
- Description-Content-Type: text/markdown
11
- License-File: LICENSE
12
- Requires-Dist: httpx>=0.27
13
- Dynamic: license-file
14
-
15
- # pywrit
16
-
17
- Python client + CLI for [Writ](https://withwrit.com) — the gate before the write.
18
- Put a policy gate in front of your agent's dangerous actions.
19
-
20
- ## Install
21
-
22
- ```bash
23
- pip install pywrit
24
- ```
25
-
26
- ## Python client
27
-
28
- ```python
29
- from pywrit import WritClient
30
-
31
- client = WritClient(api_key="writ_...")
32
-
33
- result = client.check(
34
- sponsor_id="acme",
35
- agent_id="agent-7",
36
- verb="db.write",
37
- target="prod.customers",
38
- purpose="backfill region field",
39
- )
40
-
41
- if result.decision == "ALLOW":
42
- # result.auth_token is a short-lived token bound to this exact write
43
- perform_write(...)
44
- elif result.decision == "STEP_UP":
45
- # a human sponsor must approve first: client.grant(...), then re-check
46
- ...
47
- else:
48
- # DENY
49
- ...
50
- ```
51
-
52
- No API key yet? Try the keyless sandbox:
53
-
54
- ```python
55
- client = WritClient()
56
- client.sandbox({
57
- "sponsorId": "acme",
58
- "agentId": "agent-7",
59
- "verb": "demo_write", # sandbox only allows demo_write ...
60
- "target": "demo-customers", # ... on targets starting with demo-
61
- "purpose": "trying the gate",
62
- })
63
- ```
64
-
65
- ### What's covered
66
-
67
- - `check(...)` — the gate: `ALLOW` / `DENY` / `STEP_UP`, plus a receipt every time
68
- - `verify_token(...)` — validate an `ALLOW` auth token (catches purpose drift)
69
- - `grant(...)` — human-sponsor approval for the `STEP_UP` path
70
- - `get_policy()` / `set_policy(...)` — manage the tenant policy
71
- - `revoke(...)` / `reinstate(...)` / `revoked()` — the kill switch
72
- - `receipts()` / `receipt(id)` / `verify_chain()` / `stream_receipts()` — the audit log
73
- - `sandbox(...)` — keyless trial, no API key required
74
-
75
- Every decision writes a receipt, so the audit log is the meter.
76
-
77
- ## CLI
78
-
79
- The same package ships the `writ` command:
80
-
81
- ```bash
82
- writ check --key writ_... --sponsor acme --agent agent-7 \
83
- --verb db.write --target prod.customers --purpose "backfill region field"
84
- writ scan ./my-repo
85
- writ receipts --key writ_...
86
- writ verify-chain --key writ_...
87
- ```
88
-
89
- Run `writ --help` for the full command list.
90
-
91
- ## Docs
92
-
93
- Full API reference: [withwrit.com/docs](https://withwrit.com/docs)
94
-
95
- ## License
96
-
97
- MIT
pywrit-0.2.3/README.md DELETED
@@ -1,83 +0,0 @@
1
- # pywrit
2
-
3
- Python client + CLI for [Writ](https://withwrit.com) — the gate before the write.
4
- Put a policy gate in front of your agent's dangerous actions.
5
-
6
- ## Install
7
-
8
- ```bash
9
- pip install pywrit
10
- ```
11
-
12
- ## Python client
13
-
14
- ```python
15
- from pywrit import WritClient
16
-
17
- client = WritClient(api_key="writ_...")
18
-
19
- result = client.check(
20
- sponsor_id="acme",
21
- agent_id="agent-7",
22
- verb="db.write",
23
- target="prod.customers",
24
- purpose="backfill region field",
25
- )
26
-
27
- if result.decision == "ALLOW":
28
- # result.auth_token is a short-lived token bound to this exact write
29
- perform_write(...)
30
- elif result.decision == "STEP_UP":
31
- # a human sponsor must approve first: client.grant(...), then re-check
32
- ...
33
- else:
34
- # DENY
35
- ...
36
- ```
37
-
38
- No API key yet? Try the keyless sandbox:
39
-
40
- ```python
41
- client = WritClient()
42
- client.sandbox({
43
- "sponsorId": "acme",
44
- "agentId": "agent-7",
45
- "verb": "demo_write", # sandbox only allows demo_write ...
46
- "target": "demo-customers", # ... on targets starting with demo-
47
- "purpose": "trying the gate",
48
- })
49
- ```
50
-
51
- ### What's covered
52
-
53
- - `check(...)` — the gate: `ALLOW` / `DENY` / `STEP_UP`, plus a receipt every time
54
- - `verify_token(...)` — validate an `ALLOW` auth token (catches purpose drift)
55
- - `grant(...)` — human-sponsor approval for the `STEP_UP` path
56
- - `get_policy()` / `set_policy(...)` — manage the tenant policy
57
- - `revoke(...)` / `reinstate(...)` / `revoked()` — the kill switch
58
- - `receipts()` / `receipt(id)` / `verify_chain()` / `stream_receipts()` — the audit log
59
- - `sandbox(...)` — keyless trial, no API key required
60
-
61
- Every decision writes a receipt, so the audit log is the meter.
62
-
63
- ## CLI
64
-
65
- The same package ships the `writ` command:
66
-
67
- ```bash
68
- writ check --key writ_... --sponsor acme --agent agent-7 \
69
- --verb db.write --target prod.customers --purpose "backfill region field"
70
- writ scan ./my-repo
71
- writ receipts --key writ_...
72
- writ verify-chain --key writ_...
73
- ```
74
-
75
- Run `writ --help` for the full command list.
76
-
77
- ## Docs
78
-
79
- Full API reference: [withwrit.com/docs](https://withwrit.com/docs)
80
-
81
- ## License
82
-
83
- MIT
@@ -1,97 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: pywrit
3
- Version: 0.2.3
4
- Summary: Python client + CLI for Writ — the gate before the write
5
- License: MIT
6
- Project-URL: Homepage, https://github.com/AvenueDAdmin/pywrit
7
- Project-URL: Documentation, https://withwrit.com/docs
8
- Keywords: ai-agents,policy,audit-log,guardrails
9
- Requires-Python: >=3.9
10
- Description-Content-Type: text/markdown
11
- License-File: LICENSE
12
- Requires-Dist: httpx>=0.27
13
- Dynamic: license-file
14
-
15
- # pywrit
16
-
17
- Python client + CLI for [Writ](https://withwrit.com) — the gate before the write.
18
- Put a policy gate in front of your agent's dangerous actions.
19
-
20
- ## Install
21
-
22
- ```bash
23
- pip install pywrit
24
- ```
25
-
26
- ## Python client
27
-
28
- ```python
29
- from pywrit import WritClient
30
-
31
- client = WritClient(api_key="writ_...")
32
-
33
- result = client.check(
34
- sponsor_id="acme",
35
- agent_id="agent-7",
36
- verb="db.write",
37
- target="prod.customers",
38
- purpose="backfill region field",
39
- )
40
-
41
- if result.decision == "ALLOW":
42
- # result.auth_token is a short-lived token bound to this exact write
43
- perform_write(...)
44
- elif result.decision == "STEP_UP":
45
- # a human sponsor must approve first: client.grant(...), then re-check
46
- ...
47
- else:
48
- # DENY
49
- ...
50
- ```
51
-
52
- No API key yet? Try the keyless sandbox:
53
-
54
- ```python
55
- client = WritClient()
56
- client.sandbox({
57
- "sponsorId": "acme",
58
- "agentId": "agent-7",
59
- "verb": "demo_write", # sandbox only allows demo_write ...
60
- "target": "demo-customers", # ... on targets starting with demo-
61
- "purpose": "trying the gate",
62
- })
63
- ```
64
-
65
- ### What's covered
66
-
67
- - `check(...)` — the gate: `ALLOW` / `DENY` / `STEP_UP`, plus a receipt every time
68
- - `verify_token(...)` — validate an `ALLOW` auth token (catches purpose drift)
69
- - `grant(...)` — human-sponsor approval for the `STEP_UP` path
70
- - `get_policy()` / `set_policy(...)` — manage the tenant policy
71
- - `revoke(...)` / `reinstate(...)` / `revoked()` — the kill switch
72
- - `receipts()` / `receipt(id)` / `verify_chain()` / `stream_receipts()` — the audit log
73
- - `sandbox(...)` — keyless trial, no API key required
74
-
75
- Every decision writes a receipt, so the audit log is the meter.
76
-
77
- ## CLI
78
-
79
- The same package ships the `writ` command:
80
-
81
- ```bash
82
- writ check --key writ_... --sponsor acme --agent agent-7 \
83
- --verb db.write --target prod.customers --purpose "backfill region field"
84
- writ scan ./my-repo
85
- writ receipts --key writ_...
86
- writ verify-chain --key writ_...
87
- ```
88
-
89
- Run `writ --help` for the full command list.
90
-
91
- ## Docs
92
-
93
- Full API reference: [withwrit.com/docs](https://withwrit.com/docs)
94
-
95
- ## License
96
-
97
- MIT
File without changes
File without changes
File without changes
File without changes