# Vibetuner > Vibetuner is a production-ready FastAPI project scaffolding tool that generates full-stack web applications with > authentication, flexible database support (MongoDB or SQL), frontend, Docker deployment, and CLI tools > pre-configured in seconds. Built by All Tuner Labs for rapid iteration and modern development. Important notes: - Vibetuner consists of four packages: a Python framework (`vibetuner`), a JavaScript build-deps package (`@alltuner/vibetuner`) which bundles tailwind, daisyui, htmx and an npm-shipped mirror of the framework's jinja templates (`@alltuner/vibetuner-jinja`, pulled in as a transitive — consumers don't install it directly), and a Copier scaffolding template - The framework separates immutable framework code (`vibetuner` package) from your application code (`src/app/`) for clean updates - In all examples, `app` refers to your project's Python package (the directory under `src/`). The actual name depends on your project slug (e.g., `src/myproject/` for a project named "myproject") - HTMX is used instead of React/Vue for simplicity - server-rendered HTML with sprinkles of interactivity - All tools are chosen for speed: uv (Python), bun (JavaScript), Granian (ASGI server), Ruff (linting) - The project is designed to work excellently with AI coding assistants like Claude, Cursor, and ChatGPT ## Quick Start ### Prerequisites - **Python 3.11+**: [python.org/downloads](https://www.python.org/downloads/) - **Docker**: [docker.com/get-started](https://www.docker.com/get-started/) (for containerized development) That's it! Vibetuner handles the rest. ### Create Your First Project #### Using uvx (Recommended) No installation needed: ```bash uvx vibetuner scaffold new my-app ``` #### Install Globally ```bash uv tool install vibetuner vibetuner scaffold new my-app ``` #### Interactive Setup The scaffold command will ask you: - **Project name**: `my-app` - **Company name**: Your company/organization - **Author details**: Name and email - **Features**: OAuth providers, background jobs, etc. #### Skip Prompts Use defaults for everything: ```bash uvx vibetuner scaffold new my-app --defaults ``` ### Start Development ```bash cd my-app just dev ``` For projects using SQL models (SQLModel/SQLite/PostgreSQL), create tables first: ```bash uv run vibetuner db create-schema # Required once before first run just dev ``` This starts: - Database (MongoDB or SQL, if configured) - Redis (if background jobs enabled) - FastAPI application with hot reload - Frontend asset compilation Visit `http://localhost:8000` - your app is running! ### Project Structure ```text my-app/ ├── src/app/ # Your code (edit freely) │ ├── frontend/routes/ # Your HTTP routes │ ├── models/ # Your database models │ └── services/ # Your business logic ├── templates/ # Jinja2 templates ├── assets/ # Static files └── Dockerfile # Production deployment ``` The `vibetuner` framework (auth, database, core services) is installed as a package dependency. ### Justfile Commands All project management tasks use `just` (command runner). Run `just` to see all available commands. **Development:** ```bash just dev # Docker development with hot reload (recommended) just local-dev PORT=8000 # Local development without Docker just worker-dev # Background worker (if background jobs enabled) ``` **Dependencies:** ```bash just install-deps # Install from lockfiles just update-repo-deps # Update root scaffolding dependencies uv add package-name # Add Python package bun add package-name # Add JavaScript package ``` **Code Formatting:** ```bash just format # Format ALL code (Python, Jinja, TOML, YAML) just format-py # Format Python with ruff just format-jinja # Format Jinja templates with djlint just format-toml # Format TOML files with taplo just format-yaml # Format YAML files with dprint ``` **IMPORTANT**: Always run `ruff format .` or `just format-py` after Python changes. **Code Linting:** ```bash just lint # Lint ALL code just lint-py # Lint Python with ruff just lint-jinja # Lint Jinja templates with djlint just lint-md # Lint markdown files just lint-toml # Lint TOML files with taplo just lint-yaml # Lint YAML files with dprint just type-check # Type check at the repo root (template + tooling) just type-check-py # Type check the vibetuner-py framework code with ty ``` **Localization:** ```bash just i18n # Full workflow: extract, update, compile just extract-translations # Extract translatable strings just update-locale-files # Update existing .po files just compile-locales # Compile .po to .mo files just new-locale LANG # Create new language (e.g., just new-locale es) just i18n-fuzzy-audit # Report fuzzy-marked entries across catalogs ``` `update-locale-files` runs `msgmerge --no-fuzzy-matching`: new strings land with an empty `msgstr` (honest untranslated → English fallback) instead of a wrong guess copied from a textually similar entry. The i18n recipes also pass `--no-wrap`, keeping every `msgid`/`msgstr` on a single line so catalogs don't drift when gettext's line-wrapping differs between macOS and the Linux used in CI. **CI/CD & Deployment:** ```bash just build-dev # Build development Docker image just test-build-prod # Test production build locally just build-prod # Build production image just release # Build and release production image just deploy-latest HOST # Deploy to remote host ``` **Scaffolding:** ```bash just update-scaffolding # Update project to latest vibetuner template just deps-scaffolding # Update deps + scaffolding on the current branch (no PR) just deps-scaffolding-pr # Update deps + scaffolding in a worktree and open a PR ``` ## Core Documentation ### Development Guide #### Development Environment Vibetuner supports two development modes: ##### Docker Development (Recommended) Run everything in containers with hot reload: ```bash just dev ``` This starts: - Database (MongoDB or SQL, if configured) - Redis (if background jobs enabled) - FastAPI application with auto-reload - Frontend asset compilation with watch mode Changes to Python code, templates, and assets automatically reload. ##### Local Development Run services locally without Docker: ```bash # Terminal 1: Frontend assets bun dev # Terminal 2: Backend server just local-dev ``` A database (MongoDB or SQL) is required if using database features. Redis is only required if background jobs are enabled. #### Adding New Routes Create a new file in `src/app/frontend/routes/`: ```python # src/app/frontend/routes/blog.py from fastapi import APIRouter router = APIRouter(prefix="/blog", tags=["blog"]) @router.get("/") async def list_posts(): return {"posts": []} ``` Register in `src/app/frontend/__init__.py`: ```python from app.frontend.routes import blog app.include_router(blog.router) ``` **`@render` decorator** — for simple routes, eliminate `render_template()` boilerplate: ```python from vibetuner import render @router.get("/dashboard") @render("dashboard.html.jinja") async def dashboard(request: Request, user=Depends(get_current_user)) -> dict: return {"user": user} ``` Returns `Response` objects unchanged (escape hatch for redirects, conditional logic). #### Adding Database Models Create models in `src/app/models/`. The approach depends on your database choice: ##### MongoDB (Beanie ODM) ```python # src/app/models/post.py from beanie import Document from pydantic import Field class Post(Document): title: str content: str published: bool = Field(default=False) class Settings: name = "posts" ``` Beanie builds queries from class-level field access (`Post.title == "x"`, `Eq(cls.author.id, user.id)`), which static checkers don't model: `ty` resolves `Model.field` from the field's instance annotation, not as a query expression. Most comparisons type-check, but two patterns trip a spurious `unresolved-attribute` in `just lint`: an optional link in a query (`field: Link[X] | None` with `cls.field.id` → `id is not defined on None`, fundamental to how `ty` treats `X | None`, not fixable via field/alias changes), and queries inside a classmethod on a non-`Document` (`BaseModel`) mixin. Silence on the exact line: ```python return await cls.find_one(Eq(cls.author.id, user.id)) # ty: ignore[unresolved-attribute] ``` `ty` reports directives that stop matching (`unused-ignore-comment`), so keep the comment on the erroring line and remove it when no longer needed. ##### SQL (SQLModel) ```python # src/app/models/post.py from sqlmodel import SQLModel, Field class Post(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) title: str content: str published: bool = Field(default=False) ``` For SQL databases, create tables with: `vibetuner db create-schema` Register models in `src/app/models/__init__.py`: ```python from app.models.post import Post __all__ = ["Post"] ``` The model will be automatically registered with the database on startup. ##### Soft Delete (MongoDB) Use `DocumentWithSoftDelete` instead of `Document` to mark documents as deleted instead of removing them: ```python from vibetuner.models import DocumentWithSoftDelete from vibetuner.models.mixins import TimeStampMixin class Post(DocumentWithSoftDelete, TimeStampMixin): title: str content: str class Settings: name = "posts" ``` Soft-deleted documents are automatically excluded from `find()`, `find_one()`, and `get()` queries. Use `find_many_in_all()` to include them. `delete()` sets `deleted_at` instead of removing the document. `hard_delete()` permanently removes it. To restore: `doc.deleted_at = None; await doc.save()`. The CRUD factory's DELETE endpoint automatically performs a soft delete for these models. ##### Encrypted Fields (MongoDB) Encrypt sensitive fields at rest using `EncryptedFieldsMixin` and `EncryptedStr`: ```python from beanie import Document from pydantic import Field from vibetuner.models.mixins import EncryptedFieldsMixin, EncryptedStr class ApiCredential(Document, EncryptedFieldsMixin): provider: str api_key: EncryptedStr = Field(..., description="Encrypted API key") token: EncryptedStr | None = Field(default=None) class Settings: name = "api_credentials" ``` Fields are transparently encrypted before database writes and decrypted on load. Requires `FIELD_ENCRYPTION_KEY` environment variable (Fernet key). Use `vibetuner crypto set-key` to generate a key, `vibetuner crypto rotate-key` to rotate it. #### Creating Templates Add templates in `templates/`: ```html {% extends "base/skeleton.html.jinja" %} {% block content %}