Database access and permissions

Petti 1.4 · Plugin API v1 · Experimental

Declaring a permission makes an operation eligible for approval. It does not bypass the database lock, grant every callback the same context, or authorize automatic writes.

Choose only the permissions you need

All ten names are case-sensitive. Unknown permissions fail validation. Callbacks expose different capabilities: previewers receive only a value; inspectors stay read-only; jobs do not receive command storage or arbitrary file access. The callback matrix is the authoritative guide.

Read with parameters and exact values

const result = await context.database.query(
  'SELECT name FROM sqlite_schema WHERE type = ?',
  [{type: 'text', value: 'table'}]
);
// Preserve result.truncated and tagged cell values in your output.

This is a callback fragment, not a complete package. The complete Schema Explorer example shows where it belongs. Each query uses a genuine read-only connection, even when Petti is unlocked. A query cannot execute DDL, write SQL, ATTACH, arbitrary PRAGMAs or load extensions.

Cells and parameters use tagged objects: integer and real values are decimal strings; text is UTF-8, blob is base64, and null omits the value. Output may use textBytes for invalid UTF-8 TEXT. Keep integer strings intact. Use placeholders for values; do not interpolate user input into SQL. Multiple calls are not a single consistent transaction.

Pick the correct write path

Reviewed actions and write jobs return inert statements for native review. Save then requires licensed unlocked access, creates a recovery snapshot and commits through the host. Direct command transactions are a separate API: they require native invocation confirmation but do not automatically snapshot every small edit or participate in cell Undo.

Use context.database.transaction(statements) only in the permitted command callback. The host owns BEGIN/COMMIT/ROLLBACK. Statements require exact expectedChanges; a mismatch rolls back the batch. Include the old value in an update predicate to reject stale edits. Row-count checks alone do not provide optimistic concurrency.

The restricted DML path accepts only supported ordinary-table INSERT, UPDATE and DELETE operations. It rejects triggers, virtual tables, unsupported schemas and raw DDL. App migrations have their own declarative API; they are not arbitrary SQL transactions.

Cancellation and safety limits

Commands normally have a three-second wall deadline; plugin jobs have 30 seconds. Database reads are bounded to 200 rows, 64 columns and transport limits, with at most eight calls per invocation and a 1.5-second query budget. Ordinary DML batches allow up to 20 statements and 100 expected changed rows. Native transfer/materialization phases have their own documented limits.

Cancellation before commit can roll back pending work. Cancellation or revocation after SQLite accepts a commit cannot undo it. Check reported committed outcomes before retrying. Closing or replacing the database, disabling the plugin, or relocking revokes applicable access. Do not write your own bypass or retry loop around these errors.