pyre-icp 1.1.0__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.
Files changed (54) hide show
  1. pyre_icp-1.1.0/LICENSE +21 -0
  2. pyre_icp-1.1.0/PKG-INFO +235 -0
  3. pyre_icp-1.1.0/README.md +193 -0
  4. pyre_icp-1.1.0/pyproject.toml +35 -0
  5. pyre_icp-1.1.0/pyre/__init__.py +86 -0
  6. pyre_icp-1.1.0/pyre/_runtime.py +28 -0
  7. pyre_icp-1.1.0/pyre/adapters/__init__.py +17 -0
  8. pyre_icp-1.1.0/pyre/adapters/supabase.py +301 -0
  9. pyre_icp-1.1.0/pyre/adapters/upstash.py +148 -0
  10. pyre_icp-1.1.0/pyre/application.py +317 -0
  11. pyre_icp-1.1.0/pyre/auth.py +176 -0
  12. pyre_icp-1.1.0/pyre/certification.py +384 -0
  13. pyre_icp-1.1.0/pyre/cli.py +222 -0
  14. pyre_icp-1.1.0/pyre/compat/__init__.py +1 -0
  15. pyre_icp-1.1.0/pyre/compat/_stubs.py +91 -0
  16. pyre_icp-1.1.0/pyre/compat/urllib_request.py +61 -0
  17. pyre_icp-1.1.0/pyre/cors.py +68 -0
  18. pyre_icp-1.1.0/pyre/crypto.py +365 -0
  19. pyre_icp-1.1.0/pyre/data.py +158 -0
  20. pyre_icp-1.1.0/pyre/dev.py +119 -0
  21. pyre_icp-1.1.0/pyre/errors.py +53 -0
  22. pyre_icp-1.1.0/pyre/gateway.py +100 -0
  23. pyre_icp-1.1.0/pyre/http_types.py +129 -0
  24. pyre_icp-1.1.0/pyre/kv.py +127 -0
  25. pyre_icp-1.1.0/pyre/log.py +84 -0
  26. pyre_icp-1.1.0/pyre/outcall.py +233 -0
  27. pyre_icp-1.1.0/pyre/prandom.py +291 -0
  28. pyre_icp-1.1.0/pyre/ptime.py +78 -0
  29. pyre_icp-1.1.0/pyre/puuid.py +18 -0
  30. pyre_icp-1.1.0/pyre/routing.py +128 -0
  31. pyre_icp-1.1.0/pyre/sign.py +270 -0
  32. pyre_icp-1.1.0/pyre/templates/bare-api/.gitignore +4 -0
  33. pyre_icp-1.1.0/pyre/templates/bare-api/README.md +36 -0
  34. pyre_icp-1.1.0/pyre/templates/bare-api/dfx.json +8 -0
  35. pyre_icp-1.1.0/pyre/templates/bare-api/src/app.py +23 -0
  36. pyre_icp-1.1.0/pyre/templates/bare-api/src/main.py +126 -0
  37. pyre_icp-1.1.0/pyre/templates/crud-kv/.gitignore +4 -0
  38. pyre_icp-1.1.0/pyre/templates/crud-kv/README.md +36 -0
  39. pyre_icp-1.1.0/pyre/templates/crud-kv/dfx.json +8 -0
  40. pyre_icp-1.1.0/pyre/templates/crud-kv/src/app.py +61 -0
  41. pyre_icp-1.1.0/pyre/templates/crud-kv/src/main.py +126 -0
  42. pyre_icp-1.1.0/pyre/templates/outbound-proxy/.gitignore +4 -0
  43. pyre_icp-1.1.0/pyre/templates/outbound-proxy/README.md +36 -0
  44. pyre_icp-1.1.0/pyre/templates/outbound-proxy/dfx.json +8 -0
  45. pyre_icp-1.1.0/pyre/templates/outbound-proxy/src/app.py +35 -0
  46. pyre_icp-1.1.0/pyre/templates/outbound-proxy/src/main.py +126 -0
  47. pyre_icp-1.1.0/pyre/transform.py +54 -0
  48. pyre_icp-1.1.0/pyre/validation.py +94 -0
  49. pyre_icp-1.1.0/pyre_icp.egg-info/PKG-INFO +235 -0
  50. pyre_icp-1.1.0/pyre_icp.egg-info/SOURCES.txt +52 -0
  51. pyre_icp-1.1.0/pyre_icp.egg-info/dependency_links.txt +1 -0
  52. pyre_icp-1.1.0/pyre_icp.egg-info/entry_points.txt +2 -0
  53. pyre_icp-1.1.0/pyre_icp.egg-info/top_level.txt +2 -0
  54. pyre_icp-1.1.0/setup.cfg +4 -0
pyre_icp-1.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PYRE contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,235 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyre-icp
3
+ Version: 1.1.0
4
+ Summary: PYRE — Flask-flavored Python on the Internet Computer: certified reads, urllib-shaped outcalls, stable-memory collections
5
+ License: MIT License
6
+
7
+ Copyright (c) 2026 PYRE contributors
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+ Project-URL: Homepage, https://github.com/Sweet-Papa-Technologies/PYRE
28
+ Project-URL: Documentation, https://github.com/Sweet-Papa-Technologies/PYRE/tree/main/docs
29
+ Project-URL: Repository, https://github.com/Sweet-Papa-Technologies/PYRE
30
+ Project-URL: Issues, https://github.com/Sweet-Papa-Technologies/PYRE/issues
31
+ Project-URL: Changelog, https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/DECISIONS.md
32
+ Keywords: internet-computer,icp,canister,web-framework,kybra
33
+ Classifier: Development Status :: 4 - Beta
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python :: 3.10
37
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
38
+ Requires-Python: >=3.10
39
+ Description-Content-Type: text/markdown
40
+ License-File: LICENSE
41
+ Dynamic: license-file
42
+
43
+ <p align="center">
44
+ <img src="https://raw.githubusercontent.com/Sweet-Papa-Technologies/PYRE/main/img/pyre-larger-banner.jpg" alt="PYRE — Python on the Internet Computer" width="720">
45
+ </p>
46
+
47
+ <p align="center">
48
+ <a href="https://pypi.org/project/pyre-icp/"><img src="https://img.shields.io/pypi/v/pyre-icp?color=e05d44&label=pypi%20%7C%20pyre-icp" alt="PyPI"></a>
49
+ <a href="https://github.com/Sweet-Papa-Technologies/PYRE/actions/workflows/ci.yml"><img src="https://github.com/Sweet-Papa-Technologies/PYRE/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
50
+ <a href="https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT"></a>
51
+ <img src="https://img.shields.io/badge/python-3.10-blue" alt="Python 3.10">
52
+ </p>
53
+
54
+ Write recognizable Python — Flask-style routes, a data layer, an outbound
55
+ HTTP call — and run it on the [Internet Computer](https://internetcomputer.org)
56
+ (ICP), a decentralized WASM host. No Candid, no Rust, no Motoko.
57
+
58
+ What that buys you over Flask-on-a-VPS:
59
+
60
+ - **Certified responses** — clients cryptographically verify your API's
61
+ answers against the network's root of trust, not "trust the server."
62
+ - **Threshold-signed JWTs** — the subnet signs cooperatively; there is no
63
+ private key anywhere to steal.
64
+ - **Consensus-safe randomness & audited encryption** — the platform
65
+ footguns are defused; the safe paths look like ordinary Python.
66
+ - **~$0.40/month** for a light backend, measured on mainnet.
67
+
68
+ ```python
69
+ from pyre import App, Request, Response, data
70
+
71
+ app = App()
72
+ app.enable_cors(origins="*")
73
+
74
+ items = data.collection("items", schema={"name": str, "qty": (int, 1)})
75
+
76
+ @app.get("/health", certified=True) # served with a verifiable certificate
77
+ def health(req: Request) -> Response:
78
+ return Response.json({"status": "ok"})
79
+
80
+ @app.post("/items") # runs as an update: writes persist
81
+ def create_item(req: Request) -> Response:
82
+ return Response.json(items.insert(req.json()), status=201)
83
+
84
+ @app.get("/items")
85
+ def list_items(req: Request) -> Response:
86
+ return Response.json(items.list(limit=20, after=req.query.get("after")))
87
+ ```
88
+
89
+ Outbound HTTPS looks like urllib, but async — because on ICP it is:
90
+
91
+ ```python
92
+ from pyre.compat import urllib_request as urllib
93
+
94
+ @app.get("/quote", update=True)
95
+ async def quote(req):
96
+ resp = await urllib.urlopen("https://api.example.com/quote",
97
+ max_response_bytes=8_192)
98
+ return Response.json({"upstream_status": resp.status, "data": resp.json()})
99
+ ```
100
+
101
+ And ICP's genuinely differentiated capabilities read like ordinary Python
102
+ (every one opt-in — a plain CRUD app never meets them):
103
+
104
+ ```python
105
+ from pyre import random as prandom, time as ptime, sign
106
+ from pyre.adapters import supabase
107
+
108
+ @app.get("/id")
109
+ def new_id(req):
110
+ return Response.json({"id": prandom.uuid4()}) # consensus-safe; naive uuid4 fails loudly
111
+
112
+ @app.get("/attest", update=True)
113
+ async def attest(req):
114
+ token = await sign.jwt({"sub": req.caller, "iat": ptime.now()})
115
+ return Response.json({"jwt": token}) # threshold-signed: no key to steal
116
+
117
+ @app.get("/external", update=True)
118
+ async def external(req):
119
+ db = supabase.Client(url=SUPA_URL, anon_key=SUPA_KEY)
120
+ return Response.json(await db.table("items").select().limit(10))
121
+ ```
122
+
123
+ ## Install
124
+
125
+ ```bash
126
+ pip install pyre-icp
127
+ ```
128
+
129
+ The distribution is `pyre-icp`; the import package and the CLI are both
130
+ `pyre`. To deploy canisters you also need, one time:
131
+
132
+ - **Python 3.10.x** (Kybra's RustPython targets 3.10 — [pyenv](https://github.com/pyenv/pyenv) recommended)
133
+ - **dfx** (the ICP SDK): `DFXVM_INIT_YES=true sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)"`
134
+ - **Kybra** in your project's deploy venv: `pip install kybra==0.7.1` +
135
+ `python -m kybra install-dfx-extension`
136
+
137
+ Then:
138
+
139
+ ```bash
140
+ pyre new myapp --template crud-kv # bare-api | crud-kv | outbound-proxy
141
+ cd myapp
142
+ pyre dev src/app.py # instant local server, no replica needed
143
+ dfx start --background && dfx deploy # real local canister
144
+ dfx deploy --network ic # mainnet
145
+ ```
146
+
147
+ The [quickstart](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/quickstart.md)
148
+ walks the whole path in ~15 minutes.
149
+
150
+ ## The API surface
151
+
152
+ | Module | What it gives you | Docs |
153
+ |---|---|---|
154
+ | `pyre.App` / `Request` / `Response` | Flask-style routing, path params, hooks, error handlers, CORS, certified routes | [api.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/api.md) |
155
+ | `pyre.data` / `pyre.kv` | Collections + KV over stable memory — survives upgrades; schemas, pagination, lazy migration | [api.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/api.md#pyredata--collections-over-kv) |
156
+ | `pyre.validate` | Dict-schema request validation → clean per-field 400s | [api.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/api.md#pyrevalidate) |
157
+ | `pyre.auth` | Bearer / API-key / HTTP Basic middleware, constant-time, hash-stored creds | [api.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/api.md#pyreauth) |
158
+ | `pyre.compat.urllib_request` | urllib-shaped async HTTPS outcalls with determinism transforms | [concepts.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/concepts.md) |
159
+ | `pyre.random` / `pyre.uuid` / `pyre.time` | Consensus-safe RNG, UUIDs, timestamps (naive stdlib entropy **fails loudly** in-canister — by design) | [random-uuid-time.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/random-uuid-time.md) |
160
+ | `pyre.crypto` | AES-GCM, ChaCha20-Poly1305, sha2/sha3/blake2/blake3, HMAC — audited RustCrypto under the hood | [crypto.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/crypto.md) |
161
+ | `pyre.sign` | Threshold tECDSA signatures + ES256K JWTs — no private key exists | [api.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/api.md#pyresign--threshold-signing-tecdsa) |
162
+ | `pyre.adapters` | Supabase (PostgREST) + Upstash Redis over outcalls, amplification-safe writes | [adapters.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/adapters.md) |
163
+ | `pyre.log` | Structured logging retrievable via `dfx canister logs` | [observability.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/observability.md) |
164
+ | `pyre` CLI | `pyre new` (templates), `pyre dev` (local server + footgun warnings) | [quickstart.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/quickstart.md) |
165
+
166
+ **All docs:**
167
+ [quickstart](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/quickstart.md) ·
168
+ [concepts](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/concepts.md) ·
169
+ [API reference](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/api.md) ·
170
+ [troubleshooting](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/troubleshooting.md) ·
171
+ [stdlib support matrix](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/stdlib-matrix.md) ·
172
+ [secrets & outcalls](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/secrets-and-outcalls.md) ·
173
+ [extending with Rust](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/extending-with-rust.md) ·
174
+ [observability](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/observability.md) ·
175
+ [LLM/agent skill file](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/SKILL.md) ·
176
+ reference app: [examples/food_tracker](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/examples/food_tracker/src/app.py)
177
+
178
+ ## The four ICP concepts PYRE teaches (and hides everything else)
179
+
180
+ 1. **Query vs. update calls.** Queries are fast, read-only, uncertified;
181
+ updates go through consensus (~1–2 s) and can write. PYRE maps GET →
182
+ query, writes/async → update; honesty guards raise if you write state
183
+ or make outcalls from a query.
184
+ 2. **Outbound HTTP is async and consensus-gated.** Every replica performs
185
+ your outcall independently and must agree byte-for-byte — hence
186
+ `await`, and hence transforms.
187
+ 3. **The determinism transform.** Upstream responses differ per replica
188
+ (Date headers, request ids). Outcalls run through a transform that
189
+ canonicalizes the response before consensus; `pyre dev` shows you what
190
+ gets stripped before you ever deploy.
191
+ 4. **Canisters are long-lived actors.** The interpreter boots once at
192
+ install and stays warm — no cold starts, but funding (cycles) and
193
+ instruction budgets are real. `make budgets` measures; DECISIONS.md
194
+ records.
195
+
196
+ Full explanations with failure symptoms in
197
+ [concepts.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/concepts.md).
198
+
199
+ ## Mainnet-proven, not aspirational
200
+
201
+ Every load-bearing claim was tested against ICP mainnet (13-node subnet)
202
+ and recorded in
203
+ [DECISIONS.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/DECISIONS.md):
204
+ response certification verified by the official DFINITY verifier (BLS to
205
+ the NNS root key, tamper/stale rejected), outcall determinism proven
206
+ across real replicas, threshold signatures externally verified
207
+ (26.19B cycles ≈ 3.5¢ each), 13× write-amplification converging to single
208
+ rows through the adapters, and a light backend costing ≈ $0.40/month.
209
+
210
+ ## Working from a clone
211
+
212
+ ```bash
213
+ make setup # venvs (Python 3.10.7 via pyenv), kybra, dfx extension
214
+ make test # ~240 unit tests, no replica needed
215
+ make dev # instant local server for examples/rest_api
216
+ make start deploy # local replica + all example canisters
217
+ make e2e # 20-check acceptance suite
218
+ make pocketic # canister-level integration tests
219
+ make budget-gate # instruction + wasm-size/idle-burn regression gates
220
+ ```
221
+
222
+ CI runs the same gates on every push. See
223
+ [CONTRIBUTING](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/CONTRIBUTING.md).
224
+
225
+ ## Scope fences
226
+
227
+ Pure Python only — no C extensions, no Pydantic. No sockets/threads
228
+ (stubbed with guidance), no websockets/streaming. Secret-bearing outcalls
229
+ (calling Stripe/OpenAI with a private key) are a
230
+ [documented limitation](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/secrets-and-outcalls.md)
231
+ until v1.2's signed proxy.
232
+
233
+ ## License
234
+
235
+ MIT © Sweet Papa Technologies
@@ -0,0 +1,193 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/Sweet-Papa-Technologies/PYRE/main/img/pyre-larger-banner.jpg" alt="PYRE — Python on the Internet Computer" width="720">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a href="https://pypi.org/project/pyre-icp/"><img src="https://img.shields.io/pypi/v/pyre-icp?color=e05d44&label=pypi%20%7C%20pyre-icp" alt="PyPI"></a>
7
+ <a href="https://github.com/Sweet-Papa-Technologies/PYRE/actions/workflows/ci.yml"><img src="https://github.com/Sweet-Papa-Technologies/PYRE/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
8
+ <a href="https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT"></a>
9
+ <img src="https://img.shields.io/badge/python-3.10-blue" alt="Python 3.10">
10
+ </p>
11
+
12
+ Write recognizable Python — Flask-style routes, a data layer, an outbound
13
+ HTTP call — and run it on the [Internet Computer](https://internetcomputer.org)
14
+ (ICP), a decentralized WASM host. No Candid, no Rust, no Motoko.
15
+
16
+ What that buys you over Flask-on-a-VPS:
17
+
18
+ - **Certified responses** — clients cryptographically verify your API's
19
+ answers against the network's root of trust, not "trust the server."
20
+ - **Threshold-signed JWTs** — the subnet signs cooperatively; there is no
21
+ private key anywhere to steal.
22
+ - **Consensus-safe randomness & audited encryption** — the platform
23
+ footguns are defused; the safe paths look like ordinary Python.
24
+ - **~$0.40/month** for a light backend, measured on mainnet.
25
+
26
+ ```python
27
+ from pyre import App, Request, Response, data
28
+
29
+ app = App()
30
+ app.enable_cors(origins="*")
31
+
32
+ items = data.collection("items", schema={"name": str, "qty": (int, 1)})
33
+
34
+ @app.get("/health", certified=True) # served with a verifiable certificate
35
+ def health(req: Request) -> Response:
36
+ return Response.json({"status": "ok"})
37
+
38
+ @app.post("/items") # runs as an update: writes persist
39
+ def create_item(req: Request) -> Response:
40
+ return Response.json(items.insert(req.json()), status=201)
41
+
42
+ @app.get("/items")
43
+ def list_items(req: Request) -> Response:
44
+ return Response.json(items.list(limit=20, after=req.query.get("after")))
45
+ ```
46
+
47
+ Outbound HTTPS looks like urllib, but async — because on ICP it is:
48
+
49
+ ```python
50
+ from pyre.compat import urllib_request as urllib
51
+
52
+ @app.get("/quote", update=True)
53
+ async def quote(req):
54
+ resp = await urllib.urlopen("https://api.example.com/quote",
55
+ max_response_bytes=8_192)
56
+ return Response.json({"upstream_status": resp.status, "data": resp.json()})
57
+ ```
58
+
59
+ And ICP's genuinely differentiated capabilities read like ordinary Python
60
+ (every one opt-in — a plain CRUD app never meets them):
61
+
62
+ ```python
63
+ from pyre import random as prandom, time as ptime, sign
64
+ from pyre.adapters import supabase
65
+
66
+ @app.get("/id")
67
+ def new_id(req):
68
+ return Response.json({"id": prandom.uuid4()}) # consensus-safe; naive uuid4 fails loudly
69
+
70
+ @app.get("/attest", update=True)
71
+ async def attest(req):
72
+ token = await sign.jwt({"sub": req.caller, "iat": ptime.now()})
73
+ return Response.json({"jwt": token}) # threshold-signed: no key to steal
74
+
75
+ @app.get("/external", update=True)
76
+ async def external(req):
77
+ db = supabase.Client(url=SUPA_URL, anon_key=SUPA_KEY)
78
+ return Response.json(await db.table("items").select().limit(10))
79
+ ```
80
+
81
+ ## Install
82
+
83
+ ```bash
84
+ pip install pyre-icp
85
+ ```
86
+
87
+ The distribution is `pyre-icp`; the import package and the CLI are both
88
+ `pyre`. To deploy canisters you also need, one time:
89
+
90
+ - **Python 3.10.x** (Kybra's RustPython targets 3.10 — [pyenv](https://github.com/pyenv/pyenv) recommended)
91
+ - **dfx** (the ICP SDK): `DFXVM_INIT_YES=true sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)"`
92
+ - **Kybra** in your project's deploy venv: `pip install kybra==0.7.1` +
93
+ `python -m kybra install-dfx-extension`
94
+
95
+ Then:
96
+
97
+ ```bash
98
+ pyre new myapp --template crud-kv # bare-api | crud-kv | outbound-proxy
99
+ cd myapp
100
+ pyre dev src/app.py # instant local server, no replica needed
101
+ dfx start --background && dfx deploy # real local canister
102
+ dfx deploy --network ic # mainnet
103
+ ```
104
+
105
+ The [quickstart](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/quickstart.md)
106
+ walks the whole path in ~15 minutes.
107
+
108
+ ## The API surface
109
+
110
+ | Module | What it gives you | Docs |
111
+ |---|---|---|
112
+ | `pyre.App` / `Request` / `Response` | Flask-style routing, path params, hooks, error handlers, CORS, certified routes | [api.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/api.md) |
113
+ | `pyre.data` / `pyre.kv` | Collections + KV over stable memory — survives upgrades; schemas, pagination, lazy migration | [api.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/api.md#pyredata--collections-over-kv) |
114
+ | `pyre.validate` | Dict-schema request validation → clean per-field 400s | [api.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/api.md#pyrevalidate) |
115
+ | `pyre.auth` | Bearer / API-key / HTTP Basic middleware, constant-time, hash-stored creds | [api.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/api.md#pyreauth) |
116
+ | `pyre.compat.urllib_request` | urllib-shaped async HTTPS outcalls with determinism transforms | [concepts.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/concepts.md) |
117
+ | `pyre.random` / `pyre.uuid` / `pyre.time` | Consensus-safe RNG, UUIDs, timestamps (naive stdlib entropy **fails loudly** in-canister — by design) | [random-uuid-time.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/random-uuid-time.md) |
118
+ | `pyre.crypto` | AES-GCM, ChaCha20-Poly1305, sha2/sha3/blake2/blake3, HMAC — audited RustCrypto under the hood | [crypto.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/crypto.md) |
119
+ | `pyre.sign` | Threshold tECDSA signatures + ES256K JWTs — no private key exists | [api.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/api.md#pyresign--threshold-signing-tecdsa) |
120
+ | `pyre.adapters` | Supabase (PostgREST) + Upstash Redis over outcalls, amplification-safe writes | [adapters.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/adapters.md) |
121
+ | `pyre.log` | Structured logging retrievable via `dfx canister logs` | [observability.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/observability.md) |
122
+ | `pyre` CLI | `pyre new` (templates), `pyre dev` (local server + footgun warnings) | [quickstart.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/quickstart.md) |
123
+
124
+ **All docs:**
125
+ [quickstart](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/quickstart.md) ·
126
+ [concepts](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/concepts.md) ·
127
+ [API reference](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/api.md) ·
128
+ [troubleshooting](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/troubleshooting.md) ·
129
+ [stdlib support matrix](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/stdlib-matrix.md) ·
130
+ [secrets & outcalls](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/secrets-and-outcalls.md) ·
131
+ [extending with Rust](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/extending-with-rust.md) ·
132
+ [observability](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/observability.md) ·
133
+ [LLM/agent skill file](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/SKILL.md) ·
134
+ reference app: [examples/food_tracker](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/examples/food_tracker/src/app.py)
135
+
136
+ ## The four ICP concepts PYRE teaches (and hides everything else)
137
+
138
+ 1. **Query vs. update calls.** Queries are fast, read-only, uncertified;
139
+ updates go through consensus (~1–2 s) and can write. PYRE maps GET →
140
+ query, writes/async → update; honesty guards raise if you write state
141
+ or make outcalls from a query.
142
+ 2. **Outbound HTTP is async and consensus-gated.** Every replica performs
143
+ your outcall independently and must agree byte-for-byte — hence
144
+ `await`, and hence transforms.
145
+ 3. **The determinism transform.** Upstream responses differ per replica
146
+ (Date headers, request ids). Outcalls run through a transform that
147
+ canonicalizes the response before consensus; `pyre dev` shows you what
148
+ gets stripped before you ever deploy.
149
+ 4. **Canisters are long-lived actors.** The interpreter boots once at
150
+ install and stays warm — no cold starts, but funding (cycles) and
151
+ instruction budgets are real. `make budgets` measures; DECISIONS.md
152
+ records.
153
+
154
+ Full explanations with failure symptoms in
155
+ [concepts.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/concepts.md).
156
+
157
+ ## Mainnet-proven, not aspirational
158
+
159
+ Every load-bearing claim was tested against ICP mainnet (13-node subnet)
160
+ and recorded in
161
+ [DECISIONS.md](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/DECISIONS.md):
162
+ response certification verified by the official DFINITY verifier (BLS to
163
+ the NNS root key, tamper/stale rejected), outcall determinism proven
164
+ across real replicas, threshold signatures externally verified
165
+ (26.19B cycles ≈ 3.5¢ each), 13× write-amplification converging to single
166
+ rows through the adapters, and a light backend costing ≈ $0.40/month.
167
+
168
+ ## Working from a clone
169
+
170
+ ```bash
171
+ make setup # venvs (Python 3.10.7 via pyenv), kybra, dfx extension
172
+ make test # ~240 unit tests, no replica needed
173
+ make dev # instant local server for examples/rest_api
174
+ make start deploy # local replica + all example canisters
175
+ make e2e # 20-check acceptance suite
176
+ make pocketic # canister-level integration tests
177
+ make budget-gate # instruction + wasm-size/idle-burn regression gates
178
+ ```
179
+
180
+ CI runs the same gates on every push. See
181
+ [CONTRIBUTING](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/CONTRIBUTING.md).
182
+
183
+ ## Scope fences
184
+
185
+ Pure Python only — no C extensions, no Pydantic. No sockets/threads
186
+ (stubbed with guidance), no websockets/streaming. Secret-bearing outcalls
187
+ (calling Stripe/OpenAI with a private key) are a
188
+ [documented limitation](https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/docs/secrets-and-outcalls.md)
189
+ until v1.2's signed proxy.
190
+
191
+ ## License
192
+
193
+ MIT © Sweet Papa Technologies
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pyre-icp"
7
+ version = "1.1.0"
8
+ description = "PYRE — Flask-flavored Python on the Internet Computer: certified reads, urllib-shaped outcalls, stable-memory collections"
9
+ requires-python = ">=3.10"
10
+ readme = "README.md"
11
+ license = { file = "LICENSE" }
12
+ keywords = ["internet-computer", "icp", "canister", "web-framework", "kybra"]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Topic :: Internet :: WWW/HTTP :: HTTP Servers",
19
+ ]
20
+
21
+ [project.urls]
22
+ Homepage = "https://github.com/Sweet-Papa-Technologies/PYRE"
23
+ Documentation = "https://github.com/Sweet-Papa-Technologies/PYRE/tree/main/docs"
24
+ Repository = "https://github.com/Sweet-Papa-Technologies/PYRE"
25
+ Issues = "https://github.com/Sweet-Papa-Technologies/PYRE/issues"
26
+ Changelog = "https://github.com/Sweet-Papa-Technologies/PYRE/blob/main/DECISIONS.md"
27
+
28
+ [project.scripts]
29
+ pyre = "pyre.cli:main"
30
+
31
+ [tool.setuptools.packages.find]
32
+ include = ["pyre*"]
33
+
34
+ [tool.setuptools.package-data]
35
+ pyre = ["templates/*/**/*", "templates/*/**/.*"]
@@ -0,0 +1,86 @@
1
+ """PYRE — Python Runtime for the Edge.
2
+
3
+ Flask-flavored Python on the Internet Computer.
4
+
5
+ from pyre import App, Request, Response, kv
6
+
7
+ app = App()
8
+
9
+ @app.get("/health")
10
+ def health(req: Request) -> Response:
11
+ return Response.json({"status": "ok"})
12
+ """
13
+
14
+ from pyre._runtime import in_canister
15
+ from pyre.application import App
16
+ from pyre.errors import (
17
+ BadRequest,
18
+ KvWriteInQueryContext,
19
+ OutcallFailed,
20
+ OutcallInQueryContext,
21
+ PyreError,
22
+ ResponseTooLarge,
23
+ UpstreamHTTPError,
24
+ )
25
+ from pyre.http_types import Request, Response
26
+ from pyre.validation import ValidationError, validate
27
+ from pyre import auth
28
+ from pyre import crypto
29
+ from pyre import data
30
+ from pyre import kv
31
+ from pyre import log
32
+ from pyre import sign
33
+ from pyre import ptime
34
+ from pyre import prandom # imports ptime; keep after it
35
+ from pyre import puuid
36
+
37
+ # DX aliases (§v1.1 randomness/time). The real module files are
38
+ # prandom/ptime/puuid — files named random.py/uuid.py/time.py would shadow
39
+ # the stdlib inside the Kybra bundle. These attribute aliases make the
40
+ # documented spelling work:
41
+ #
42
+ # from pyre import random as prandom
43
+ # from pyre import time as ptime
44
+ # from pyre import uuid as puuid
45
+ #
46
+ # Note: only the `from pyre import ...` form works; the statement form
47
+ # `import pyre.random` will raise ModuleNotFoundError because there is no
48
+ # pyre/random.py file — that is deliberate.
49
+ random = prandom
50
+ time = ptime
51
+ uuid = puuid
52
+
53
+ __version__ = "1.1.0"
54
+
55
+ __all__ = [
56
+ "App",
57
+ "Request",
58
+ "Response",
59
+ "kv",
60
+ "auth",
61
+ "crypto",
62
+ "data",
63
+ "log",
64
+ "sign",
65
+ "random",
66
+ "time",
67
+ "uuid",
68
+ "prandom",
69
+ "ptime",
70
+ "puuid",
71
+ "validate",
72
+ "ValidationError",
73
+ "in_canister",
74
+ "PyreError",
75
+ "BadRequest",
76
+ "KvWriteInQueryContext",
77
+ "OutcallInQueryContext",
78
+ "OutcallFailed",
79
+ "ResponseTooLarge",
80
+ "UpstreamHTTPError",
81
+ ]
82
+
83
+ if in_canister():
84
+ from pyre.compat._stubs import install_stubs
85
+
86
+ install_stubs()
@@ -0,0 +1,28 @@
1
+ """Runtime environment detection and per-dispatch context.
2
+
3
+ PYRE runs in two environments:
4
+ - "canister": inside a Kybra canister (RustPython compiled to WASM on ICP)
5
+ - "dev": on the host CPython (unit tests, `pyre dev` local runner)
6
+
7
+ Keep this module dependency-free; everything else in pyre imports it.
8
+ """
9
+
10
+ import sys
11
+
12
+
13
+ def in_canister() -> bool:
14
+ # Kybra executes canister code under RustPython; host tooling is CPython.
15
+ return sys.implementation.name == "rustpython"
16
+
17
+
18
+ class _Context:
19
+ """Per-dispatch flags set by the gateway/dev-server around handler calls."""
20
+
21
+ def __init__(self):
22
+ # True while a handler is executing in query context (no state
23
+ # writes, no outcalls). The dev server also sets this for routes
24
+ # that are not marked update, so ICP restrictions surface locally.
25
+ self.in_query = False
26
+
27
+
28
+ ctx = _Context()
@@ -0,0 +1,17 @@
1
+ """pyre.adapters — clients for external HTTPS APIs, outcall-hazard-aware.
2
+
3
+ These wrap pyre's HTTPS outcalls with each service's auth and REST
4
+ conventions, and are designed around the platform facts:
5
+
6
+ - one canister outcall fans out to ~node-count upstream requests
7
+ (measured 13x on a 13-node subnet), so WRITES MUST BE IDEMPOTENT;
8
+ - only GET/HEAD/POST reach upstream;
9
+ - responses ride a determinism transform;
10
+ - ~2s consensus latency per call.
11
+
12
+ Standing rule: integration, not hot path. Your real datastore is
13
+ pyre.data over stable memory; adapters are for syncing with systems
14
+ that live outside the IC.
15
+ """
16
+
17
+ from pyre.adapters import supabase, upstash # noqa: F401