Skip to main content

PYRE — Flask-flavored Python on the Internet Computer: certified reads, urllib-shaped outcalls, stable-memory collections

Project description

PYRE — Python on the Internet Computer

PyPI CI MIT Python 3.10

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.x (Kybra's RustPython targets 3.10 — pyenv recommended)
  • 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
dfx deploy --network ic               # mainnet

The quickstart walks the whole path in ~15 minutes.

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

The four ICP concepts PYRE teaches (and hides everything else)

  1. 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.
  2. Outbound HTTP is async and consensus-gated. Every replica performs your outcall independently and must agree byte-for-byte — hence await, and hence transforms.
  3. The determinism transform. Upstream responses differ per replica (Date headers, request ids). Outcalls run through a transform that canonicalizes the response before consensus; pyre dev shows you what gets stripped before you ever deploy.
  4. 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 budgets measures; 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

pyre_icp-1.2.0.tar.gz (81.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyre_icp-1.2.0-py3-none-any.whl (96.3 kB view details)

Uploaded Python 3

File details

Details for the file pyre_icp-1.2.0.tar.gz.

File metadata

  • Download URL: pyre_icp-1.2.0.tar.gz
  • Upload date:
  • Size: 81.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.7

File hashes

Hashes for pyre_icp-1.2.0.tar.gz
Algorithm Hash digest
SHA256 df36357aa3827d1fb9a84367b0bcb04a908178e99aa5f70ad2c96da339efd5bb
MD5 4d1cc714d4d54283414865983f615f96
BLAKE2b-256 05ab55b9dd28fb309cf8660326a66561f9d446671d41f4e35b8d6408223ed6be

See more details on using hashes here.

File details

Details for the file pyre_icp-1.2.0-py3-none-any.whl.

File metadata

  • Download URL: pyre_icp-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 96.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.7

File hashes

Hashes for pyre_icp-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 915b7f352282d46ad5b53b3791de4682bb917bad90c4a8aca6c41ab1e378287b
MD5 656fe3063c4e655443a772dec4dc7a0d
BLAKE2b-256 aae3b45625bde5374bb2f38fd72918fc428caaf3f4f4867f9a17e856b90ac5d2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page