FastAPI engine, Caspian workflow
Run native async Python with FastAPI dependencies and middleware, then expose route-local work through typed RPC, streaming responses, uploads, and redirects.
A full-stack framework that combines FastAPI, Python-only routing, focused components, and PulsePoint reactivity in one application model.
from casp.component_decorator import html from src.components.TodoList import TodoList async def page(): todos = await load_todos() return html(r""" <main class="space-y-6"> <x-todo-list items="{{ todos | json }}" /> </main> """, todos=todos)
One application model
Python owns routing, data, actions, and reusable UI. PulsePoint adds reactive browser behavior without introducing a separate frontend application.
Run native async Python with FastAPI dependencies and middleware, then expose route-local work through typed RPC, streaming responses, uploads, and redirects.
State, effects, refs, context, portals, transitions, optimistic updates, error boundaries, and SPA navigation.
Create src/app/users/index.py. Nested layout.py modules compose the surrounding shell.
Enable Prisma to generate an async Python ORM for route loaders, RPC actions, uploads, auth flows, and shared services.
async def page(): users = await prisma.user.find_many() return html(markup, users=users)
RPC for request/response work, generator streams for progressive output, multipart RPC for uploads, and self-healing named sockets for persistent bidirectional channels.
PPIcons
Bring Lucide-based icons into the project as native Python components. They participate in real imports, prop forwarding, class merging, and the same x-* composition system as your UI.
Add accessible Python components to your own source tree, import them explicitly, and adapt every detail. The result stays readable, local, and native to Caspian.
✔ Button → src/lib/maddex/Button.py
✔ Accordion → src/lib/maddex/Accordion.py
from casp.component_decorator import component, html from casp.html_attrs import get_attributes @component def Button(variant="default", **props): attributes = get_attributes( {"type": "button", "class": styles[variant]}, props, ) return html(r""" <button {{ attributes }}>{{ children | safe }}</button> """, attributes=attributes)
Live preview
Component-first authoring
Pages remain short assemblies. Each substantial header, toolbar, form, table, panel, or repeated card becomes a focused Python component — one native root when it takes props, or a fragment when it does not.
from casp.component_decorator import component, html from casp.html_attrs import get_attributes @component def StatusCard(label="", value="", **props): attributes = get_attributes( {"label": label, "value": value}, props, ) return html(r""" <article {{ attributes }}> <p>{cardLabel}</p> <strong>{cardValue}</strong> <script> const cardLabel = pp.props.label ?? ""; const cardValue = pp.props.value ?? ""; </script> </article> """, attributes=attributes)
Keep Python parameters, server interpolation, readable HTML, and the owning PulsePoint script together. Use real imports for every child x-* tag.
Forward browser-facing props with get_attributes(...) so the native root exposes them to pp.props, then read them in the owned script — props are never bare identifiers in markup.
Live usage
<x-status-card label="Users" value="42" />Call declared Python actions from component events. Caspian filters payload keys, applies auth and rate policy, serializes results, and supports normal, multipart, and streamed responses.
from casp.rpc import rpc @rpc(require_auth=True, limits="30/minute") async def like_post(post_id: str): post = await prisma.post.update( where={"id": post_id}, data={"likes": {"increment": 1}}, ) return {"likes": post.likes}
<button onclick="likePost()"> Likes: {likes} </button> <script> const [likes, setLikes] = pp.state(0); async function likePost() { const result = await pp.rpc( "like_post", { post_id: "123" }, { abortPrevious: true } ); setLikes(result.likes); } </script>
Start with the core, then enable the capabilities your application needs in caspian.config.json. They join the same runtime, project structure, and verification workflow.
"mcp": trueMount an app-owned FastMCP server at /mcp, share the FastAPI lifespan, and protect production access with MCP_AUTH_TOKEN.
➜ fastmcp run src/lib/mcp/fastmcp.json
✔ tools mounted → /mcp
"websocket": trueDefine authenticated Python @socket() handlers and connect through pp.socket(). Connections heartbeat and reconnect on their own after a drop, and pools support rooms, broadcasts, presence, chat, and collaboration.
npm run test runs Pyright, Ruff, authored-template validation, and Pytest in one deterministic application gate.
pyright — pass
ruff — pass
templates — pass
pytest — pass
✓ all checks passed
npm run format formats component markup with djLint, then Python with Ruff. Every markup rewrite is checked against the original and written only when it is guaranteed to render identically — unproven blocks are skipped and reported.
markup — 418 blocks formatted
python — 37 files reformatted
⚠ 12 skipped — would change rendering
a skip is a result, not a failure
PulsePoint errors, uncaught exceptions, interaction failures, and clean page loads feed a route-aware browser log. npm run logs reports what is clean, unresolved, or still needs an interaction recheck.
LIVE dev session active
✕ /dashboard — total is not defined
/ — clean
routes not listed were never opened
Pre-render eligible routes and public assets into deployable static HTML, including declared paths for dynamic routes.
✔ /docs → static/docs/index.html
Centralized sessions, OAuth providers, redirects, RBAC, route privacy, RPC authorization, secure headers, CSRF, rate limits, and safe public-file delivery.
✔ fail-closed production defaults
A deliberate workflow
Caspian keeps project generation, local development, framework updates, production builds, static export, and application checks discoverable through one project surface.
Start with installation, then follow the feature guides for your project.
caspian-native wraps your existing
Python, HTML, and PulsePoint application with Tauri 2 and the operating
system WebView. It preserves the Caspian programming model; it does not
translate your interface into operating-system widgets.
$ pip install caspian-native $ caspian-native init --identifier com.example.app \ --windows --android $ caspian-native dev --target windows $ caspian-native dev --target android
Embedded or remote
Bundle the production Python server as a sidecar, or point the WebView at a deployed Caspian backend.
Remote production backend
Develop locally through ADB and BrowserSync, then ship a thin client connected to your deployed Caspian server.
Ship it
The runtime contract is short: build the assets into the image, bind
0.0.0.0 on
$PORT, pass
--proxy-headers, and point the health
check at /health. Everything after that
is your platform's choice.
FROM python:3.14-slim # ... uv sync, npm ci # Compiles the stylesheet and the route index. RUN npm run build ENV APP_ENV=production PORT=8080 EXPOSE 8080 CMD uvicorn main:app --host 0.0.0.0 \ --port $PORT --proxy-headers
Only an explicit development value relaxes the session secret, secure
cookies, and error detail. Forgetting
APP_ENV on a host is safe — it
counts as production, and a missing secret refuses to boot.
npm run static renders every static
route through the real app into a folder any CDN will serve — the
right call for docs, marketing, and landing pages.
Recommended host
Apps and databases as isolated Docker services on AWS — and it recognizes Caspian directly. Connect a repository with no Dockerfile and it generates a matching one from your project files. A Dockerfile you commit always wins.
$ npm install -g dilnaka-cli $ dilnaka login $ dilnaka link --create $ dilnaka up
One thing the container will not keep for you: uploaded files. See where those belong.
Every push builds and redeploys, with watch paths, manual releases, and one-click rollback to a past commit.
Postgres, MySQL, MariaDB, Redis, or MongoDB, with credentials injected straight into the app and scheduled backups.
Your own domain over a certificate issued automatically. Containers are reached only through the managed proxy, never the open internet.
Build logs, runtime output, request logs, and resource pressure per service — with usage tracked before it becomes an overage.
Files that outlive the container
Writing to public/uploads/** is the
right default in development and data loss in production — a container
filesystem does not survive a redeploy, and two replicas never share one.
Keep the bytes in object storage and the metadata in your database.
Recommended storage
A secure-by-default S3 platform: zero-config buckets, IAM policies generated
per request, and a typed Python SDK that drops straight into a route-owned
@rpc().
from dilnaka import Dilnaka client = Dilnaka() @rpc(require_auth=True) async def upload_avatar(avatar): uploaded = client.upload(path, folder="avatars") access = client.get_file_access_url(uploaded.id, expires_in=1200) return "url": access.url
The SDK gets a short-lived presigned URL, never credentials.
Large files stream from disk in parts and retry a failed one.
Share a file or a folder for 5 minutes or 30 days, revocable.
Rotating a key is not a migration. On paid plans each API key owns an isolated file store; revoking a key keeps every file and hands the store to its replacement.