Skip to main content

FastAPI scaffold for invite-only Python apps — auth, admin dashboard, signup approval, and one-command fly.io deploy. Also installable as an MCP server so AI coding agents can scaffold and ship the whole app via tool calls.

Project description

parbaked — agent-native FastAPI scaffold for invite-only Python apps

A FastAPI starter template with auth, admin dashboard, signup approval workflow, and one-command fly.io deploy — built so an AI coding agent can scaffold and ship the whole app via MCP tool calls.

PyPI License: MIT Python 3.11+ MCP

Signup with admin approval, JWT sessions, rate limiting, magic-link admin login, SQLite-on-fly-volume persistence, and a published off-boarding contract — set up in two commands. Ships as both a Python package (pip install parbaked) and an MCP server (claude mcp add parbaked -- uvx parbaked mcp) so the same kernel works whether you're writing code by hand or having an agent build it.

Why exists: if you ship PoCs on fly.io, two things bite you — anyone on the internet can spin up accounts in a loop (and run up your bill), and you write the same auth + admin boilerplate every time. parbaked is the slice between "I have an idea" and "this is safe to put online."

Searchable terms: FastAPI scaffold, FastAPI starter template, FastAPI boilerplate, Python admin dashboard, FastAPI invite-only auth, FastAPI fly.io deploy, MCP server FastAPI, Python web app scaffold, FastAPI authentication template, Python SaaS starter, FastAPI with admin panel, FastAPI signup admin approval.

30-second quickstart

uv tool install parbaked
parbaked new myapp
cd myapp
parbaked dev

What you get

  • Signup + login + JWT sessions
  • Admin-approval gate (nobody gets in until you click approve)
  • Built-in admin dashboard with pending/active/rejected queues
  • Per-IP rate limiting (5/min signup, 10/min login by default)
  • SQLite DB on a fly volume (persists across machine restarts)
  • Secrets auto-generated on first run, never committed

parbaked new scaffolds the project and runs uv sync so the first parbaked dev is instant. Open http://localhost:8000/auth/admin and paste the password from the terminal banner. The admin dashboard, signup/login REST API, and rate-limit middleware are already wired. Drop more .py files in routes/ and they auto-mount.

Once you set PARBAKED_ADMIN_EMAIL, the admin login form switches to a one-time magic-link flow that mails sign-in links to that inbox — no shared password to remember. See Admin auth.

What parbaked new scaffolds

myapp/
├── parbaked.toml
├── routes/
│   └── __init__.py           # add .py files here, they auto-mount
├── models.py
├── web/
│   └── index.html            # vanilla-JS placeholder — replace freely
├── pyproject.toml
├── .env.example
├── .gitignore
└── README.md

No main.py. No Dockerfile. No fly.toml. The kernel runtime owns the entrypoint; parbaked deploy generates the deploy targets from parbaked.toml. If you ever want to take ownership, parbaked eject hands you everything (see below).

Adding routes

Drop a .py file in routes/ with a module-level router:

# routes/notes.py
from fastapi import APIRouter, Depends
from parbaked import current_user

router = APIRouter()

@router.get("/")
def list_notes(user = Depends(current_user)):
    return {"user": user.email, "notes": []}

Restart the dev server (or wait for hot reload) and GET /notes/ returns your handler's response. The auto-discovery rule: file path becomes URL prefix. routes/api/users.py mounts at /api/users. routes/index.py mounts at /.

Shipping to fly.io

First-time setup — one command walks you through installing flyctl (if needed), signing in, claiming the app, pushing secrets, and deploying:

parbaked init

parbaked init generates a globally-unique fly app name from your parbaked.toml (<app-name>-<5-char-suffix> — bypasses fly's "name taken" wall on common project names), claims it under your personal org, pushes .env / auto-generated secrets via fly secrets set, then runs the first deploy. The chosen fly app name + first-deploy timestamp are cached to .parbaked/deploy.json, so subsequent redeploys are zero-prompt:

parbaked deploy

That regenerates Dockerfile + fly.toml into .parbaked/build/ from your parbaked.toml, creates the SQLite volume if it doesn't exist, and runs fly deploy against the cached app. The generated fly.toml ships with cost-protection defaults — auto_stop_machines = "stop", min_machines_running = 0, max_machines_running = 2 — so an idle deploy costs nothing and a traffic spike can't autoscale you into a bill.

Other helpers:

parbaked logs       # tail fly logs for the cached app
parbaked secrets    # push .env entries to fly secrets
parbaked tunnel     # cloudflared quick tunnel for sharing localhost
parbaked destroy    # permanently destroy the fly app (asks for confirmation)

parbaked init is for the first deploy of a given project. After that, parbaked deploy is the only one-shot you need; the rest are situational. You shouldn't need to run raw fly commands directly — if you do, file an issue.

Off-boarding contract

Every byte parbaked owns is in a public, importable format:

  • Password hashes: bcrypt 2b, cost 12 — verifiable with any bcrypt-compliant library.
  • Session tokens: RFC 7519 JWT, HS256 — decodable on jwt.io or any stdlib.
  • Users table: documented column-by-column in docs/data-format-guarantees.md. Additive changes only between major versions.
  • Audit events: one JSON object per stdout line — ships to any log aggregator.

When you outgrow parbaked, run parbaked eject. You get a parbaked-export/ directory: PostgreSQL-compatible schema.sql, CSV data dumps, the generated Dockerfile + fly.toml, an .env.example listing the env-var contract, and a MANIFEST.md pointing back at the format docs. No vendor-specific decoders. No lobster trap.

Every release adds a section to CHANGELOG.md, and breaking changes ship a migration recipe there.

Configuration

Everything has sensible defaults. Set in parbaked.toml (non-secret) or via PARBAKED_* env vars (secrets):

Env var Default What it does
PARBAKED_JWT_SECRET (auto-generated) Session-token signing key. Required in prod.
PARBAKED_APPROVAL_TOKEN_SECRET (auto-generated) Magic-link signing key. Required in prod.
PARBAKED_ADMIN_PASSWORD (auto-generated) Dashboard login password (no-email mode only — see Admin auth). Ignored when PARBAKED_ADMIN_EMAIL is set.
PARBAKED_ADMIN_EMAIL unset The admin's email address. When set, the admin login form switches to a one-time magic-link flow AND signup-approval emails go to this inbox.
PARBAKED_APP_NAME "My App" Used in email subjects.
PARBAKED_APP_URL http://localhost:8000 Public URL for magic links.
PARBAKED_RESEND_KEY Set this to send real email via Resend. Unset → emails print to stdout. Get a free key (no card) at https://resend.com/api-keys or run parbaked email setup.
PARBAKED_MAIL_FROM onboarding@resend.dev From address. Default is Resend's sandbox. For real users, verify your domain at resend.com/domains.
PARBAKED_RATELIMIT_SIGNUP 5/minute Per-IP signup limit.
PARBAKED_RATELIMIT_LOGIN 10/minute Per-IP login limit.
PARBAKED_DATABASE_URL sqlite:///./parbaked.db SQLite-only. Standard SQLAlchemy URL.

Auto-generated secrets get persisted to .parbaked.json (chmod 600). In production, set them as env vars instead.

Admin auth

parbaked has one admin. There are two ways to sign them in, picked per-mode (no hybrids):

  • Email mode (PARBAKED_ADMIN_EMAIL is set) — the admin login form asks for the admin email. Submitting the matching address mails a 15-minute magic link to that inbox. Click → admin session cookie set → dashboard. The admin password is unused. Approve / reject buttons in the approval emails still work as before.
  • No-email mode (PARBAKED_ADMIN_EMAIL is unset) — the form asks for the shared PARBAKED_ADMIN_PASSWORD (printed in the boot banner in dev, set as a fly secret in prod). No magic links because there's no transport.

Flipping modes is a single env var: add PARBAKED_ADMIN_EMAIL and restart — the login form changes on next boot. No DB migration.

When the admin inbox is unreachable (DNS broke, Resend bouncing, you lost access), shell into the deployment and run parbaked admin signin. It prints a one-time signed URL using the local JWT secret:

# locally
parbaked admin signin

# in prod
fly ssh console -C "parbaked admin signin"

Shell access IS the recovery path; there is no shadow password.

Security posture

What protects you from a bill:

  • Per-IP rate limits on signup, login, and password reset (slowapi).
  • No email enumeration — signup with an existing email returns the same 201 + "check your inbox" response as a fresh signup; if the existing account is still pending verification, parbaked re-sends the verify link so a "forgot I signed up" user can actually log in (#314) without the response shape leaking which addresses are registered. Login with a wrong password returns the same generic 401 as an unknown email.
  • Approval gate — even if someone gets past the rate limit, they can't do anything until you click approve.
  • bcrypt for passwords, HS256 JWT for sessions, audience-scoped JWT for magic links so a session token can never be replayed as approval (and vice versa).
  • CSRF double-submit cookies on every admin POST.
  • Production-mode tripwiresPARBAKED_ENV=production refuses to boot if any required secret was auto-generated, warns on multi-instance setups, suppresses the admin password in the banner.

parbaked assumes one process, one machine — see the single-instance contract in AGENTS.md for what breaks when you scale horizontally.

Running under a different ASGI server

parbaked dev runs uv run uvicorn parbaked.runtime:create_app --factory (pinning uvicorn + parbaked to the project's pyproject.toml / .venv rather than whatever uvicorn happens to be first on $PATH). For Gunicorn / Hypercorn / Granian, write a 2-line wsgi.py:

from parbaked.runtime import create_app
app = create_app()

Then gunicorn -k uvicorn.workers.UvicornWorker wsgi:app --workers 4. See AGENTS.md for the full advanced section.

Install

The CLI is a uv tool (one-shot scaffold/dev/deploy):

uv tool install parbaked

If you'd rather embed parbaked as a library dependency in an existing project:

uv add parbaked
# or
pip install parbaked

Requires Python 3.11+.

Tracking pre-releases

If you want to pull an in-flight alpha or release-candidate (e.g. to test an upcoming 1.4.0a* before it ships stable), opt in with --prerelease=allow and bypass uv's index cache with --refresh:

uv tool install parbaked --prerelease=allow --refresh
# or
uv add parbaked --prerelease=allow
# or
pip install --pre parbaked

These flags are not needed on the stable channel — vanilla uv tool install parbaked is the recommended path.

Develop / contribute

git clone https://github.com/saml7n/parbaked
cd parbaked
uv sync --extra dev
uv run pytest

Issues and PRs welcome.

How does this compare to other FastAPI scaffolds?

The FastAPI scaffolding/template space has a few well-known options. Quick honest comparison:

vs. fastapi/full-stack-fastapi-template (the official template) — full-stack-fastapi-template ships React + Postgres + Docker Compose + an SQLAdmin panel, designed for teams building a customer-facing SaaS. It's the right pick if you want a production-grade React+API monorepo from day one. parbaked is the right pick if you want to ship a single-instance Python web app to fly.io today with invite-only auth as the headline feature — no React build pipeline, no Postgres bootstrap, no DIY approval workflow. Two commands from zero to deployed.

vs. fastapi-users + a separate admin panel — fastapi-users is a solid auth-primitives library but is now in maintenance mode (no new features), and you'd still build the admin UI, the approval queue, the rate limiter, the magic-link flow, the deploy story, and the Dockerfile yourself. parbaked bundles all of that, plus an MCP server so an agent can extend it via tool calls.

vs. sqladmin + DIY auth — sqladmin gives you a Django-style /admin for any SQLAlchemy model in ~10 lines. Excellent for adding an admin to an existing app, but you're still building the entire auth + approval + deploy pipeline. parbaked includes sqladmin-grade admin out of the box and adds the auth/deploy/MCP-server pieces.

vs. Pocketbase — Pocketbase is a single Go binary with its own auth, admin, realtime, and file storage — drop-and-run. parbaked is a Python scaffolding layer over FastAPI — same single-instance philosophy and admin-dashboard goal, but FastAPI-native (drop in any FastAPI route), no realtime / no file storage primitives yet, and the data layer is plain SQLite + SQLModel so you can parbaked eject to real Postgres without parbaked-specific decoders.

vs. FastAPI-Amis-Admin — FastAPI-Amis-Admin is the closest "Django admin for FastAPI" with RBAC built in. Last release was March 2025, docs are heavily Chinese-first. parbaked is English-first, actively maintained (cut today), and has a tighter MCP/agent surface.

vs. agent-built-from-scratch — if you let Claude Code or Cursor write a FastAPI app from scratch they'll typically generate ~500 lines covering signup + JWT + an admin route + a Dockerfile + fly.toml + a half-broken approval workflow. parbaked is the same shape, but 32 alphas + a full pen-test suite ahead on security, deploy reliability, and edge cases like password redaction in 422 errors. Cost: you accept parbaked's conventions (flat module layout, magic-link admin login, opinionated rate-limit defaults).

License

MIT — see LICENSE.

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

parbaked-1.4.1a4.tar.gz (481.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

parbaked-1.4.1a4-py3-none-any.whl (555.7 kB view details)

Uploaded Python 3

File details

Details for the file parbaked-1.4.1a4.tar.gz.

File metadata

  • Download URL: parbaked-1.4.1a4.tar.gz
  • Upload date:
  • Size: 481.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for parbaked-1.4.1a4.tar.gz
Algorithm Hash digest
SHA256 2d814443effead72f8256a73c2031fe67f35256a9bc600c267fdaf855e87a2b6
MD5 b0c79072116d31e6ca3ab68c1ab1ad14
BLAKE2b-256 46959b2d0b223ada0f5e848f71e6c1f80a840808eeadf6d2cccd518fec0f0e4f

See more details on using hashes here.

Provenance

The following attestation bundles were made for parbaked-1.4.1a4.tar.gz:

Publisher: release.yml on saml7n/parbaked

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file parbaked-1.4.1a4-py3-none-any.whl.

File metadata

  • Download URL: parbaked-1.4.1a4-py3-none-any.whl
  • Upload date:
  • Size: 555.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for parbaked-1.4.1a4-py3-none-any.whl
Algorithm Hash digest
SHA256 68f80601c1aaae2956b9929a1da9e13402bbbb2f6d9a5c5e1f0ec3234d8a0099
MD5 654961db002503f046e1cd77bd17cc5a
BLAKE2b-256 00f8d0d3fc57de6dd55a4aa1b88651650f8ea41ebf73e769d879227c13971d41

See more details on using hashes here.

Provenance

The following attestation bundles were made for parbaked-1.4.1a4-py3-none-any.whl:

Publisher: release.yml on saml7n/parbaked

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page