Code Canvas Database

Add a real Postgres database to any Code Canvas project. Theo writes code against live tables, auto-seeds sample rows, and can repair runtime errors with a click. Built on Neon Serverless Postgres.

Data tab layout

Overview

The default landing surface. Surfaces a storage meter, table count, total row estimate, auth user/session counts, last activity, plus actions to View users, New table, Open SQL runner, and Open workspace.

Data Workspace

Click the maximize icon next to the DB pill in the header (or the Overview action card) to open a full-screen workspace: searchable left rail of tables, wide row grids, a taller SQL editor, and side-by-side schema + history cards. Selection + sub-view state sync with the sidebar.

All six sub-views (Overview, Tables, Users, SQL, Schema, History) live in both surfaces. Auth lives in a single chip in the workspace header and as a full-width card at the top of the sidebar Overview — use either to enable, disable, view user / session counts, or open the read-only Users directory.

Enable the database

Open a Code Canvas project, switch to the Data tab, and click Enable Database. Provisioning takes a few seconds and drops a green indicator next to the project name.

Behind the scenes we create a new Neon project, wire a restrictedapp_reader role for the public share proxy, and store the connection strings encrypted in Appwrite.

Connect your own database

Prefer to use a database you already own? On the Data tab, choose Connect your own and paste a PostgreSQL connection string from Supabase, Neon, AWS RDS, Railway, or any Postgres host. We validate + test the connection, then the whole Data tab — tables, the SQL runner, row editing, schema view, migration history, and Theo's AI edits — works against it exactly like a built-in database. Your app'swindow.__db queries hit it too.

  • Use a connection string reachable from the internet (for Supabase, the Transaction pooler string). Private / localhost hosts are rejected.
  • SSL is required. Your connection string is encrypted at rest and never shown again.
  • End-user auth (window.__auth) works via the built-in _auth_users tables.
  • Neon-only conveniences — Preview/Production environments, backups & promote, and managed auth providers — aren't available on an external database; you manage those in your provider's dashboard.

To switch back, use Remove database in the Data tab kebab. Disconnecting only drops the credentials we stored — your database is left completely untouched.

What you get

Live Postgres database

Every Code Canvas project can provision its own Neon Postgres instance. Theo and the preview talk to it through window.__db.

End-user authentication

Add real signup and login through window.auth. Project owners can browse signed-up accounts in Data → Users, separately from application tables.

Seed data after each table

When Theo creates a table it auto-generates 3–5 realistic rows so the preview isn't empty. Call `db_seed_table` later for more.

Per-user scoping

Theo can call `scope_table_to_user` to add a `user_id` column that is filled in automatically for whoever is signed in, plus a row-level-security policy so each user only sees their own rows (or everyone reads and only the author edits).

Schema history + revert

Every DDL applied to your database is logged. Open the History tab to see the full ledger and revert non-destructive migrations in one click.

CSV import / export

Export up to 10,000 rows from any table. Import rows client-side — we parse and insert them in chunks, showing per-row errors inline.

The window.__db SDK

The preview iframe exposes window.db (with window.__db kept as an alias to the same object, so older generations keep working). Generated code reads and writes like any other Postgres client:

const { rows } = await window.__db.query('SELECT * FROM todos ORDER BY created_at DESC');

await window.__db.query(
  'INSERT INTO todos (title, completed) VALUES ($1, $2)',
  [title, false]
);
  • Always parameterized — never string-concatenate user input.
  • Always awaited.
  • Wrap in try/catch — Theo reads Postgres errors via the Try-to-fix button (see below).

Theo controls the database

The lifecycle is chat-controlled end to end. Ask for anything that needs data ("save these signups", "add a leaderboard") and Theo enables the database first, then creates the tables — on any turn, in the editor or a fresh chat. Ask him to "remove the database" and he can disable it too: that path only runs when you explicitly request it, and it always goes through the same amber Confirm & run card before anything is torn down. On a connected external database, disabling only drops the credentials we stored — your database itself is untouched. Enabling and disabling are owner-only: team editors can use every data tool, but the database lifecycle stays with the project owner, whose plan it bills against.

Theo isn't limited to creating tables. In an edit turn it can run any SQL against your project's database: add or reshape columns, create indexes for slow queries, define views, functions, triggers, enum types, and row-level-security policies, install Postgres extensions, and read or write data — multi-step migrations run inside a single transaction so a partial failure rolls everything back.

Theo also sees the whole schema — tables, columns, indexes, foreign-key relationships, constraints, enums, views, RLS policies, and installed extensions — so changes respect what already exists.

Destructive changes are gated: dropping a table, truncating data, or deleting without a WHERE clause requires confirmation. If your request explicitly asked for it ("drop the orders table"), Theo proceeds; otherwise the editor chat shows an amber Confirm & run card spelling out exactly what would be destroyed — one click approves, Cancel leaves everything untouched. Every schema change lands in the migration history with best-effort revert SQL.

Theo can also read its own trail: db_list_migrations shows the recent history, db_revert_migration undoes an entry via its stored compensating SQL, and slow queries get diagnosed withEXPLAIN (ANALYZE, BUFFERS) before Theo adds an index.

Preview & Production environments

Experiment safely — your live site keeps its own data

Your database splits into two environments built on Neon branching. Preview is where Theo, the editor preview, the SQL runner, and the row grids work by default — experiment freely. Production is what your published site reads and writes. A schema change or a bulk delete in Preview never touches your visitors' data.

Switch environments with the Preview / Production pill in the Data tab header. The Environments & backups card on the Overview tells you whether Production matches Preview or how many schema changes are still waiting to go live, and its Promote to Production button applies them by hand. Promote has two modes:

  • Schema only (default) — replays the schema changes you made in Preview since the last promote onto Production, one migration at a time. Your production data is untouched. The dialog shows exactly which pending migrations will replay; if one fails, the replay stops there and re-promoting after a fix resumes from the same point.
  • Full restore — makes Production an exact copy of Preview, including its data. The dialog warns you when production tables hold more rows than Preview, since those rows would be replaced.

Either way the pre-promote production state is kept as an automatic backup. Reset Preview from Production pulls live data the other way when you want to test against real rows.

Publishing can do the promote for you. When it's on for your workspace, publishing first brings Production up to date with your Preview schema — your live data is untouched and a backup is taken first. On your very first publish you can also start the live database as a copy of your preview data, so your site launches with the content you built. If a change can't be applied to the live database, the publish stops before anything changes and your live site keeps working; the publish dialog then offers Fix with Theo or Publish without updating the database.

Environments are rolling out — if you don't see the switcher yet, your project is on the single-database setup and everything else on this page works the same.

Backups & restore

Point-in-time restore + automatic pre-restore backups

Every environment can be rewound to any moment in the last 24 hours — pick a time in the Restore dialog and the branch rolls back to exactly that state. Before ANY restore or promote runs, the current state is preserved as abackup-* branch, so a restore is itself always undoable.

The three most recent backups are kept (older ones prune automatically). Restore from any of them — or delete ones you no longer need — from the same Environments & backups card. Each backup shows its size, and the Overview's storage meter has a Where is it going? breakdown (Production / Preview / Backups) — deleting old backups is the quickest way to free space when you're near your plan's limit.

Background migrations & imports

Long-running SQL no longer hits the request timeout

Big backfills and slow migrations can run as a durable background job with a 4-minute statement budget. Theo dispatches them automatically when a statement times out inline (it re-runs with background: true), and large file imports queue themselves. You get a notification when the job finishes, and the outcome lands in the migration history — Theo verifies withdb_list_migrations before building on it.

Progress is live: the moment a job is dispatched it appears in Schema history as a Running entry with a spinner, and the view refreshes itself until the job flips to applied or failed — no reload needed.

Authentication

Click Auth → Enabled in the Data tab, or ask Theo "add signup and login". Your app then uses:

await window.__auth.signup(email, password, displayName?);
await window.__auth.login(email, password);
const user = await window.__auth.currentUser();
window.__auth.onAuthChange((user) => { /* react to state */ });

window.auth is the canonical name;window.__auth stays as an alias to the same object.

Ask Theo to call scope_table_to_userto add a user_id column + index (and an optional Postgres RLS policy) so each user only sees their own rows. The column fills itself in with the signed-in user when a row is added, so your app code can't forget to. For things everyone should see but only the author should change — reviews, a message board, blog posts — ask for it to be readable by everyone instead.

Auth and environments: when separate authentication is configured, signups made in the editor preview land in Preview as test accounts, while your published site's users and sessions live in Production. If Preview authentication is still using the Production account service, the Users view explicitly says Preview is showing Production accounts. Application tables still use the selected data environment. The promote / reset dialogs warn when an operation would replace accounts.

Browse signed-up users

As the project owner, open Data → Users, choose Authentication in the sidebar menu, or click View users on the authentication card. The workspace's left rail also has Authentication / Users.

Search by email, name or user ID and page through the matching accounts. Each entry shows its ID, email, display name and available timestamps; copy the ID to match it to an application record. Missing timestamps say Not available. The directory is read-only — it never exposes passwords or session credentials and does not offer account creation, deletion or impersonation.

Account records and application tables are separate. Managed accounts live in the authentication schema, so they do not appear in the public table explorer. Use Usersfor signups and Tables for saved application records such as registrations. Check both the Data and Accounts labels before comparing them.

Refresh and read status

The header's Refresh updates database details, account counts and the visible Users page or table rows together. Data also refreshes when you change views or environments, return to the window, or save a database change. While a Data surface is visible, it checks every 30 seconds; background browser tabs pause that polling.

A zero count means the read succeeded and found no accounts. Setup pending, unavailable and failed reads are shown separately, with a Retry action when a read fails. If a refresh fails after a successful read, the previous data stays visible with an out-of-date warning and its last successful refresh time.

Table counts prefixed with ~ are database estimates, not exact totals. Rows unknown means no estimate is available. The grid separately reports how many rows were fetched, up to 100. A failed query never means the table is empty.

API keys & secrets

Building something that calls a paid API — a weather service, a payments provider, a model API? Don't paste the key into your code, where anyone can read it in the page source. Add it in the project's Environment view instead: give it a name, a separate value for Preview and for Production, and the websites it's allowed to be sent to. You can also import the keys from an existing .env file.

Theo then writes requests that refer to the key by name — {{secret:WEATHER_API_KEY}} — and the real value is filled in on our servers as the request goes out, only to the sites you allowed. The key never appears in your code, your published site, the chat, or anything Theo sees; once saved, a value can be replaced but not read back.

Secrets are rolling out — if you don't see them in the Environment view yet, they're not on for your workspace.

Preview toolbar

Address bar, viewport selector, and reload — above every preview

Every Code Canvas preview now has a Bolt/Replit-style toolbar across the top. The address bar accepts any in-project route (“/about”, “/dashboard?tab=stats”) — hit Enter and Theo routes the iframe to that page. If a sibling HTML file matches (“about.html”) the preview swaps instantly; otherwise SPA routers (React Router, Vue Router) pick up the new path via the same pushState +popstate dance the in-preview link clicks use.

Back, Home, and Reload sit next to the address bar. Reload re-mounts the iframe (or re-runs Sandpack) without losing your code state — useful after a manual edit you want to apply in isolation.

The device viewport selector on the right of the toolbar constrains the preview to a fixed width × height — Desktop (fluid), iPhone 16 / 16 Pro / 16 Pro Max, Pixel 10, Galaxy S25, iPad, iPad Pro 13, or Custom dimensions you type yourself. Zoom + / − keeps iPad presets readable on small laptop screens. Selections persist per project, so refreshing the page leaves you in the same frame.

Inside an edit turn

One agent loop, one Project State, one recovery contract

Every Code Canvas chat message runs through a single agent loop: one streaming AI call, one set of tools, one heartbeat, one budget. The model picksupdate_code,patch_code,generate_code, or a DB tool, and the loop short-circuits the moment real code lands so changes ship immediately.

Theo opens every turn with a Project State block: a compact summary of your project's framework, entry file, whether the database is live, every table + auth provider, and whether auth is enabled. That single authoritative snapshot replaces the older stack of competing context blocks so the model never has to guess what's wired and what isn't.

The tier picker above the composer (Theo Code Fast / Theo Code / Theo Code Max) controls which model handles the loop and how long it can run before the heartbeat trips. The picker also exposes a Plan mode for multi-step requests — see the section below.

After a substantial build (3 or more files in a single ad-hoc turn) Theo finishes the chat reply with two extra sections — Key features and Design highlights — enumerating the user-visible deliverables and the visual choices he made. The bubble picks up a subtle accented background so the recap reads like a delivery report. Small CSS / single-file edits and plan-mode turns skip the recap so iterative changes stay terse.

Project credit usage

See what each project costs — live, in the header

A credits pill sits at the top of the editor, just left of the Visual Edit toggle. It shows the running total of AI credits this project has spent. Every time you send a prompt and Theo ships changes, the number ticks up the moment the turn finishes — so you always know roughly what a given build is costing you.

Click the pill to open an itemized breakdown of where your credits went. Each turn is listed newest-first with what it did (a code change, a database step, a design tweak, an auto-fix, a plan step, or an upscale), which tier ran it (Theo Code Fast / Theo Code / Theo Code Max), how many files it touched, and how long ago it happened. The breakdown updates automatically the instant a turn completes.

The breakdown also surfaces a smart suggestion when it spots a cheaper path — for example, if most of your recent spend is going to Theo Code Max, it nudges you toward Theo Code, which handles the majority of changes for a fraction of the credits. Switch tiers any time from the model picker in the composer.

The total is per-project and persists across reloads. Instant CSS tweaks that Theo applies without an AI round-trip don't add to it, and your overall monthly balance still lives in Settings → Billing.

Try-to-fix & Keep going

Free, one-click repair

When the preview throws a runtime error (including Postgres errors from window.__db), a red Try to fix chip appears above the composer. Clicking it sends the recent errors to Theo and routes the fix through a fastpatch_code repair.

We pair Postgres error codes with one-line hints so Theo knows, for example, that42P01 means "table doesn't exist — create it before querying". Throttled to 3 attempts per project per minute.

When Theo stalls or finishes a turn without writing any code (for example, a DB-only call to enable_auth with no UI follow-up, or an upstream timeout) the assistant message ends with a small orange Keep going card. Clicking it re-sends a typed resume message so Theo finishes the work without you re-typing anything. The card surfaces a specific reason (timeout, no tool call, provider error, etc.) so you always know whether to wait or try a different tier.

Seed data

Never start with an empty preview

Theo now passes realistic seed_rows with everydb_create_table call, and can calldb_seed_table later with a hint like "three fitness customers" or "urgent support tickets". Internal_auth_* tables are never seeded.

Import & export data

Open any table in the Data tab. Use Export to download up to 10,000 rows as a CSV, or Import to upload a CSV whose headers match existing columns. Rows insert one-by-one with parameterized SQL and per-row error reporting.

For bigger or messier data, attach the CSV or JSON file in chat and ask Theo to import it. The db_import_data tool parses it server-side, infers column types (numbers, booleans, timestamps), can create the table for you (adding an id primary key when the data has none), and inserts in chunked transactions. Up to 2,000 rows import instantly; files up to 50,000 rows / 15MB run as a background job with a notification when done. Replacing a table's data goes through the same confirm gate as any destructive SQL.

Chat imports are all-or-nothing: rows are staged into a temporary table first, and your real table only changes in one final transaction once every row has landed. If anything fails mid-import, the staging table is discarded and your data is exactly as it was — no half-imported tables.

Edit rows inline

Fix a value without writing SQL

In the Tables view, double-click any cell to edit it in place — Enter saves, Escape cancels, and nullable columns get a NULL toggle. Each row also has a delete action with a confirm dialog. Edits run as parameterized UPDATE / DELETE statements keyed on the table's primary key (tables without one show editing as disabled), so a typo fix is one click instead of a SQL round-trip.

The editor matches the column's type: booleans get a true / false picker, timestamps get a date-time picker, JSON columns get a multi-line editor that validates before saving (⌘/Ctrl+Enter commits), and numeric columns get a number input — so you can't accidentally save malformed values.

Plan mode

Review the plan before Theo touches your code

Flip the mode pill above the chat composer from Agent to Plan and send your request. Theo drafts a structured plan first — phases, tasks, tech stack, components — and surfaces it in a side panel anchored to the top-right corner of the preview. Nothing in your code changes until you approve.

Each task starts as queued. You can edit the wording, drop tasks you don't want, or dismiss the plan entirely. When you're ready, hit Execute Plan and Theo runs each task in order — flipping rows to active →done in real time. A task that stops without shipping code surfaces a recovery card on that row so you can keep going manually.

The plan panel can be minimized into a slim pill (e.g. Plan · 3/7 tasks) so it never fights the preview for screen space. Click the pill any time to expand the full task list. Once a plan completes, the mode picker automatically returns to Agent so the next ad-hoc message ships directly.

  • Engine tier sticks. Plan creation runs on a fast planner; execution runs on the tier you picked (Theo Code Fast / Code / Max).
  • Persistence is local-only. Plans rehydrate from your browser for 24h. The server never stores them; the next page reload picks up where you left off in review mode.
  • Per-task recovery. Every task reuses Code Canvas's standard heartbeat, retry, and salvage contract — if Theo stalls on task 5, the rest of the plan stays untouched.

Schema history + revert

The History sub-view lists every schema-changing SQL statement Theo or you applied — with source, tool, and timestamp. Each row shows a best-effort revert button; clicking it runs a compensating DDL (e.g.DROP TABLE,DROP COLUMN) and marks the entry as reverted. Destructive reverts prompt for confirmation.

Theo works from the same ledger: ask "what changed in my database?" and it readsdb_list_migrations; ask it to undo a change anddb_revert_migration runs the stored revert SQL (destructive reverts go through the same confirm card). Background jobs, imports, promotes, and restores are recorded here too, so the ledger doubles as your job history.

Your live site

What publishing brings live — and how you know it works

Your published site has its own Production database. Publishing brings over the tables you built and, the first time each table goes live, the content you added in Preview — your books, events or products. It never copies your test accounts or anything they saved, and it never overwrites a live table that already has data.

Right after you publish, we check the live site the way a visitor's browser uses it: whether the page can reach its database and sign-in, and whether every table can be read. The result appears in the Publish window. A problem on our side is labelled as such — there's nothing to change in your app — while a problem in your app comes with a Fix with Theo button.

Ask Theo to "check my live site" at any time and it runs the same check, including a full sign-up and sign-in test with a temporary account it deletes afterwards. Sign-in on a published site is email + password; if a visitor's session expires, your app simply asks them to sign in again.

Publishing a code project keeps the database behind a signed public query proxy. Enable auth + call scope_table_to_userbefore launch so each visitor only sees their own rows.
Was this article helpful?

Related Articles