A self-hostable, single-binary backend for web, mobile, and real-time apps.
pylon gives you what Convex / Firebase / Supabase do — declarative schema,
real-time sync, server functions, auth, file storage — but as a single Rust
binary you can scp to a VPS or run with SQLite or Postgres.
# Install
curl -fsSL https://pylonsync.com/install.sh | bash
# New project
pylon init my-app
cd my-app
# Dev server with live reload
pylon devVisit http://localhost:4321/studio for the inspector.
- Declarative schema in JSON or DSL → tables, types, OpenAPI, client types
- Real-time sync — clients see updates as they happen (WebSocket + SSE)
- TypeScript functions —
mutation/query/actionwith typedctx.db- Handler IS the transaction (atomic by default)
- Streaming responses for AI chat / live data
- Auth — sessions, magic codes, OAuth (Google + GitHub), RBAC
- Real-time shards for multiplayer games & collab apps
- Tick-driven simulations
- Matchmaker, area-of-interest, replay
- Background jobs + cron scheduler
- Workflows — long-running, durable
- File storage, email, rate limiting, policies, plugins
| pylon | Convex | Supabase | Firebase | |
|---|---|---|---|---|
| Self-host | ✅ single binary | ✅ docker-compose | ✅ multi-service | ❌ |
| Deploy targets | managed cloud, self-host, AWS, Workers (experimental) | their cloud or self-host | their cloud, self-host, k8s | their cloud only |
| Real-time sync | ✅ | ✅ reactive | ✅ Realtime | ✅ |
| Server functions | ✅ TypeScript | ✅ TypeScript | ✅ Edge Functions (Deno) | ✅ Cloud Functions |
| Game shards | ✅ tick-based | ❌ | ❌ | ❌ |
| Built on | Rust + SQLite | Rust + custom db | PG + Go + Deno | proprietary |
| Single process | ✅ | ❌ | ❌ | n/a |
curl -fsSL https://pylonsync.com/install.sh | bashOther install paths:
# Homebrew (macOS + Linux)
brew install pylonsync/tap/pylon
# Cargo (compiles from source)
cargo install pylon-cli
# Docker
docker pull ghcr.io/pylonsync/pylon:latestpylon.manifest.json:
{
"manifest_version": 1,
"name": "todos",
"version": "0.1.0",
"entities": [
{ "name": "Todo", "fields": [
{ "name": "title", "type": "string", "optional": false, "unique": false },
{ "name": "done", "type": "bool", "optional": false, "unique": false }
], "indexes": [], "relations": [] }
],
"routes": [], "queries": [], "actions": [], "policies": []
}pylon devimport { init, db } from "@pylon/react";
init({ baseUrl: "http://localhost:4321" });
function TodoList() {
const { data: todos } = db.useQuery("Todo");
const { mutate: add } = db.useMutation("createTodo");
return (
<>
{todos.map(t => <li key={t.id}>{t.title}</li>)}
<button onClick={() => add({ title: "New todo" })}>Add</button>
</>
);
}functions/createTodo.ts:
import { mutation, v } from "@pylon/functions";
export default mutation({
args: { title: v.string() },
async handler(ctx, args) {
const id = await ctx.db.insert("Todo", {
title: args.title,
done: false,
authorId: ctx.auth.userId,
});
return { id };
},
});use pylon_realtime::{Shard, ShardConfig, SimState};
struct MyGame { /* state */ }
impl SimState for MyGame { /* tick, snapshot, apply_input */ }
let shard = Shard::new("match_1", MyGame::default(), ShardConfig {
tick_rate_hz: 20,
..Default::default()
});Then connect from the client:
import { useShard } from "@pylon/react";
const { snapshot, send } = useShard("match_1", { subscriberId: "player_42" });pylon/
├── crates/
│ ├── core/ Shared types, error codes, utilities
│ ├── http/ Platform-agnostic HTTP types + DataStore trait
│ ├── runtime/ SQLite-backed dev/prod server
│ ├── router/ HTTP routing logic, reused across platforms
│ ├── workers/ Cloudflare Workers adapter (experimental)
│ ├── functions/ Rust side of the TypeScript function runtime
│ ├── realtime/ Sharded game/collab server primitives
│ ├── auth/ Sessions, magic codes, OAuth, RBAC
│ ├── policy/ Access control rules engine
│ ├── sync/ Change log + push/pull
│ ├── storage/ SQLite + Postgres backends, file storage
│ ├── plugin/ Built-in plugins (cache, webhooks, soft delete, ...)
│ ├── migrate/ Schema migration diff engine
│ ├── cli/ The `pylon` binary
│ └── ...
└── packages/
├── sdk/ Schema DSL + manifest builder
├── react/ React hooks + typed client
├── react-native/ RN hooks + offline storage
├── next/ Next.js integration
├── swift/ Swift SDK (iOS, macOS, Linux) — sync, realtime, SwiftUI
├── functions/ Function definitions + Bun runtime
├── sync/ Sync engine (optimistic + offline-capable)
├── workflows/ Durable workflow runner
└── create-pylon/ Project scaffolder
All configuration is via environment variables. See crates/runtime/src/config.rs.
Common settings:
PYLON_PORT=4321
PYLON_DB_PATH=/var/lib/pylon/pylon.db
PYLON_FILES_DIR=/var/lib/pylon/uploads
PYLON_SESSION_DB=/var/lib/pylon/sessions.db
PYLON_ADMIN_TOKEN=<long random>
PYLON_CORS_ORIGIN=https://your-app.com
PYLON_DEV_MODE=false- Managed cloud: cloud.pylonsync.com — same binary, hosted; sign up and
pylon deploy - Self-host:
curl … | bashordocker run— see docs/ops/DEPLOY.md - AWS ECS: see
deploy/terraform/anddeploy/sst/ - Cloudflare Workers: see
crates/workers/README.md(experimental)
Architecture docs:
- RUNTIME.md — how TypeScript functions execute, what JS engine, what we evaluated
- SYNC.md — sync semantics, CRDT-backed rows, offline behavior
- ARCHITECTURE.md — crate-by-crate map of the system
Operational docs:
- DEPLOY.md — env vars, reverse proxy, health checks
- SIZING.md — measured throughput, capacity planning
- TOKEN_ROTATION.md — admin token rotation
- INCIDENT.md — incident response playbook
- WORKERS_COSTS.md — cost patterns on Cloudflare
Pre-1.0. API is stable enough to build with but may evolve. SQLite is
the default backend and Postgres is supported for deployments that need an
external database or horizontal database operations. Cloudflare Workers / D1
is experimental — see SECURITY.md for a list of pre-1.0 hardening gaps.
See CONTRIBUTING.md. Bug reports and PRs welcome.
See SECURITY.md for vulnerability reporting and hardening notes. Do not file security issues publicly. Email security@pylonsync.com.
Dual-licensed under MIT or Apache-2.0 at your option.