IKAREM
Zero-dependency Python ASGI framework, built for humans and LLMs.
FastAPI-style DX (DI, validation, OpenAPI, auth) + Django/Nest-style structure (plugins, config, RBAC) + a core that runs on stdlib alone. Pip-installable, zero required dependencies.
A Python web framework for REST APIs, HTML apps, websockets, background jobs, and LLM tool servers — one package from prototype to production.
- Install · 30-second example · Industry-grade example · Production web apps · Flask's best, taken · Ops batch · NISH Mode · Stronger · Switching · Why IKAREM · Verification · Layout · Project basics
Install
pip install -e . # core only, zero dependencies
pip install -e ".[server,test]" # uvicorn + pytest + httpx for dev
pip install -e ".[postgres]" # asyncpg strategy
pip install -e ".[mysql]" # aiomysql strategy
pip install -e ".[sqlserver]" # aioodbc strategy
30-second example
from ikarem import Ikarem
app = Ikarem(debug=True)
@app.get("/")
async def home(req):
return {"hello": "ikarem"} # dict auto-becomes JSON
@app.get("/users/{uid:int}")
async def get_user(req, uid: int):
return {"uid": uid}
if __name__ == "__main__":
app.run() # needs `pip install ikarem[server]`
python examples/basic.py
# or
ikarem run examples.basic:app --reload
Industry-grade example
from ikarem import (
BackgroundTasks,
Depends,
Ikarem,
Schema,
CORSMiddleware,
RateLimitMiddleware,
create_token,
require_roles,
)
app = Ikarem(auth_secret="secret")
app.use(CORSMiddleware())
app.use(RateLimitMiddleware(per_minute=120))
class Item(Schema):
name: str
qty: int = 1
def get_prefix():
return "hi"
@app.post("/items")
async def create(req, item: Item, bg: BackgroundTasks, prefix=Depends(get_prefix)):
bg.add(print, f"created {item.name}")
return {"msg": f"{prefix} {item.name}", "qty": item.qty} # validated + coerced
tok = create_token("u1", "secret", roles=["admin"])
@app.get("/admin")
async def adm(req, claims=Depends(require_roles("admin"))):
return {"sub": claims["sub"]} # 401 without token, 403 without role
Built-ins on every app: GET /healthz, GET /metrics,
GET /openapi.json (OpenAPI 3.1 auto-gen), GET /docs (Swagger UI).
Production web apps
ikarem new myapp && cd myapp # auth + sessions + SQLite CRUD (HTML + JSON), tests + Dockerfile
pytest -q && uvicorn app:app
from ikarem import SessionMiddleware, CSRFMiddleware, Field, Schema
app.use(SessionMiddleware()) # signed cookies: req.session["uid"] = ...
app.use(CSRFMiddleware()) # unsafe routes need X-CSRF-Token or _csrf_token field
class Signup(Schema):
email: str = Field(..., email=True, max_length=254)
password: str = Field(..., min_length=8, max_length=128)
class Note(Schema):
text: str = Field(..., min_length=1, max_length=500)
@app.post("/notes")
async def add(req, note: Note): # validates JSON *and* HTML form bodies
form = await req.form() # urlencoded + multipart, UploadFile files,
f = form.get("doc") # 413 past size caps
await f.write(f"/uploads/{f.filename}")
TestClient keeps a cookie jar (login flows just work) and speaks
get/post/put/patch/delete. ikarem check audits handlers, ikarem mcp
serves every route as an LLM tool (plus ikarem://openapi.json and
ikarem://manifest MCP resources), ikarem inspect prints a compact route
manifest for LLM context, and site/llms.txt is the framework manual in one
page. AGENTS.md holds the contributor laws for AI and human agents alike.
@app.tool exposes plain functions and @app.prompt exposes message
templates over the same protocol; safe GET reads carry readOnlyHint.
app.mount_mcp("/mcp") serves it all over Streamable HTTP (stateless)
for remote clients like ChatGPT plugin backends.
Flask's best, taken
from ikarem import Blueprint, MethodView, Templates, abort, flash
api = Blueprint("api", url_prefix="/api")
@api.get("/items/{uid:int}") # own hooks, own errors, url_for("api.x")
async def one(req, uid: int): ...
app.register_blueprint(api) # hooks wrap once — still zero per-request reflection
abort(403, "owner only") # terse errors through the normal pipeline
class Items(MethodView): # one class per resource, full DI per method
async def get(self, req): ...
async def post(self, req, item: Item): ...
app.route("/items", Items.methods())(Items.as_view("items"))
flash(req, "Saved.") # session-backed, shown once in templates
Templates("templates/").response("hi.html", name="amy") # Jinja2 via ikarem[jinja]
Deliberately not taken: context locals (g, global request proxies) and signals —
explicit req params plus plugins and middleware cover that ground without the magic.
Ops batch: migrations, queues, cron, API keys
from ikarem import Migrator, QueuePlugin, require_scopes, APIKeyAuth, task
from ikarem.db import DatabasePlugin
app.register(DatabasePlugin("postgresql://..."))
app.register(QueuePlugin()) # durable jobs in app.state_queue
@task("welcome") # name -> callable, worker-dispatched
async def welcome(to: str): ...
@app.post("/signup")
async def signup(req):
await req.app.state_queue.enqueue("welcome", {"to": "a@b.co"})
return {"ok": True}, 201
@app.every(300) # or @app.cron("0 2 * * *")
async def nightly(): ...
# await app.start_scheduler() # explicit: nothing runs unless started
ikarem migrate new add_users && ikarem migrate up myapp:app
ikarem worker myapp:app # drain the queue until Ctrl+C
Plus the small ones the checklist demanded: Schema(extra="forbid") sanitization,
XMLResponse/dict_to_xml, escape_html, APIKeyAuth (static keys or async lookup=),
require_scopes() for JWT scopes, require_if() predicate (ABAC-lite).
Out of scope on purpose: full ORM (strategy + validated schemas is the answer) and
OAuth2 dance (JWT bearer covers service auth).
Stronger: timeouts, bulkheads, idempotency, rooms
from ikarem import TimeoutMiddleware, ConcurrencyLimitMiddleware, IdempotencyMiddleware, TrustedHostMiddleware
app.use(TimeoutMiddleware(30)) # hung handler -> 503 + Retry-After
app.use(ConcurrencyLimitMiddleware(100)) # bulkhead: fail fast past N in-flight
app.use(IdempotencyMiddleware()) # Idempotency-Key replays, no double charges
app.use(TrustedHostMiddleware(["example.com", ".example.com"]))
Plus Ikarem(max_body_bytes=...) DoS floor, latency fields in /metrics,
atomic SQLite transactions, Room pub/sub for websockets (tested via
TestClient.ws_connect), and a py.typed marker so downstream type checkers
see the real types.
NISH Mode: one switch
from ikarem import Ikarem
from ikarem.testing import TestClient
app = Ikarem(enable_docs=False)
app.nish_mode() # or app.nish = True
@app.get("/hello")
async def hello(req):
return {"hello": "ikarem"}
c = TestClient(app)
assert c.get("/hello").json() == {"hello": "ikarem"}
nish = c.get("/hello", query="format=nish")
assert nish.body.startswith(b"NISH/1.0") and "etag" in nish.headers
One switch and the whole API negotiates NISH (?format=nish or Accept),
both shapes get content-hash ETags with automatic 304s, errors speak NISH,
await req.nish() parses bodies, /openapi.nish serves the contract, and
app.nish_mode(config="app.nish") loads typed config. Full story:
docs/NISH.md.
Live proof it all works, three apps deep: ledger/ — a
personal-finance app (auth, dashboard with SVG charts, CRUD, receipt
uploads, CSV export, JSON API); cadence/ — a habit tracker
(streaks, heatmaps); forge/ — a workshop OS (jobs, kanban,
crew chat, ledger, habits, NISH-first API). All running on stock IKAREM
- uvicorn.
Switching? Bring your routes
Meraki (5 minutes):
-from meraki import Meraki
+from ikarem.meraki_compat import Meraki
Your app runs unchanged (same routes, middleware, 404/405 bodies) on the IKAREM engine — then migrate handler-by-handler. From anywhere else:
- FastAPI (a day):
docs/MIGRATING_FROM_FASTAPI.md - Starlette (hours):
docs/MIGRATING_FROM_STARLETTE.md - Litestar (a day):
docs/MIGRATING_FROM_LITESTAR.md - Flask (a day):
docs/MIGRATING_FROM_FLASK.md - Django (a week, views rewrite):
docs/MIGRATING_FROM_DJANGO.md - Meraki (full guide):
docs/MIGRATING_FROM_MERAKI.md
Plus honest benchmarks: bench/RESULTS.md ·
installable extensions: self-pentest scanner (pip install ./extensions/ikarem-pentest — auth bypass, XSS, SQLi probes)
and OAuth2 login (pip install ./extensions/ikarem-oauth — Google/GitHub + refresh rotation) —
hub ·
extension registry: docs/ECOSYSTEM.md ·
20 runnable recipes: docs/COOKBOOK.md ·
From Zero to Production guide: docs/GUIDE.md (PDF) ·
NISH responses for the Viewer extension: docs/NISH.md ·
install: pip install ikarem.
Footnote: IKAREM started as an answer to Meraki — same decorator shape, working plugins, and everything Meraki's README promised but never shipped. The rivalry is archived; this section is just the moving van.
Why IKAREM
| Rival feature | IKAREM answer |
|---|---|
| pip package, minimal deps | Zero required deps. uvicorn/asyncpg/etc are optional extras. Core is pure stdlib + ASGI. Measured: 1 package / 905 kB vs 11 / 14 MB (FastAPI), 4 / 31 MB (Django) — bench/RESULTS.md. |
| ASGI + Uvicorn boundary | Strict server boundary: Ikarem exposes __call__(scope, receive, send). Any ASGI server works (uvicorn, hypercorn, daphne). HTTP + WebSocket + lifespan. |
| Central app + lifecycle | Ikarem() + on_startup / on_shutdown + lifespan. Plugins hook in with priority + topological dependency order. |
| Request/Response | Lazy Request (query, headers, cookies, await body()/json()), Response helpers (JSON, text, html, stream, redirect, File). Handlers never touch raw ASGI. |
| Routing | Compiled routes with {param} + {param:int/float/uuid/path} converters, 404 vs 405 distinction, url_for, include_router(prefix), static mounts. |
| Middleware | Onion pipeline, short-circuitable, global + composable: CORS, security headers, rate limiting (429 + Retry-After), request-ID/timing, metrics. |
| Plugins | Plugin protocol (name, requires, priority, register, on_startup/shutdown/request/response). Manager sorts dependencies, detects cycles. |
| Config | Layered Config: defaults < kwargs < dict < IKAREM_* env vars. Typed config.get(key, default, cast=...). |
| Errors | HTTPException hierarchy + @app.exception_handler(ExcType) with MRO most-specific match. Tracebacks only when debug=True. |
| DB Strategy (pg/mysql/sqlite/mssql) | DatabaseConnector async ABC (connect/disconnect/execute/fetch_one/fetch_all/execute_many/transaction). Lazy driver imports. SQLite runs on stdlib today. DatabasePlugin proves the extension model. |
| DI (FastAPI parity) | Depends() with nesting, per-request cache (+ opt-out), sync/async/yield deps, finalizers guaranteed after response send and on the exception path, circular-dep rejection. |
| Validation (Pydantic-lite) | Schema models from type hints: coercion, required/optional, nested models, ValidationError → 400. Zero deps — and pydantic models are accepted as body params too (duck-typed seam, 8.3x on 7µs either way: bench/bench_validate.py). |
| Auth | Stdlib HS256 JWT (algorithm-confusion resistant, sub required, expiry enforced), pbkdf2 passwords, BearerAuth + APIKeyAuth, require_roles() / require_scopes() / require_if(). |
| Caching | MemoryCache + RedisCache + @cached over a swappable CacheBackend interface. |
| Background work | BackgroundTasks param (after send) + durable Queue (survives deploys) + cron. |
| Realtime / files | app.websocket(path) + Room pub/sub + WebSocket helper; mount_static() + FileResponse. |
| NISH | app.nish_mode(): whole-API negotiation, ETags + 304s, req.nish(), /openapi.nish, NISH config. |
| Observability | JSON logging, x-request-id + x-process-time-ms, /healthz + /readyz + /metrics, OTel tracing (ikarem[otel]), shared Redis rate limits. |
Verification
381 passed, 3 skipped — framework plus four showcase apps, one command:
python -m pytest tests/ ledger/tests cadence/tests forge/tests relay/tests -q
CI runs the same suite on Python 3.10–3.13 × Ubuntu/macOS/Windows, plus a
live-Postgres job, a Docker showcase build, a starter-template smoke job
(ikarem new + template tests), ruff lint + format, and
ikarem check ledger.app:app. Every behavior ships with a test; bugfix PRs
include a regression test. check also AST-audits handler source —
f-string/.format()/% SQL and blocking calls warn with remedies
(the interpreter accepts both; production regrets both). Gate harder
with check --strict, emit machine output with check --format json,
review exposure with inspect --format auth, dump the spec with
inspect --format openapi, and list tools with mcp --list.
Layout
ikarem/ zero-dep stdlib core (v1.4.0) — optional integrations lazy-load behind extras
app.py Ikarem core + ASGI callable + lifespan + background/cleanup wiring
compiled.py one-time handler plans (no per-request reflection) + check/describe IR
routing.py compiled routes + converters + Did-you-mean 404s
http.py Request (+forms/uploads) + Response family (+cookies)
di.py Depends + nesting + cycle detection + run_cleanups
validation.py Schema models + Field() constraints (zero-dep validation)
auth.py JWT + passwords + Bearer/API-key auth + roles/scopes guards
session.py signed-cookie sessions + CSRF
security.py CORS + security headers + trusted hosts + rate limiting (+ Redis)
resilience.py timeouts + bulkheads + idempotency
cache.py CacheBackend + MemoryCache + RedisCache + @cached
tracing.py TracingMiddleware (OTel spans, SDK lazy via ikarem[otel])
nish.py NISH writer/reader + NISHResponse + negotiate (see NISH Mode)
conditional.py ConditionalMiddleware (ETag 304s)
db/ DatabaseConnector ABC + sqlite/postgres/mysql/sqlserver + plugin + factory
queue.py durable task queue + QueuePlugin + `ikarem worker`
scheduler.py cron/intervals + SchedulerPlugin
migrations.py versioned migrations + `ikarem migrate`
resources.py app.resource() validated CRUD
blueprints.py prefixed route groups + MethodView (views.py)
middleware.py Middleware base + stack
plugins.py Plugin protocol + dependency-sorted PluginManager
config.py layered Config + NISH config files
errors.py HTTPException hierarchy + handler registry
openapi.py OpenAPI 3.1 builder + /openapi.json + /openapi.nish + /docs
mcp.py routes-as-MCP-tools + resources + stdio server
websocket.py WebSocket disconnects + Room pub/sub + WSRouter
static.py FileResponse + escape-proof static mounts
templating.py Jinja2 via ikarem[jinja] + flashing.py one-shot messages
observability.py logging + request-ID + metrics + /healthz + /readyz + /metrics
cli.py `ikarem run|check|mcp|new|migrate|worker|inspect` (+ audit.py source checks)
testing.py TestClient (cookie jar, all verbs, WS driving — no server needed)
scaffold.py `ikarem new` starter generator
deprecation.py deprecated() upgrade path + meraki_compat.py drop-in shim
tests/ + ledger/tests + cadence/tests + forge/tests + relay/tests 381-test suite (see Verification)
ledger/ + cadence/ + forge/ production showcase apps (finance, habits, workshop OS)
relay/ incident + status hub (28 routes: JWT/RBAC/API keys, SpikeManager ingest lane, nitro rollups, live feed, cron probes, MCP, debug pulse)
examples/basic.py minimal CRUD + DB plugin app
bench/ honest benches (bench_switch.py) + sustained-load proof (load.py)
docs/ COOKBOOK.md (20 runnable recipes) · GUIDE.md (25 chapters) · NISH.md ·
ECOSYSTEM.md (extension registry) · MIGRATING_FROM_*.md (6 frameworks) ·
DEFAULTS.md (every default, stated plainly) · CHATGPT.md (GPT pack) ·
DEPLOY.md · PLUGINS.md · SECURITY.md · PHASE1.md (original spec)
site/ static docs site (no build step) + llms.txt framework manual
Project basics
- Security:
SECURITY.md— supported versions, how to report (GitHub private vulnerability reporting; no public issues for vulns). - Contributing:
CONTRIBUTING.md— setup, per-PR checks, the five agent laws (zero-dep core, tests, no per-request reflection, fix-saying errors, Conventional Commits). - Changelog:
CHANGELOG.md— every release cut from Conventional Commits, Keep-a-Changelog format. - Versioning (SemVer):
1.xkeeps the public API backward-compatible —ikarem/__init__.pyexports, ASGI behavior, CLI commands, response shapes. Minor versions add; breaking changes wait for a major. - Deprecation policy: removals land only in majors, announced at least one
minor earlier via
deprecated(since=, removal=, use_instead=)— upgrades warn with the version, the removal target, and the replacement. - CI: Python 3.10–3.13 × Ubuntu/macOS/Windows, live Postgres, Docker build, scaffold smoke, ruff lint + format.
Author
Built by Nishant Paudel in Nepal. Issues and PRs welcome at
github.com/nishantXnova/IKAREM.
Metadata
Release files for ikarem 1.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ikarem-1.4.0.tar.gz | 157.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ikarem-1.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 280.3 kB
Release files / ikarem-1.4.0.tar.gz
| Download URL | ikarem-1.4.0.tar.gz |
|---|---|
| Size | 157.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2800d287e11cb2e46427a1ed90af580e21982bf3c7a2a1eea05917192f65f065
|
|
BLAKE2b-256 checksum How to use checksums |
2652bac843ae98fe32743ebf353922381e261f1d239421d83f0dd7280677c0f7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency logRelease files / ikarem-1.4.0-py3-none-any.whl
| Download URL | ikarem-1.4.0-py3-none-any.whl |
|---|---|
| Size | 123.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
06f564f70faedcab8cd4cfa954aaede9b87436d74e244ee65e1f5f6ea79f77fc
|
|
BLAKE2b-256 checksum How to use checksums |
98f876700d06064c17b404c713538770aa874abdd0db74e459b2ea52180d817e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency log