Skip to content

Repository files navigation

EdgeTable

An Airtable-looking app whose AI columns are computed on your machine, by the database itself. No API key, no queue, no data leaving the laptop.

It is a demo for three SQLite extensions:

what it does here
sqlite-ai Runs a local GGUF model inside SQLite. A trigger calls llm_text_generate() on insert, so a new row comes back already summarised, classified and scored for sentiment — in the same transaction that created it.
sqlite-vector Embeds every row and answers "complaints about billing" by meaning rather than by keyword — including rows that never use the word billing.
sqlite-sync CRDT sync to SQLite Cloud and between devices, carrying the AI values with the rows, so a second device gets enriched data without running a model at all.

The point of the app is the contrast. Airtable is a cloud database with a spreadsheet face; this is a local AI engine with a spreadsheet face. Everything except sync works with the network off.

See it

demo.mp4

2 min 46 s: sample data arriving unlabelled, a ✨ column filling in a row at a time as the model runs, semantic search finding rows that never use the word searched for, and a row reaching a second device with its AI values already attached.

The same recording is committed as docs/demo.mp4, so it is still there when the repository is cloned, mirrored, or read anywhere but github.com.

Quick start

Install once:

  • Node 18+
  • Rust, via rustuprust-toolchain.toml pins the version, so cargo fetches the right toolchain itself
  • on macOS, the Xcode command line tools: xcode-select --install. Without them the build fails at the link step with an error that does not mention Xcode.

Then:

make install
make run

make install downloads the three extensions (6 MB, versions pinned and checksum-verified against extensions.lock) and the two models — Qwen3-1.7B for labelling, all-MiniLM-L6-v2 for search — into vendor/, then installs the web dependencies. It is 1.14 GB in total, the models are the slow part, and the download resumes if it drops. Re-running it skips whatever is already there.

The first make run takes a few minutes to build; later ones about 30 seconds. make on its own lists everything else.

The app opens with an empty grid. Nothing is downloaded, generated or synced until you ask for it.

Using it

Add sample data (the button in the middle of the empty grid) inserts 24 pieces of customer feedback. They arrive as plain text — no AI values yet.

Add an AI column. The + at the right of the header opens a field-type menu. Three of the entries are marked ✨ — AI Summary, AI Sentiment, AI Category. Pick one and the column fills in a row at a time, at roughly a second a row. That second is a language model running on your machine, inside a SQL trigger.

Adding the other two ✨ columns is instant. One generation produces all three values, so the second and third columns are already computed and merely revealed.

Add a row. Type into the row at the bottom of the grid. It comes back labelled before it finishes inserting.

Search semantically. Type in the toolbar box with Semantic selected. Try complaints about billing: the results include rows that never say "billing" anywhere. Switch to Keyword for the same query and watch most of them vanish.

Beside the toggle is an optional distance cutoff. It filters results already on screen, so it re-filters instantly. It is an exploration knob, not a relevance setting — distances are not comparable between different queries — and it is off by default.

Filter by category or sentiment. Those dropdowns stay disabled until the matching ✨ column exists and has values, because there would be nothing to filter on.

Delete rows. Click a row number to select it, then press Delete.

Syncing (optional)

The app is fully usable with no account — that is most of what it is arguing. The sync chip reads Local only until you add credentials.

  1. Create a database in the SQLite Cloud dashboard.
  2. Create a feedback table there with the same columns in the same order. The exact DDL is the SCHEMA constant in core/src/schema.rs.
  3. Click OffSync on the database and enable feedback only.
  4. In the app, click the Local only chip (or the ⚙ beside it) and paste in the database ID and API key.

The credentials are checked against the service before they are stored, so a typo is reported there and then rather than becoming a mysterious "Offline" later. They are kept on this device and reused next launch, and the chip becomes Sync now immediately — no restart.

If you prefer a file, cp .env.example .env and set EDGETABLE_DB_ID and EDGETABLE_API_KEY. The environment wins over anything saved in the app, and the panel says so when it does.

Sync is manual, always — nothing goes over the network unless you press it.

The chip tells you where you stand:

Local only no credentials; everything works, nothing leaves. Click it to connect
Sync now · 3 unsent three rows changed here that the server has not seen
Syncing… in flight
Synced · 12s ago up to date, and when that was true
Offline · 3 unsent last attempt failed; your changes are safe and will go up next time

A first sync onto an empty device takes a few seconds — the service builds a snapshot before it can send one.

Seeing sync work, without a second laptop

Start the app as usual, then in another terminal:

make run-second

A second window opens on the same machine with its own database, which is the whole trick: the site id sqlite-sync identifies a device by lives in the database file, not in the app, so two files are two devices. Its title bar says EdgeTable — device-b so the two are tellable apart on a projector.

Connect it to the same cloud database (its chip will read Local only), then: add a row on one, press Sync there, press Sync on the other. The row arrives with its AI values already filled in — computed once, on the device that created it.

make run-second attaches to the dev server make run started, so run that one first. Point it elsewhere with make run-second DEVICE_B_DB=/tmp/laptop-c.db, or set EDGETABLE_DB yourself for a third.

make reset empties both devices when you want to rehearse from scratch.

Two ways to empty the grid, and they are not the same

  • Delete all rows really deletes them. Sync propagates the deletion to every device. There is no undo.
  • Clear this device empties this copy only. Other devices keep their rows, and the next Sync pulls them back here.

What this demo is not

Deliberately out of scope: views (kanban, calendar), linked records, formulas, permissions, collaboration cursors, attachments, undo history, and column sorting. Those are product surface. Teams can build product surface; the argument here is about the engine underneath it.

Status

Working end to end: the grid, inline editing, the ✨ AI columns and their progressive backfill, semantic and keyword search, filters, row delete, the sample data and clear controls, and manual sync against SQLite Cloud.

Not built yet: column sorting, CSV paste for bulk insert, and a packaged .app (today it runs from source via make run). The sample set is 24 rows.

Tested by an engine selftest (9 assertions, real models, real database) and 24 headless UI tests.

Documentation

  • notes/ENGINEERING.md — how it is built, what was measured, and the things a change here must not break
  • notes/FINDINGS.md — measured extension behaviour, including several undocumented gotchas
  • notes/plan.md — the build plan and the demo script

About

An Airtable-style app whose AI columns are computed on your machine, inside SQLite. Demo for sqlite-ai, sqlite-vector, sqlite-sync.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages