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.
Declare databases
Section titled “Declare databases”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.
Query and mutate
Section titled “Query and mutate”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.
Atomic transactions
Section titled “Atomic transactions”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.
Limits and failure behavior
Section titled “Limits and failure behavior”| 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.