Build a new app
An ordinary platform app is a folder, a manifest, and application code. The platform discovers it and mounts its URL without a new nginx rule, port, or service.
1. Scaffold
at new my_app --name "My App"
This creates apps/my_app/ with a backend and React frontend. Use --no-web for an API-only app. New apps default to administrator access while you build them.
apps/my_app/
app.toml name, URL, access, capability requirements
api.py AppKit instance and HTTP routes
jobs.py optional background handlers
README.md app-specific documentation and notes
web/ frontend source and built assets
2. Declare the requirements
name = "My App"
path = "/my-app"
access = "authenticated"
requires = ["identity", "database", "files", "jobs"]
required_env = []
required_secrets = []
Use admin for private tools, authenticated for signed-in humans, and public when the app deliberately supports anonymous visitors. A public route that starts paid AI work must still have appropriate authorization and a budget.
Capabilities supported by preflight are identity, database, files, jobs, ai, and kv. Declare only the ones you use. kv requires a separately provisioned DynamoDB table.
3. Use the shared abstractions
from fastapi import Request
from pydantic import BaseModel, Field
from at import AppKit
kit = AppKit(__name__)
router = kit.router
class Note(BaseModel):
text: str = Field(min_length=1, max_length=10000)
@router.post("/api/notes")
async def save_note(body: Note, request: Request):
user = await kit.auth.require(request)
store = kit.store.for_owner(user.id)
note_id = await store.put("notes", body.model_dump())
return {"id": note_id}
@router.get("/api/notes")
async def list_notes(request: Request):
user = await kit.auth.require(request)
return await kit.store.for_owner(user.id).list("notes", limit=50)
These routes become /my-app/api/notes. The owner comes from verified identity, not a request body. For uploads, call kit.enable_files() once to install the shared owner-scoped file endpoints.
4. Build the interface
The default frontend uses React, TypeScript, Vite, Tailwind, and @at/ui. Plain HTML and static multi-page apps are also supported.
import { api, Button, Card, Page } from "@at/ui";
// Resolves to this app's API, including its configured URL prefix.
const notes = await api<{ id: string; text: string }[]>("notes");
See UI & live updates for the shared components and path conventions.
5. Check and deploy
at doctor my_app
at build my_app
at doctor --live
at deploy
If you added SQL tables, generate and review a migration, test it on an isolated database, and apply it with operator credentials before deploying. Runtime database users cannot perform schema changes.
Deploy checks requirements, schema state, dependency readiness, and every app's HTTP page. Open the real URL and exercise the flow as well. A successful health check is not a substitute for checking authorization and user behavior.
6. Leave a trail
Add an app README.md, list the app in the repository catalog, and commit the app and documentation together. The app directory discovers the new manifest and links its notes on the next deployment.
Use the private authoring guide for the full repository workflow.