PYRE — Flask-flavored Python on the Internet Computer: certified reads, urllib-shaped outcalls, stable-memory collections
Project description
Write recognizable Python — Flask-style routes, a data layer, an outbound HTTP call — and run it on the Internet Computer (ICP), a decentralized WASM host. No Candid, no Rust, no Motoko.
What that buys you over Flask-on-a-VPS:
- Certified responses — clients cryptographically verify your API's answers against the network's root of trust, not "trust the server."
- Threshold-signed JWTs — the subnet signs cooperatively; there is no private key anywhere to steal.
- Consensus-safe randomness & audited encryption — the platform footguns are defused; the safe paths look like ordinary Python.
- ~$0.40/month for a light backend, measured on mainnet.
from pyre import App, Request, Response, data
app = App()
app.enable_cors(origins="*")
items = data.collection("items", schema={"name": str, "qty": (int, 1)})
@app.get("/health", certified=True) # served with a verifiable certificate
def health(req: Request) -> Response:
return Response.json({"status": "ok"})
@app.post("/items") # runs as an update: writes persist
def create_item(req: Request) -> Response:
return Response.json(items.insert(req.json()), status=201)
@app.get("/items")
def list_items(req: Request) -> Response:
return Response.json(items.list(limit=20, after=req.query.get("after")))
Outbound HTTPS looks like urllib, but async — because on ICP it is:
from pyre.compat import urllib_request as urllib
@app.get("/quote", update=True)
async def quote(req):
resp = await urllib.urlopen("https://api.example.com/quote",
max_response_bytes=8_192)
return Response.json({"upstream_status": resp.status, "data": resp.json()})
And ICP's genuinely differentiated capabilities read like ordinary Python (every one opt-in — a plain CRUD app never meets them):
from pyre import random as prandom, time as ptime, sign
from pyre.adapters import supabase
@app.get("/id")
def new_id(req):
return Response.json({"id": prandom.uuid4()}) # consensus-safe; naive uuid4 fails loudly
@app.get("/attest", update=True)
async def attest(req):
token = await sign.jwt({"sub": req.caller, "iat": ptime.now()})
return Response.json({"jwt": token}) # threshold-signed: no key to steal
@app.get("/external", update=True)
async def external(req):
db = supabase.Client(url=SUPA_URL, anon_key=SUPA_KEY)
return Response.json(await db.table("items").select().limit(10))
Install
pip install pyre-icp
The distribution is pyre-icp; the import package and the CLI are both
pyre. To deploy canisters you also need, one time:
- Python 3.10.7 as Kybra's build interpreter (CPython, via pyenv). It compiles your code to Wasm — it is not what runs on-chain: the canister runs RustPython (a Rust Python interpreter) plus Rust crates, so CPython 3.10's Oct-2026 EOL is a build-toolchain note, not a running-service one. (details)
- dfx (the ICP SDK):
DFXVM_INIT_YES=true sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)" - Kybra in your project's deploy venv:
pip install kybra==0.7.1+python -m kybra install-dfx-extension
Then:
pyre new myapp --template crud-kv # bare-api | crud-kv | outbound-proxy
cd myapp
pyre dev src/app.py # instant local server, no replica needed
dfx start --background && dfx deploy # real local canister — free, no wallet
dfx deploy --network ic # mainnet — needs cycles (free coupon path below)
Everything up to the last line is free and needs no blockchain identity, ICP, or cycles — build and exercise the whole app locally at $0. Only a persistent mainnet canister costs anything, and there's a free cycles-faucet coupon for it. (Heads-up: a Kybra Wasm is ~27 MB, so ICP's playground / ICP Ninja "instant deploy" options don't fit it — mainnet is the public-URL path. The quickstart walks it honestly, coupon included.)
The API surface
| Module | What it gives you | Docs |
|---|---|---|
pyre.App / Request / Response |
Flask-style routing, path params, hooks, error handlers, CORS, certified routes | api.md |
pyre.data / pyre.kv |
Collections + KV over stable memory — survives upgrades; schemas, pagination, lazy migration | api.md |
pyre.validate |
Dict-schema request validation → clean per-field 400s | api.md |
pyre.auth |
Bearer / API-key / HTTP Basic middleware, constant-time, hash-stored creds | api.md |
pyre.compat.urllib_request |
urllib-shaped async HTTPS outcalls with determinism transforms | concepts.md |
pyre.random / pyre.uuid / pyre.time |
Consensus-safe RNG, UUIDs, timestamps (naive stdlib entropy fails loudly in-canister — by design) | random-uuid-time.md |
pyre.crypto |
AES-GCM, ChaCha20-Poly1305, sha2/sha3/blake2/blake3, HMAC — audited RustCrypto under the hood | crypto.md |
pyre.sign |
Threshold tECDSA signatures + ES256K JWTs — no private key exists | api.md |
pyre.oidc (1.2) |
Verify Google/OIDC RS256/ES256 ID tokens in-canister (JWKS cached + determinism-transformed); real sign-in without trusting a server | oidc.md |
pyre.static (1.2) |
Serve a built SPA (Vue/React/…) from the canister, certified index + chunked stable-memory assets; pyre assets push dist/ |
static-serving.md |
pyre.adapters |
Supabase (PostgREST) + Upstash Redis over outcalls, amplification-safe writes | adapters.md |
pyre.log |
Structured logging retrievable via dfx canister logs |
observability.md |
pyre CLI |
pyre new (templates), pyre dev (local server + footgun warnings), pyre assets push |
quickstart.md |
All docs: quickstart · concepts · API reference · troubleshooting · stdlib support matrix · secrets & outcalls · extending with Rust · observability · LLM/agent skill file · reference app: examples/food_tracker
Give your coding agent PYRE (Claude Code skill)
This repo is a Claude Code marketplace. Teach your agent the framework — the golden rules, the Kybra build traps, the capability map — with two lines:
claude plugin marketplace add Sweet-Papa-Technologies/PYRE
claude plugin install pyre-icp@pyre
Now Claude auto-loads the PYRE skill whenever you write canister code, debug a Kybra
build, or deploy. (The skill is SKILL.md —
installing it just keeps it current via claude plugin update pyre-icp.)
The four ICP concepts PYRE teaches (and hides everything else)
- Query vs. update calls. Queries are fast, read-only, uncertified; updates go through consensus (~1–2 s) and can write. PYRE maps GET → query, writes/async → update; honesty guards raise if you write state or make outcalls from a query.
- Outbound HTTP is async and consensus-gated. Every replica performs
your outcall independently and must agree byte-for-byte — hence
await, and hence transforms. - The determinism transform. Upstream responses differ per replica
(Date headers, request ids). Outcalls run through a transform that
canonicalizes the response before consensus;
pyre devshows you what gets stripped before you ever deploy. - Canisters are long-lived actors. The interpreter boots once at
install and stays warm — no cold starts, but funding (cycles) and
instruction budgets are real.
make budgetsmeasures; DECISIONS.md records.
Full explanations with failure symptoms in concepts.md.
Mainnet-proven, not aspirational
Every load-bearing claim was tested against ICP mainnet (13-node subnet) and recorded in DECISIONS.md: response certification verified by the official DFINITY verifier (BLS to the NNS root key, tamper/stale rejected), outcall determinism proven across real replicas, threshold signatures externally verified (26.19B cycles ≈ 3.5¢ each), 13× write-amplification converging to single rows through the adapters, and a light backend costing ≈ $0.40/month.
Working from a clone
make setup # venvs (Python 3.10.7 via pyenv), kybra, dfx extension
make test # ~240 unit tests, no replica needed
make dev # instant local server for examples/rest_api
make start deploy # local replica + all example canisters
make e2e # 20-check acceptance suite
make pocketic # canister-level integration tests
make budget-gate # instruction + wasm-size/idle-burn regression gates
CI runs the same gates on every push. See CONTRIBUTING.
Scope fences
Pure Python only — no C extensions, no Pydantic. No sockets/threads (stubbed with guidance), no websockets/streaming. Secret-bearing outcalls (calling Stripe/OpenAI with a private key) are a documented limitation until v1.2's signed proxy.
License
MIT © Sweet Papa Technologies
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyre_icp-1.3.0.tar.gz.
File metadata
- Download URL: pyre_icp-1.3.0.tar.gz
- Upload date:
- Size: 119.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6a56d54437be6e2db023da057b4ee986747c4976b06e4f11fb372aa1da815a2d
|
|
| MD5 |
6ee38f3822eca3d236535b15dd19b965
|
|
| BLAKE2b-256 |
81fdd5d9acdfcd692c5fa1f6b9debce00a2d89030d7b56ae34d8f6bd367f19e0
|
File details
Details for the file pyre_icp-1.3.0-py3-none-any.whl.
File metadata
- Download URL: pyre_icp-1.3.0-py3-none-any.whl
- Upload date:
- Size: 138.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0dda0a3e3915114a80d9e957a47b912fe4f034e5cade69412e077b5cb18fd9a1
|
|
| MD5 |
ceea53d777151e79bc9c69672ccc71ed
|
|
| BLAKE2b-256 |
10162f8d279ac936c8fc75301c490bd9fd6bdd0a7ae4a2ceb0b5a586653066bb
|