Self-hosted Backend-as-a-Service platform. Authentication, database, storage, realtime, serverless functions, AI, scheduled jobs and more — through a single unified API.
| Layer | Technology |
|---|---|
| Runtime | Bun |
| HTTP Framework | Hono |
| Database | PostgreSQL 15 + Kysely |
| Realtime | Bun WebSockets + PostgreSQL LISTEN/NOTIFY |
| Functions | Deno (isolated container) |
| Validation | Zod |
| Auth | JWT (Jose) + Argon2 |
| AI | OpenRouter |
| Storage | Local filesystem or AWS S3 + CloudFront |
| Testing | Vitest |
| Linting | Biome |
syntra/
├── packages/
│ ├── backend/ # Main API server (Bun + Hono)
│ └── shared/ # Shared types, Zod schemas, constants
├── functions/ # Deno serverless functions runtime
├── deploy/
│ └── docker-init/db/ # PostgreSQL init scripts
├── docker-compose.yml # postgres + syntra + deno
└── .env.example
| Dependency | Version | Required |
|---|---|---|
| Bun | >= 1.0 | Yes |
| Docker + Docker Compose | Latest | Yes (for PostgreSQL) |
| PostgreSQL | 15+ with pgvector | Only if not using Docker |
Everything runs in containers. Only Docker is required.
# 1. Clone the repository
git clone https://github.com/MauricioPerera/syntra.git
cd syntra
# 2. Create your environment file
cp .env.example .envGenerate security keys and edit .env:
# Linux/macOS:
openssl rand -base64 32 # copy → JWT_SECRET
openssl rand -base64 32 # copy → ENCRYPTION_KEY
# Windows (PowerShell):
[Convert]::ToBase64String((1..32 | ForEach-Object { Get-Random -Max 256 }) -as [byte[]])Fill in the required values in .env:
JWT_SECRET=<generated-key>
ENCRYPTION_KEY=<generated-key>
ADMIN_EMAIL=[email protected]
ADMIN_PASSWORD=your-secure-password
POSTGRES_PASSWORD=your-db-password# 3. Start all services
docker compose up -d
# 4. Verify
curl http://localhost:7130/api/health
# → {"success":true,"statusCode":200,"data":{"status":"ok",...}}This starts three containers:
| Service | Port | Image | Description |
|---|---|---|---|
| postgres | 5432 | pgvector/pgvector:pg15 |
PostgreSQL 15 + pgvector |
| syntra | 7130 | oven/bun:1-alpine |
API server (auto-installs deps + hot reload) |
| deno | 7133 | denoland/deno:alpine-2.0.6 |
Serverless functions runtime |
The database is automatically initialized with schemas, extensions (pgcrypto, uuid-ossp, vector), roles (anon, authenticated), and all migrations run on first startup.
Run Syntra locally with Bun for faster iteration. Docker only for PostgreSQL.
# 1. Clone and install
git clone https://github.com/MauricioPerera/syntra.git
cd syntra
bun install
# 2. Configure environment
cp .env.example .env
# Edit .env — fill in JWT_SECRET, ENCRYPTION_KEY, ADMIN_EMAIL, ADMIN_PASSWORD, POSTGRES_PASSWORD
# 3. Start PostgreSQL
docker compose up postgres -d
# 4. Wait for PostgreSQL to be ready
docker compose ps # should show "healthy"
# 5. Start Syntra with hot reload
bun run devThe server starts at http://localhost:7130. Migrations run automatically.
For existing PostgreSQL 15+ instances:
# 1. Create database and run init script
psql -U postgres -c "CREATE DATABASE syntra;"
psql -U postgres -d syntra -f deploy/docker-init/db/00-init.sql
# 2. Install pgvector extension (required for AI vector features)
# Ubuntu/Debian:
sudo apt install postgresql-15-pgvector
# macOS:
brew install pgvector
# 3. Clone and install
git clone https://github.com/MauricioPerera/syntra.git
cd syntra
bun install
cp .env.example .env
# 4. Edit .env — point to your PostgreSQL instance
# POSTGRES_HOST=localhost
# POSTGRES_PORT=5432
# POSTGRES_DB=syntra
# POSTGRES_USER=postgres
# POSTGRES_PASSWORD=your-password
# + JWT_SECRET, ENCRYPTION_KEY, ADMIN_EMAIL, ADMIN_PASSWORD
# 5. Start
bun run dev# 1. Install Bun
curl -fsSL https://bun.sh/install | bash
source ~/.bashrc
# 2. Clone and install
git clone https://github.com/MauricioPerera/syntra.git /opt/syntra
cd /opt/syntra
bun install --production
# 3. Configure environment
cp .env.example .env
# Edit .env:
# NODE_ENV=production
# JWT_SECRET=<openssl rand -base64 32>
# ENCRYPTION_KEY=<openssl rand -base64 32>
# [email protected]
# ADMIN_PASSWORD=<strong-password>
# POSTGRES_HOST=<your-db-host>
# POSTGRES_PASSWORD=<your-db-password>
# CORS_ORIGINS=https://yourdomain.com
# 4. Start PostgreSQL (Docker or system service)
docker compose up postgres -d
# 5. Start Syntra
bun run startcat > /etc/systemd/system/syntra.service << 'EOF'
[Unit]
Description=Syntra BaaS
After=network.target postgresql.service
[Service]
Type=simple
WorkingDirectory=/opt/syntra
ExecStart=/root/.bun/bin/bun run start
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable syntra
systemctl start syntracurl http://localhost:7130/api/health# Login as admin
TOKEN=$(curl -s -X POST http://localhost:7130/api/auth/admin/sessions \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"your-password"}' \
| jq -r '.data.accessToken')
# Generate API key
curl -X POST http://localhost:7130/api/secrets/rotate-api-key \
-H "Authorization: Bearer $TOKEN" \
| jq '.data.apiKey'Add to your MCP client config:
{
"mcpServers": {
"syntra": {
"type": "streamable-http",
"url": "http://localhost:7130/api/mcp",
"headers": { "X-API-Key": "ik_your_api_key" }
}
}
}| Client | Config file |
|---|---|
| Claude Code | ~/.claude.json |
| Claude Desktop | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Cursor | .cursor/mcp.json in your project |
- API docs:
http://localhost:7130/api/docs - Health check:
http://localhost:7130/api/health - All 105 MCP tools are auto-discovered by connected AI agents
| Variable | Validation | Description |
|---|---|---|
JWT_SECRET |
min 32 chars | Token signing key |
ADMIN_EMAIL |
valid email | Initial admin user email |
ADMIN_PASSWORD |
min 8 chars | Initial admin password |
POSTGRES_PASSWORD |
non-empty | Database password |
| Variable | Default | Description |
|---|---|---|
PORT |
7130 |
Server port |
NODE_ENV |
development |
development, production, or test |
ENCRYPTION_KEY |
— | AES-256-GCM key for secrets (min 32 chars) |
POSTGRES_HOST |
localhost |
Database host |
POSTGRES_PORT |
5432 |
Database port |
POSTGRES_DB |
syntra |
Database name |
POSTGRES_USER |
postgres |
Database user |
CORS_ORIGINS |
http://localhost:7131 |
Comma-separated allowed origins |
STORAGE_PROVIDER |
local |
local or s3 |
LOCAL_STORAGE_PATH |
./data/storage |
Path for local file storage |
DENO_RUNTIME_URL |
http://localhost:7133 |
Deno functions runtime URL |
LOGS_DIR |
./data/logs |
Log files directory |
| Variable | Description |
|---|---|
S3_BUCKET |
S3-compatible bucket name |
S3_REGION |
S3 region |
S3_ACCESS_KEY |
S3 access key |
S3_SECRET_KEY |
S3 secret key |
S3_ENDPOINT |
Custom endpoint (MinIO, Cloudflare R2, etc.) |
OPENROUTER_API_KEY |
AI features (chat, embeddings, images) |
VERCEL_TOKEN |
Vercel deployment integration |
VERCEL_TEAM_ID |
Vercel team ID |
VERCEL_PROJECT_ID |
Vercel project ID |
DENO_SUBHOSTING_TOKEN |
Deno Deploy subhosting |
DENO_SUBHOSTING_ORG_ID |
Deno Deploy organization |
EMAIL_FROM |
Sender address for emails |
SMTP_HOST |
SMTP server host |
SMTP_PORT |
SMTP port (default: 587) |
SMTP_USER |
SMTP username |
SMTP_PASS |
SMTP password |
All variables are validated with Zod on startup. Missing or invalid required variables produce clear error messages and prevent the server from starting.
Base URL: http://localhost:7130/api
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /users |
— | Register user |
| POST | /sessions |
— | Login |
| POST | /admin/sessions |
— | Admin login |
| POST | /refresh |
Refresh token | Refresh access token |
| POST | /logout |
User | Logout |
| GET | /sessions/current |
User | Get current session |
| POST | /tokens/anon |
Admin | Generate anonymous token |
| PATCH | /profiles/current |
User | Update own profile |
| GET | /profiles/:userId |
User | Get public profile |
| GET | /users |
Admin | List all users |
| GET | /users/:userId |
Admin | Get user by ID |
| DELETE | /users |
Admin | Delete users |
| GET | /config |
Admin | Get auth config |
| PUT | /config |
Admin | Update auth config |
| GET | /public-config |
— | Public auth settings |
| Method | Endpoint | Description |
|---|---|---|
| POST | /email/send-verification |
Send verification email |
| POST | /email/verify |
Verify email with OTP |
| POST | /email/send-reset-password |
Send password reset email |
| POST | /email/reset-password |
Reset password with token |
| Method | Endpoint | Description |
|---|---|---|
| GET | /oauth/providers |
List enabled OAuth providers |
| GET | /oauth/:provider |
Start OAuth flow |
| GET | /oauth/:provider/callback |
OAuth callback |
| POST | /oauth/exchange |
Exchange code for tokens |
| GET | /oauth/configs |
List OAuth configs (admin) |
| POST | /oauth/configs |
Create/update OAuth config (admin) |
| DELETE | /oauth/configs/:provider |
Delete OAuth config (admin) |
Supported providers: Google, GitHub, Discord, Microsoft, LinkedIn, Apple, X (Twitter), Facebook, Instagram, TikTok, Spotify.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /tables |
Admin | List all tables |
| POST | /tables |
Admin | Create table |
| GET | /tables/:name/schema |
Admin | Get table schema |
| PATCH | /tables/:name/schema |
Admin | Alter table |
| DELETE | /tables/:name |
Admin | Drop table |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /records/:table |
User | Query records |
| POST | /records/:table |
User | Insert records |
| PATCH | /records/:table |
User | Update records |
| DELETE | /records/:table |
User | Delete records |
Query parameters:
?select=id,name,email # Column selection
?status=eq.active # Equality
?age=gt.18 # Greater than (also: gte, lt, lte)
?name=like.%John% # Pattern matching
?role=in.(admin,editor) # In list
?order=created_at.desc # Sorting
?limit=10&offset=20 # Pagination
| Method | Endpoint | Description |
|---|---|---|
| POST | /rpc/:functionName |
Execute PostgreSQL function |
| GET | /functions |
List database functions |
| GET | /indexes |
List indexes |
| GET | /policies |
List RLS policies |
| GET | /triggers |
List triggers |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /buckets |
Admin | List buckets |
| POST | /buckets |
Admin | Create bucket |
| GET | /buckets/:id |
Admin | Get bucket |
| PATCH | /buckets/:id |
Admin | Update bucket |
| DELETE | /buckets/:id |
Admin | Delete bucket |
| GET | /objects/:bucket |
User | List objects |
| POST | /objects/:bucket/:key |
User | Upload object |
| GET | /objects/:bucket/:key |
User | Download object |
| DELETE | /objects/:bucket/:key |
User | Delete object |
| POST | /signed/upload |
User | Get signed upload URL |
| POST | /signed/download |
User | Get signed download URL |
| POST | /copy |
User | Copy object |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /channels |
Admin | List channels |
| POST | /channels |
Admin | Create channel |
| GET | /channels/:id |
Admin | Get channel |
| PATCH | /channels/:id |
Admin | Update channel |
| DELETE | /channels/:id |
Admin | Delete channel |
| GET | /channels/:id/messages |
Admin | Message history |
| GET | /channels/:id/stats |
Admin | Channel stats |
| POST | /publish |
User | Publish message |
| GET | /stats |
Admin | Live connection stats |
Connect to ws://localhost:7130/api/realtime?token={jwt}
// Subscribe
{ "type": "subscribe", "channel": "room:123" }
// Publish
{ "type": "publish", "channel": "room:123", "event": "message", "payload": { "text": "hello" } }
// Unsubscribe
{ "type": "unsubscribe", "channel": "room:123" }
// Heartbeat
{ "type": "ping" } → { "type": "pong" }| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /chat |
User | Chat completion (supports streaming) |
| POST | /images |
User | Generate images |
| POST | /embeddings |
User | Generate embeddings |
| GET | /configs |
Admin | List AI configs |
| POST | /configs |
Admin | Create AI config |
| GET | /configs/:id |
Admin | Get config |
| PATCH | /configs/:id |
Admin | Update config |
| DELETE | /configs/:id |
Admin | Delete config |
| GET | /usage |
Admin | Usage stats |
| GET | /usage/list |
Admin | Usage records |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | / |
Admin | List functions |
| POST | / |
Admin | Create function |
| GET | /:id |
Admin | Get function |
| PATCH | /:id |
Admin | Update function |
| DELETE | /:id |
Admin | Delete function |
| POST | /:id/deploy |
Admin | Deploy to Deno runtime |
| GET | /:id/deployments |
Admin | List deployments |
| POST | /:slug/invoke |
User | Invoke function by slug |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | / |
Admin | List scheduled jobs |
| POST | / |
Admin | Create job |
| GET | /:id |
Admin | Get job |
| PATCH | /:id |
Admin | Update job |
| DELETE | /:id |
Admin | Delete job |
| POST | /:id/toggle |
Admin | Toggle active/inactive |
| GET | /:id/logs |
Admin | Execution logs |
Syntra exposes all its capabilities as an MCP server, allowing AI agents (Claude Code, Cursor, etc.) to manage your entire backend through natural language.
Transport: Streamable HTTP (stateless, JSON-RPC over POST)
Configuration:
{
"mcpServers": {
"syntra": {
"type": "streamable-http",
"url": "http://localhost:7130/api/mcp",
"headers": { "X-API-Key": "ik_your_api_key" }
}
}
}105 tools across 10 modules:
| Module | Tools | Examples |
|---|---|---|
| auth | 18 | auth_register, auth_login, auth_list_users, auth_get_config |
| database | 17 | database_create_table, database_select_records, database_execute_sql |
| storage | 12 | storage_create_bucket, storage_upload_object, storage_get_signed_url |
| ai | 16 | ai_chat_completion, ai_generate_embedding, ai_vector_search |
| realtime | 8 | realtime_create_channel, realtime_publish_message |
| functions | 9 | functions_create, functions_deploy, functions_invoke |
| schedules | 7 | schedules_create_job, schedules_toggle_active |
| secrets | 4 | secrets_create, secrets_list, secrets_rotate_api_key |
| system | 11 | system_get_metadata, system_list_modules, system_send_email |
| usage | 3 | usage_get_stats, usage_list_records |
Uses a pre-warmed server pool (10 instances) for low-latency responses (~4ms overhead over direct HTTP).
| Endpoint | Description |
|---|---|
/api/secrets |
Encrypted environment variables management |
/api/email |
Send transactional emails via SMTP |
/api/webhooks |
Webhook management and delivery |
/api/logs |
Application log retrieval |
/api/deployments |
Vercel deployment integration |
/api/metadata |
Project metadata |
/api/usage |
Usage metrics |
/api/docs |
API documentation |
/api/health |
Health check |
Seven PostgreSQL schemas for logical separation:
auth → users, user_providers, configs, oauth_configs, email_otps
system → secrets, audit_logs, deployments, mcp_usage
ai → configs, usage
storage → buckets, objects
functions → definitions, deployments
realtime → channels, messages
schedules → jobs, job_logs
Key features:
- Row Level Security on
auth.userswith helper functionsauth.uid(),auth.role(),auth.email() - Auto-updating
updated_atvia triggers pg_notify('realtime_message')for realtime integration- Migrations auto-run on server startup
| Token | Lifetime | Delivery |
|---|---|---|
| Access token | 15 minutes | Response body |
| Refresh token | 7 days | httpOnly cookie (web) or response body (mobile) |
| CSRF token | Tied to refresh | Response body |
| Anonymous token | 24 hours | Response body |
| API key | 365 days | Response body |
- Client generates
code_verifierandcode_challenge GET /api/auth/oauth/:providerreturns authorization URL- User authorizes with provider
- Provider redirects with authorization code
POST /api/auth/oauth/exchangeexchanges code + verifier for tokens
- Passwords — Argon2id (memory-hard)
- Secrets — AES-256-GCM encryption at rest
- JWT — HMAC-SHA256 signing
- SQL Injection — Parameterized queries via Kysely
- Rate Limiting — Per-endpoint limits on auth and email
- CSRF — Token derived from refresh token hash
- RLS — PostgreSQL row-level security policies
- Table Names — Regex whitelist validation
- Storage — Signed URLs for temporary access, public/private bucket separation
bun run dev # Start backend with hot reload
bun run build # Build all packages
bun run test # Run tests (Vitest)
bun run typecheck # TypeScript type checking
bun run lint # Biome lint check
bun run lint:fix # Biome lint auto-fix
bun run bench # Run all benchmarks
bun run bench:http # HTTP endpoint benchmarks
bun run bench:mcp # MCP protocol benchmarks
bun run bench:internals # In-process crypto/JWT/Zod benchmarks
bun run bench:stress # Progressive load / stress testSyntra ships with Claude Code Skills — structured guides that help AI agents work with your backend more effectively. Skills are in .claude/skills/ and auto-load when connected via MCP.
| Skill | Description |
|---|---|
syntra-setup |
Initialize and configure a Syntra backend, verify connection, bootstrap a project |
syntra-db |
Database workflows: schema design, CRUD, filters, raw SQL, migrations |
syntra-auth |
Authentication: users, OAuth providers, OTP, email verification, password policies |
syntra-storage |
File storage: buckets, uploads, downloads, signed URLs, S3 compatibility |
syntra-ai |
AI features: chat completions, image generation, embeddings, vector search (RAG) |
syntra-functions |
Edge functions on Deno runtime + scheduled cron jobs |
syntra-status |
System diagnostics: health checks, audit logs, usage stats, troubleshooting |
Copy the .claude/skills/ directory into your project:
# From the Syntra repo
cp -r .claude/skills/syntra-* /path/to/your-project/.claude/skills/Or install individual skills:
cp -r .claude/skills/syntra-db /path/to/your-project/.claude/skills/Skills work with Claude Code, Cursor, and any MCP-compatible agent.
Apache-2.0