One tool, one statement
vendo_apps_sql runs one SQL statement against the app’s own database and answers with { columns, rows, rowCount }.
A statement is one of
SELECT, WITH, INSERT, UPDATE, DELETE, CREATE TABLE, ALTER TABLE, or DROP TABLE. Session, role, schema, and catalog verbs are refused.
The tool’s own description states the live dialect, Postgres or SQLite, because generated SQL is written for one. SQL that sticks to the common subset (TEXT, INTEGER, REAL, PRIMARY KEY, ordinary joins) travels between the two; a vendor-specific type or function does not.
Two namespaces, no third
A bare table name is refused with what happened, why, and the fix:
refusal
mine. is a separate table per person
mine.notes is not one table filtered per caller. Each person gets their own physical table, so ordinary SQL keeps its ordinary meaning:
- A
PRIMARY KEYis unique per person, not across the app. - A
UNIQUEconstraint is per person. - Delete-then-write-by-key statements have no reachable target in anyone else’s rows.
mine. are recorded once and replayed for each user who writes, so every person’s copy has the same shape. Reading a mine. table you have never written answers empty rather than creating anything.
The fence is enforced at the tool, by name resolution, never by generated SQL and never by a database privilege. mine.x and shared.x are the only addresses that exist; the physical names they resolve to cannot be written or guessed from inside a statement. Generated SQL never scopes anything and cannot.
Inside a screen
A screen loads its own rows withuseQuery. A SELECT grades as a read per call, so the tool is queryable even though its authored risk is write.
app.tsx
tools.vendo_apps_sql(…) from a handler, like every other write a screen makes.
Where the database lives
createVendo({ appDatabase }) is the adapter slot. Unset with a store wired, every app gets its own fenced schema inside that store’s Postgres, and there is nothing to configure.
vendo.ts
unavailable, naming the three ways to get a database: pass a store whose Postgres backs every app, pass appDatabase yourself, or set VENDO_API_KEY.
Passing your own AppDatabase adapter always wins. The adapter executes and decides nothing: every rule that makes mine. one person’s rows lives above the adapter seam, so two implementations cannot disagree about who sees what.
Migrating from the app-data tools
The app-data family is gone, replaced whole by the SQL database:
Nothing carries records over automatically. An app that kept data in the old collections recreates it as
shared. or mine. tables on its next edit.
Calls left on the old appData.* wire operations answer not-implemented (HTTP 501) naming the operation, so a stale integration fails loudly instead of silently.