Kirak serves your whole backend — a full REST API with authentication and row-level security — live from a few declarative JSON files, with nothing generated into your repo. You write real Python only for the logic that's unique to your app.
● Kirak is open source under Apache License 2.0
{ "schema": { "customer_id": { "type": "uuid", "required": true }, "total_cents": { "type": "integer", "required": true }, "status": { "type": "string", "enum": ["pending", "paid", "shipped"] } }, "access": { "fetch": [{ "role": "user", "condition": "customer_id = {user_id}" }], "create": [{ "role": "user", "condition": "customer_id = {user_id}" }] } }
One file per model — schema and access, deny by default, row-level condition auto-injected on create.
{ "modules": ["payments", "storage", "ai", "mcp"], "database": { "type": "postgres", "name": "shop" }, "payments": { "default_provider": "stripe", "providers": { "stripe": { "type": "stripe" } } }, "storage": { "default_provider": "files", "providers": { "files": { "type": "aws", "aws_bucket": "my-bucket" } } } }
Turn modules on and name your providers. Lazy-loaded — ship nothing you don't enable. Only secrets go in .env; every other setting lives here.
# registered inside on_kirak_ready(kirak) @kirak.on("orders").hook("before_create") def apply_deposit(payload): payload["data"]["deposit_cents"] = payload["data"]["total_cents"] * 20 // 100 return payload
The one layer that's real, owned code — scaffolded for you.
# deposit_cents set by apply_deposit() { "total_cents": 4000, "deposit_cents": 800 }
Built for AI-first workflows. Every project ships with AGENTS.md, JSON Schemas, kirak validate, and a dev MCP server, so your Claude Code, Cursor, or Codex agent writes ~20 lines of Kirak declaration instead of ~2,000 lines of boilerplate backend code — and checks it before it runs.
The problem isn't that AI writes bad code — it's that the backend it writes fails in ways you don't see until production: a missing access check, a dropped constraint, a webhook with no trace. Here's where it shows up first.
Past a few thousand lines, the model loses the whole system — its context fills with stale code and old signatures. The result: prompt loops that revert yesterday's fix, additive overwrites instead of edits, and hallucinated dependencies to get unblocked fast.
AI writes the happy path. Security lives in the edge cases it skips. The result: broken access control — an endpoint like /api/user/123 that never checks who's asking — exposed secrets on default permissions, and injection flaws from unparameterized queries.
AI adds. It rarely removes, and almost never refactors. The result: migrations that drop a constraint instead of altering it, five versions of fetchUser(), and frontend, API, and database quietly drifting apart.
Nobody wrote it line by line, so nobody can debug it line by line — invisible until real load hits. The result: a black-box wall when a webhook drops with no trace, and the rewrite verdict engineers reach for over patching it.
This isn't a prompting problem. It's what happens when AI regenerates a backend — the same CRUD, auth, and schema plumbing every app needs. Kirak's answer: that layer isn't generated, it's the runtime. The model files in models/ hold the schema and access rules; the agent appends to a declaration instead of rewriting code. What's left is a few hundred lines of hooks — small enough to read.
You write a few JSON files and, when something is genuinely custom, a hook. Kirak — the runtime — does everything from there: serves it, secures it, and shows you how it's running in production.
Fields, relationships, authentication, and row-level access go in one JSON file per model under models/. Modules — payments, storage, notifications, AI, vector, MCP — turn on in kirak.json.
{ "schema": { "vehicle_id": {"type":"uuid"}, "starts_at": {"type":"datetime"}, "ends_at": {"type":"datetime"} }, "access": { "fetch": [{"role":"admin"}, {"role":"user","condition":"renter_id = {user_id}"}], "destroy": [{"role":"admin"}] } }
Kirak reads the declaration and serves CRUD, auth, and RLS live — the same tested implementation across every project. Nothing is written to your repo.
Lifecycle hooks and custom endpoints are the one place you write code. Sync hooks form a blocking pipeline — each one takes the request, modifies it, and feeds it to the next hook or the operation, and can stop it by raising. Async hooks are non-blocking — for side effects like notifications, where a failure must never fail the request. It all runs inside the runtime's access rules, so you extend the backend without opening a hole in it. Commit it to your repo; it survives every later change to your models.
# sync hooks are a blocking pipeline: each takes the request, changes it, # and hands it to the next hook or the operation -- or stops it by raising @kirak.on("booking").hook("before_create") def check_and_price(payload): data = payload["data"] if data["ends_at"] <= data["starts_at"]: raise ValidationError("ends_at must be after starts_at") # 400 data["deposit_cents"] = data["total_cents"] * 20 // 100 return payload # async hooks are non-blocking: a failure or timeout is logged, never fails the request @kirak.on("booking").hook("after_create") async def notify_owner(result): await kirak.notifications.send_email({"to": OWNER, "template_name": "new_booking"}) return result
The runtime instruments itself — requests, errors, and a trace waterfall across your hooks, the data layer, and outbound calls. You debug by reading the timeline, not by adding print statements. It's served from the runtime's own endpoints, in the open-source release — no third-party APM.
POST /booking 142ms · 201 auth.verify ▓ 8ms hook: check_availability ░▓▓▓▓▓ 41ms └ db.overlaps (booking) ░▓▓▓▓ 28ms db.insert (booking) ░░░░░░▓▓▓ 19ms stripe.charge ░░░░░░░░░▓▓▓▓▓▓▓▓ 61ms
Illustrative — measured on a mid-size SaaS backend: ~22 models, RBAC on every endpoint, Stripe subscriptions, file uploads, scheduled jobs. Counts vary by app and stack; the ratio is the point.
Kirak is a runtime you install, not a code generator. Declare your data model, auth, and permissions in JSON model files, and Kirak serves the full REST API, including CRUD, auth, and RLS. No generated code, no files to maintain, no drift. Just one tested, versioned, open source runtime, with real code only for logic unique to your app.
That's not just less code — it's safer code. A smaller surface means less for a person or an agent to get wrong, and what's left runs through Kirak's data layer, so it's held to the same access rules as everything else.
Supabase is built for JavaScript and database-level logic — SQL policies, Edge Functions on Deno. Kirak brings that same declare-and-go speed natively to Python: your JSON model files directly serve the API with full row- and column-level security, your custom logic is FastAPI you own, and there's no separate runtime or generated clutter to maintain.
| Supabase | Kirak | |
|---|---|---|
| API layer | PostgREST auto-generates a REST/GraphQL API from your schema — you still design and maintain the schema and migrations | The runtime serves a full REST API straight from your JSON model files — nothing generated into your repo |
| Access control | Row-Level Security policies, written in SQL — enforced by Postgres on every query | Access rules, written once as JSON in each model file instead of SQL — enforced by the runtime on every query |
| Custom server logic | Edge Functions — TypeScript, running on Deno, a separate runtime from your database | Hooks and custom endpoints — real Python/FastAPI, running inside the same runtime as the rest of your backend |
| Database | Postgres only | Postgres or MySQL — switch with one config line |
| Payments | Not built in — wire your own Stripe integration via Edge Functions | Native: 12 gateways — Stripe, PayPal, Paddle, Razorpay, Square, Paystack, Flutterwave, Mercado Pago, and more — with subscriptions, refunds, saved cards, and disputes |
| Notifications | Auth-only email templates; SMS tied to phone login via a configured provider — no push, no in-app inbox | Native, multi-channel: email, SMS, push, Slack / Discord / Telegram, outgoing webhooks, and an in-app notification inbox — out of the box |
| AI agents | No agent runtime — call an LLM yourself from an Edge Function | Declarative agents in JSON, using your API's operations as tools under the caller's access rules — with human approval, cost caps, and run history |
| Vector & RAG | pgvector in Postgres — you write the embedding and retrieval code | Vector module — Pinecone or Amazon S3 Vectors, with OpenAI, Gemini, or Ollama embeddings; agents retrieve through it as a tool |
| MCP | An MCP server for managing your Supabase project from AI dev tools | MCP servers for your own API — one JSON file each, choosing the tools, who may connect, and a rate limit; every call runs under your access rules |
| Caching | CDN caching for Storage assets only | Native Redis caching for application data and queries |
| Monitoring | Logs & Analytics, gated on Pro+ plans | Analytics, logs, and traces — built into the open-source runtime itself, not gated behind a paid tier |
Describe what you're building, and the AI agent writes your models, kirak.json, hooks, and endpoints, builds the codebase, and deploys it to a VPS in one click. Same runtime underneath — Studio is the managed way to run it, and a Studio project always checks out to your own GitHub. It's in final development now; join the waitlist and we'll email you the day it opens.
You're on the list — we'll email you the day Studio opens.
No spam. Unsubscribe anytime.
Kirak isn't a starter kit you fill in. It reads your model files and kirak.json and serves the whole backend — the same implementation behind every project.
agents/*.json that use your API's operations — and MCP servers — as tools, running as the caller under your access rules. Human approval before sensitive tools, cost caps, conversations, streaming, structured output, and full run history. Bring your own key; built on pydantic-ai, so any model it supports works.mcp/*.json — one per audience, each choosing the operations it offers as tools, who may connect, and a rate limit. Any MCP client can connect, and every tool call runs as the caller, under your access rules. Nothing is exposed until you declare it.kirak new writes an AGENTS.md and JSON Schemas for every file; kirak validate checks each edit before the app starts; kirak catalog --json answers facts instead of guessing; kirak dev mcp is a dev MCP server for your coding agent; and GET /docs describes the running API in Markdown.MySQL or PostgreSQL, switched with one config line — not tied to a single vendor.
Security controls built to help the APIs you serve pass a SOC 2 review.
Fork it, self-host it, or take a Studio project's checked-out code with you — nothing here requires staying.
Kirak is open source and always free. Kirak Studio, the managed build-and-deploy service, is in final development — join the waitlist to get notified the day it opens.
You're on the list — we'll email you the day Studio opens.
Studio is in final development. Join the waitlist above and we'll email you the moment it opens — no spam.
Yes — the Kirak runtime is genuine open source, with no restrictions on how you use it, including self-hosting and commercial use. Kirak Studio is the paid managed service built on top.
No. The CRUD, auth, RLS, payments, and storage aren't code in your project — the runtime serves them from your JSON model files. The only code in your repo is your hooks/ and custom endpoints: real Python you write and own, typically a few hundred lines.
Your hooks and endpoints read and write through Kirak's data layer, so the authentication and row-level access rules you declared in your model files are enforced on your custom code automatically. You're writing business logic, not access control.
Kirak itself is not SOC 2 compliant today. The runtime is built to help the APIs it serves pass a SOC 2 review — a design goal for what you build, not a certification Kirak currently holds.
Yes — Kirak is database-agnostic. Switch between MySQL and PostgreSQL with a single config change.
Yes, today, via manual MCP configuration — declare an MCP server in mcp/ with the tools the builder may use, then point the tool at /mcp/<name>/ the way you would any other MCP server. It isn't yet a built-in default option inside those tools.