Skip to content

Native SQLite

PAM Desktop compiles SQLite into the Rust host. Production bundles therefore need neither a system SQLite installation nor a JavaScript database runtime. The renderer can access only databases declared by PHP policy.

use Pam\Desktop\Capabilities;
use Pam\Desktop\Database;
$app->capabilities(
Capabilities::none()
->database(Database::readWrite('app', 'storage/app.sqlite'))
->database(Database::read('catalog', 'resources/catalog.sqlite')),
);

Names use PAM’s identifier grammar. Paths are project-relative, cannot contain parent components, and cannot be symbolic links. A read-write database creates its parent directory when needed; a read-only database must already exist.

await pam.database.execute(
"app",
"CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT NOT NULL)",
);
const inserted = await pam.database.execute(
"app",
"INSERT INTO notes (body) VALUES (?)",
["Ship it"],
);
const notes = await pam.database.query(
"app",
"SELECT id, body FROM notes ORDER BY id DESC LIMIT ?",
[50],
);

Parameters may be null, booleans, signed 64-bit integers, finite numbers, or strings. Objects and arrays are rejected. Bind values with parameters; never concatenate user input into SQL.

Binary columns are deliberately absent from the JSON query API. Use an authorized file or streaming plugin for large binary data.

await pam.database.transaction("app", [
{
sql: "UPDATE accounts SET balance = balance - ? WHERE id = ?",
parameters: [50, 1],
},
{
sql: "UPDATE accounts SET balance = balance + ? WHERE id = ?",
parameters: [50, 2],
},
]);

Every statement succeeds and the transaction commits, or SQLite rolls the whole operation back. Read-write databases enable WAL, foreign keys, and a five-second busy timeout.

Resource Limit
SQL statement 64 KiB
Parameters per statement 1,024
Rows per query 10,000
Columns per query 256
Statements per transaction 256

The frontend selects a declared database name, never a path. Hot reload prepares the full replacement database service before swapping it into the gateway; an invalid replacement configuration cannot partially mutate the active capability set.