storing-data
Storing Data — SQL is the Source of Truth
Rule
All application data lives in SQL (SQLite locally, persistent database in production). The agent and UI share the same database. SQL stores structured records, metadata, references, and searchable text — not large raw file payloads. Do not store durable app data in the filesystem unless the app is explicitly running a Local File Mode artifact flow described below.
Large binary or file-like payloads (images, video/audio, PDFs, ZIPs, screenshots, session replay chunks, thumbnails, generated assets, data: URLs, and base64 file bodies) must go through configured file/blob storage such as uploadFile() or putPrivateBlob(). Persist only the returned URL, asset id, or opaque blob handle in SQL. If storage is unavailable in hosted or persistent-database mode, fail closed with setup guidance instead of falling back to base64 in application_state, settings, resources, or app tables. Local SQLite-only dev fallbacks may exist for tiny assets, but they must be capped, documented as dev-only, and kept off hot list/read paths.
Local File Mode exception: some artifact apps (Content, Plans, Slides, Dashboards, Designs, etc.) can intentionally use repo files as the source of truth for the artifact itself. This must be explicit via agent-native.json, AGENT_NATIVE_MODE=local-files, or an app-owned local-file action helper. In that mode, the UI and agent still go through app actions, but those actions read/write scoped files through @agent-native/core/local-artifacts instead of SQL rows. App state, auth, settings, credentials, collaboration metadata, and hosted database mode remain SQL. File-to-database or file-to-provider synchronization is an explicit sync step, not an implicit side effect of editing.
When you add a data model, a list, or a read path, also follow the performance skill: project only the columns a list renders, index the columns hot queries filter/sort on, and avoid query waterfalls — so apps stay fast as data grows.
How It Works
Agent-native apps use Drizzle ORM over the configured SQL backend. Local development works out of the box with a SQLite file at data/app.db; production and shared preview deploys need a persistent DATABASE_URL because container/serverless filesystems can reset. The code should behave the same across backends, but the local SQLite file is not durable once deployed.
For app code, use Drizzle's schema/query DSL by default. Raw SQL is an escape hatch for additive migrations, health checks, or one-off maintenance, not the normal way to build features.