IKAREMIKAREM v1.1.0

ZERO-DEPENDENCY PYTHON ASGI

IKAREM

Zero-dependency Python ASGI framework, built for humans and LLMs. FastAPI-style DX (DI, validation, OpenAPI, auth) plus Django/Nest-style structure (plugins, config, RBAC) — on a core that runs on the standard library alone.

Footprint of an origin story: IKAREM started as an answer to Meraki — same decorator shape, working plugins, everything promised but never shipped. The rivalry is archived; the framework stayed.

K IKAREM MERAKI

The original mark — the Meraki rivalry, archived. The framework underneath is all IKAREM.

208tests, all green
0required dependencies
~110kin-process req/s
21routes on the live demo

01 / INSTALL

One line. Zero required deps.

$ pip install ikarem
$ pip install "ikarem[server]"    # uvicorn to serve
$ pip install "ikarem[postgres]"  # asyncpg strategy
$ pip install "ikarem[test]"      # pytest + httpx

Python ≥3.10. Uvicorn, asyncpg, aiomysql and friends are optional extras — the core is pure stdlib + ASGI and runs anywhere.

02 / QUICKSTART

Thirty seconds to a running app.

from ikarem import Ikarem

app = Ikarem(debug=True)

# dicts become JSON. path params are typed.
@app.get("/")
async def home(req):
    return {"hello": "ikarem"}

@app.get("/users/{uid:int}")
async def get_user(req, uid: int):
    return {"uid": uid}
$ uvicorn myapp:app            # any ASGI server works
$ ikarem check myapp:app       # static audit: deps, cycles, auth gaps

Every app ships with GET /healthz, GET /readyz, GET /metrics, GET /openapi.json and GET /docs (Swagger UI). Validation, JWT + RBAC, sessions + CSRF, rate limiting, caching, background tasks, websockets, static files and an MCP server (ikarem mcp) are built in — see Reference. Learn by copying: 20 runnable recipes, every snippet executed in CI.

03 / WHY IKAREM

Stated plainly.

MerakiIKAREM answer
Hard-requires uvicornZero required deps. Core is stdlib + ASGI.
Static paths only, no paramsCompiled {param:int/float/uuid/path} routes, 404 vs 405, url_for.
Requests can't read bodiesawait body()/json()/form() — JSON and multipart uploads.
Plugin/config files are emptyWorking plugin system (deps sorted, cycles caught) + layered config. Extensions: ecosystem registry.
No validation, auth, or docsSchema + Field(), JWT + RBAC + sessions, OpenAPI + MCP tools.
No test storyCookie-jar TestClient, 208-test suite, ikarem check.
Honest footnote. On empty-route micro-benchmarks Meraki is faster — it does ~nothing per request (no header parsing, no body, no DI). Past ~50k in-process req/s both frameworks are 10× beyond what a network + database app saturates. Full numbers: §04.

Migration is a two-line diff (guide):

- from meraki import Meraki
+ from ikarem.meraki_compat import Meraki

04 / BENCHMARKS

Same harness. Same process. No fairy dust.

N=3000 in-process ASGI req/s. Rerun: python bench/bench_switch.py.

RouteAppreq/s
GET /hellomeraki~314,000
GET /helloikarem~110,000
GET /missing (404)meraki / ikarem~347,000 / ~97,000
GET /users/{id} (params)ikarem~85,000 — meraki: no path params (404)
POST /echo 1KB JSONikarem~71,000 — meraki: no body API

And the number that matters for production: ~75k sustained requests over real uvicorn against the Ledger demo app — zero 5xx, zero timeouts (healthz 1567 rps, authed SQLite reads 1178 rps, concurrent writes 991 rps). Harness: bench/load.py.

05 / LIVE DEMO

Ledger — a real app on stock IKAREM.

Personal finance: session auth + CSRF, dashboard with SVG charts, full CRUD over HTML forms and JSON API, receipt uploads, CSV export, rate limits, security headers. 21 routes, all audited by ikarem check.

$ python -m uvicorn ledger.app:app     # from the repo root
# open http://127.0.0.1:8000 — demo: demo@example.com / demo1234

Production shape: ledger/Dockerfile + ledger/docker-compose.yml (app + Postgres 16) + docs/DEPLOY.md. The app runs on SQLite or Postgres via IKAREM_DB_URL — full suite green on both.

06 / THE STACK

Everything included. Nothing required.

NeedAnswer
RoutingCompiled routes, converters, 404 vs 405, include_router, static mounts
InputSchema + Field(ge/le/pattern/email…) for JSON and forms; 400s automatic
LogicDepends() DI — nested, cached, sync/async/yield, cycle-checked, compiled once
AuthHS256 JWT + RBAC + scopes + API keys, pbkdf2 passwords, signed-cookie sessions, CSRF, trusted hosts
OpsRate limit, timeouts, bulkheads, idempotency, CORS, security headers, request IDs, /healthz /readyz /metrics, cron + durable queues + migrations
DataAsync strategy interface: SQLite (stdlib) / Postgres / MySQL / SQLServer
Machine clientsOpenAPI 3.1 + Swagger UI; every route as an MCP tool (ikarem mcp)
ModularityBlueprint groups + MethodView classes + abort()
TemplatesTemplates(dir) (Jinja2) + flash() notifications
CLIikarem run|check|mcp|new|migrate|worker|inspect — serve, audit, expose tools, scaffold, migrate, drain queues, print manifests
Recipes20 copy-paste recipes — every snippet executed in CI, so the docs can't drift

07 / OPERATE IT

Boring in production. As it should be.

$ ikarem new myapp && cd myapp   # starter: auth + CRUD + tests + Dockerfile
$ pytest -q && uvicorn app:app
$ ikarem check myapp:app          # gate deploys on this

Config is layered (defaults < kwargs < dict < IKAREM_* env). Health checks: liveness /healthz, readiness /readyz (503 while the DB is down).

08 / STRAIGHT ANSWERS

Asked in production. Answered here.

Demo login fails? Check SELECT email FROM users — if the demo row is missing, delete the SQLite file and reboot (it reseeds), or just register a fresh account.

403 on API POSTs? That's CSRF doing its job. Send the X-CSRF-Token header (fetch it from GET /api/csrf), or exempt pure token APIs via exempt_paths.

401 or 403? 401 means anonymous — browsers redirect to /login. 403 means authenticated but not allowed.

Rate-limited on localhost? Limits are per client IP (X-Forwarded-For or socket peer), 240/min default. Tune RateLimitMiddleware(per_minute=…).

SQLite under concurrent writes? Single-lane by design (WAL on, corruption-proof). Reads stay fast; past toy scale, point IKAREM_DB_URL at Postgres — the suite is green on both.

Postgres + tests? One event loop per thread: TestClient reuses it, so loop-bound pools survive across requests instead of churning.