Metadata-Version: 2.4
Name: parbaked
Version: 1.4.1a4
Summary: 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-URL: Homepage, https://github.com/saml7n/parbaked
Project-URL: Repository, https://github.com/saml7n/parbaked
Project-URL: Issues, https://github.com/saml7n/parbaked/issues
Author: Sam Leighton
License: MIT
License-File: LICENSE
Keywords: admin-dashboard,agent-native,auth,boilerplate,deploy,fastapi,fastapi-admin,fastapi-auth,fastapi-boilerplate,fastapi-fly-io,fastapi-scaffold,fastapi-starter,fastapi-template,fly-io,invite-only,magic-link,mcp,mcp-server,model-context-protocol,python-saas-starter,python-web-app-scaffold,scaffold,sqlite,sqlmodel,starter,template
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: FastAPI
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: >=3.11
Requires-Dist: bcrypt>=4.1
Requires-Dist: email-validator>=2.1
Requires-Dist: fastapi>=0.110
Requires-Dist: greenlet<3.5.2
Requires-Dist: mcp>=1.0
Requires-Dist: pydantic-settings>=2.1
Requires-Dist: pydantic>=2.5
Requires-Dist: pyjwt>=2.8
Requires-Dist: python-multipart>=0.0.9
Requires-Dist: rich>=13.0
Requires-Dist: slowapi>=0.1.9
Requires-Dist: sqlmodel>=0.0.16
Requires-Dist: typer>=0.12
Requires-Dist: uvicorn[standard]>=0.30
Provides-Extra: dev
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: mcp
Description-Content-Type: text/markdown

# 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](https://img.shields.io/pypi/v/parbaked?label=parbaked)](https://pypi.org/project/parbaked/) [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/saml7n/parbaked/blob/main/LICENSE) [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/) [![MCP](https://img.shields.io/badge/MCP-server-purple)](https://glama.ai/mcp/servers/saml7n/parbaked)

Signup with admin approval, JWT sessions, rate limiting, magic-link admin login, SQLite-on-fly-volume persistence, and a [published off-boarding contract](https://github.com/saml7n/parbaked/blob/main/docs/data-format-guarantees.md) — 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

```bash
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](#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`:

```python
# 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:

```bash
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:

```bash
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:

```bash
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`](https://github.com/saml7n/parbaked/blob/main/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`](https://github.com/saml7n/parbaked/blob/main/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](#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:

```bash
# 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 tripwires** — `PARBAKED_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](https://github.com/saml7n/parbaked/blob/main/AGENTS.md#single-instance-contract) 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`:

```python
from parbaked.runtime import create_app
app = create_app()
```

Then `gunicorn -k uvicorn.workers.UvicornWorker wsgi:app --workers 4`. See [AGENTS.md](https://github.com/saml7n/parbaked/blob/main/AGENTS.md) for the full advanced section.

## Install

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

```bash
uv tool install parbaked
```

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

```bash
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`:

```bash
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

```bash
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`](https://github.com/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`](https://github.com/fastapi-users/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`](https://github.com/aminalaee/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](https://pocketbase.io)** — 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](https://github.com/amisadmin/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](https://github.com/saml7n/parbaked/blob/main/LICENSE).

<!-- mcp-name: io.github.saml7n/parbaked -->

