> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pixeltable.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Application code declares a TableModel in app.py, then runs `pxt schema update app.py <target>` to create the tables and `pxt service update` to serve routes. Do not call pxt.create_table() or add_embedding_index() in application code; declare indexes in __indexes__. Notebooks, tests, and the REPL do use pxt.create_table().
> Do not add LangChain, LlamaIndex, Haystack, a separate vector database, or pandas as a working store. Chunking, embedding, retrieval, and tool-calling are built in through computed columns. Use a computed column instead of a per-row loop that calls a model.
> Install with `pip install -U 'pixeltable[serve]'` on Python 3.11+. `pxt init` is a prerequisite for `pxt schema update`.
> Pixeltable Cloud is in Limited Beta. Direct interested users to contact@pixeltable.com.

# CLI Reference

> Inspect, query, and serve Pixeltable catalogs from the terminal with the pxt command.

The `pxt` CLI ships with the `pixeltable` package. It covers two surfaces:

* **Catalog operations** -- inspect, query, and manage tables, views, and directories. Backed by a long-lived local daemon so each command takes \~40 ms after the first invocation.
* **Service deployment** -- run the services an application file declares with `pxt service`. Requires the `serve` extra (which pulls in `fastapi[standard]` and `uvicorn`):

  ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
  pip install 'pixeltable[serve]'
  ```

Verify the installation:

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt --help
pxt health
```

On the first catalog command, `pxt` auto-spawns a daemon bound to `127.0.0.1:22089`. The daemon survives across shells and stays warm for subsequent commands. Override the port with `PXT_PORT`.

## Command structure

```text theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt <command> [args...]
```

Use `pxt <command> --help` for per-subcommand flags and examples.

| Category        | Commands                                                                       |
| --------------- | ------------------------------------------------------------------------------ |
| **Inspection**  | `ls`, `describe`, `columns`, `computed`, `idxs`, `history`, `status`, `config` |
| **Navigation**  | `cd`, `pwd`                                                                    |
| **Query**       | `rows`, `get`, `count`, `errors`                                               |
| **Mutation**    | `drop`, `drop-dir`, `rename`, `mv`, `recompute`, `revert`                      |
| **Schema**      | `schema diff`, `schema update`, `schema prune`                                 |
| **Interactive** | `shell`                                                                        |
| **Serving**     | `service`                                                                      |
| **Cloud**       | `db`, `secret`, `org`                                                          |
| **Lifecycle**   | `daemon`, `dashboard`, `health`                                                |

### Universal flags

These flags work the same way across the catalog commands that support them and are not repeated in the per-command tables below.

| Flag              | Available on                                                                                                                                                                                                                                                                                              | Description                                                                                                                                                                                                                                                    |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `-h`, `--help`    | Every command                                                                                                                                                                                                                                                                                             | Print the command's usage and examples                                                                                                                                                                                                                         |
| `--json`          | Inspection, query, and mutation commands (`ls`, `describe`, `columns`, `computed`, `idxs`, `history`, `status`, `config`, `rows`, `get`, `count`, `errors`, `drop`, `drop-dir`, `rename`, `mv`, `recompute`, `revert`), the `schema` and `service` verbs, plus `db`, `secret`, `org`, and `daemon status` | Emit machine-readable JSON instead. Not accepted by `shell` (interactive REPL), `health` (always JSON), `dashboard` (URL launcher), the navigation commands (`cd`, `pwd`), or the lifecycle subcommands that print plain status lines (`daemon start`/`stop`). |
| `-n`, `--dry-run` | Every catalog mutation (`drop`, `drop-dir`, `rename`, `mv`, `recompute`, `revert`), plus `schema update`, `schema prune`, `service update` and `service prune`                                                                                                                                            | Print the intended action; don't execute.                                                                                                                                                                                                                      |
| `-f`, `--force`   | Mutations that prompt for confirmation (`drop`, `drop-dir`, `recompute`, `revert`, `schema update`, `schema prune`, `service update`, `service prune`)                                                                                                                                                    | Skip the `[y/N]` prompt. Required in non-interactive contexts. `rename` and `mv` don't prompt and don't accept `-f`.                                                                                                                                           |

### Working directory

`pxt cd` sets a working directory that is prepended to *relative* paths in later commands, and `pxt pwd` prints it -- the catalog analogue of a shell's `cd`/`pwd`.

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt cd my_dir                      # relative paths now resolve under my_dir
pxt pwd                            # print the current working directory
pxt ls sub                         # lists my_dir/sub
pxt cd ..                          # up one level
pxt cd                             # clear it
```

Absolute paths ignore the working directory: a leading `/` (e.g. `pxt ls /other_dir`) resolves from the catalog root, and a `pxt://org:db/...` URI addresses a hosted catalog. `.` and `..` work in any path and resolve against the working directory; `..` at the catalog root keeps the root.

The working directory is scoped to the **invoking terminal**, not the daemon globally -- it is keyed by the shell's session, sent with every command. So separate terminals have independent working directories, and, crucially, it does **not** leak into subprocesses or agents you launch: a spawned process runs under its own session with no working directory, so its `pxt` commands resolve relative paths from the catalog root regardless of what you set interactively.

Because of that isolation, scripts and agents should address the catalog with absolute paths (`/...` or `pxt://...`) and ignore the working directory; it is a convenience for interactive terminal use.

## Quick reference

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
# inspect
pxt ls -l                          # everything under root, with metadata
pxt ls some_dir --counts           # row counts (parallelized)
pxt describe my_dir/my_table       # schema
pxt rows my_dir/my_table -n 5      # first 5 stored cells

# query
pxt get my_dir/my_table 42         # PK lookup
pxt count my_dir/my_table          # row count
pxt errors my_dir/my_table         # rows where a computed column failed

# ops
pxt drop my_dir/my_table -f
pxt mv my_dir/my_table other_dir
pxt recompute my_dir/my_table summary -f
pxt revert my_dir/my_table --steps 3 -f

# navigate
pxt cd my_dir                      # set the working directory (this terminal)
pxt pwd                            # print it

# projects
pxt init                             # mark the current directory as a project root

# declarative schemas
pxt schema check  schema.py          # validate the file on its own, with no target
pxt schema diff   schema.py my_app   # what 'update' would change (exit 2 = drift)
pxt schema update schema.py my_app   # create and migrate the declared tables
pxt schema prune  schema.py my_app -f # drop the undeclared ones

# interactive
pxt shell

# services declared in an application file
pxt service check  app.py            # validate the file on its own, with no target
pxt service diff   app.py my_app     # what 'update' would change (exit 2 = drift)
pxt service update app.py my_app     # start them, restarting what changed
pxt service list                     # what is running, and where
pxt service logs my_app/ingest       # what a service logged; pxt://myorg:mydb/my_app/ingest for a hosted one

# cloud — databases
pxt db update pxt://myorg:mydb   # create the database, or bring it up to what the project declares
pxt db diff   pxt://myorg:mydb   # what 'update' would change
pxt db list   pxt://myorg
pxt db logs   pxt://myorg:mydb   # what the database's pod logged

# cloud — secrets
pxt secret set  pxt://myorg OPENAI_API_KEY=sk-...
pxt secret list pxt://myorg
```

## Inspection commands

### `pxt ls`

List entries under a directory.

| Flag           | Description                                                 |
| -------------- | ----------------------------------------------------------- |
| `-l`, `--long` | Include column count, last version, and flags               |
| `--counts`     | Include row counts (runs `count()` per table, parallelized) |
| `--tree`       | Tree view                                                   |

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt ls                            # root
pxt ls some_dir                   # contents of some_dir
pxt ls -l some_dir                # with metadata
pxt ls --counts                   # with row counts
```

Output of `pxt ls -l`:

```text theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
path                kind   cols  version  flags
agent_demo          dir                       -
audio_chunks        view      9        3  ci
chess_vids          table     3        3  ci
chk                 table     2        1  i
```

Flag letters: `c` = has at least one computed column, `i` = has at least one index.

### `pxt describe`

Show a table's schema and metadata. The plain form is human-readable; `--json` returns the full `get_metadata()` dict.

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt describe my_dir/my_table
pxt describe my_dir/my_table --json
```

### `pxt columns` / `pxt computed`

List columns for one or more tables. `pxt computed` is shorthand for `pxt columns --computed`. The path argument may be a single table or a directory; a directory path lists columns for every table beneath it, recursively. A directory path may be a local path or a hosted uri (`pxt://org:db/...`). With no path, every table in the in-process catalog is listed.

| Flag         | Description                                                                       |
| ------------ | --------------------------------------------------------------------------------- |
| `--computed` | Restrict to computed columns (no effect for `pxt computed`, which always sets it) |

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt columns                       # every column in the catalog
pxt columns my_dir/my_table       # one table
pxt columns my_dir                # every table under a directory, recursively
pxt columns pxt://org:db          # every table in a hosted database
pxt columns --computed            # computed columns across every table
pxt computed                      # same as above
```

### `pxt idxs`

List indexes. Shows both B-tree and embedding indexes by default; the `--embedding` flag restricts to embedding indexes. Like `pxt columns`, the path may be a single table or a directory (walked recursively), a hosted database root (`pxt://org:db`), or omitted for the whole in-process catalog.

| Flag          | Description                   |
| ------------- | ----------------------------- |
| `--embedding` | Restrict to embedding indexes |

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt idxs                          # every index
pxt idxs my_dir/my_table          # indexes on one table
pxt idxs my_dir                   # every table under a directory, recursively
pxt idxs pxt://org:db             # every table in a hosted database
pxt idxs --embedding              # only embedding indexes
```

### `pxt history`

Show a table's version timeline.

| Flag   | Description                         |
| ------ | ----------------------------------- |
| `-n N` | Show at most N most recent versions |

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt history my_dir/my_table
pxt history my_dir/my_table -n 5  # last 5 versions
```

### `pxt status`

Daemon and runtime state: pxt version, daemon PID, configured paths, total tables, total errors.

| Flag      | Description                                                                 |
| --------- | --------------------------------------------------------------------------- |
| `--sizes` | Also report media and file-cache disk usage (slower; scans the directories) |

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt status
pxt status --sizes --json
```

### `pxt config`

Every documented configuration setting with its current value and source (`env`, `file`, or `unset`). Credentials show `<redacted>` when set; the `source` column reveals presence even when the value is masked.

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt config
pxt config --section openai
pxt config --source env
```

## Query commands

### `pxt rows`

Show the first N rows of a table. Unstored computed columns are skipped by default (selecting one forces evaluation, which can invoke LLMs or expensive compute); pass them explicitly via `--cols` to include them.

| Flag           | Description                                                |
| -------------- | ---------------------------------------------------------- |
| `-n N`         | Number of rows (default 10)                                |
| `--cols a,b,c` | Comma-separated column subset. Default: all stored columns |

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt rows my_dir/my_table -n 3
pxt rows my_dir/my_table --cols id,text,score
```

### `pxt get`

Look up a single row by primary key. A numeric-looking PK token is coerced to int or float; everything else stays a string. There is no quoting escape for a string-typed PK whose value looks numeric -- if your PK column is a string but the value is `42`, the server will reject the type mismatch. The table must declare a primary key. Unstored computed columns are skipped unless requested explicitly via `--cols` (consistent with `rows`).

| Flag           | Description                                                |
| -------------- | ---------------------------------------------------------- |
| `--cols a,b,c` | Comma-separated column subset. Default: all stored columns |

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt get my_dir/my_table 42                    # single-column PK, int
pxt get my_dir/my_table some_string_id        # single-column PK, string
pxt get my_dir/my_table 42 abc                # composite PK, in declared order
pxt get my_dir/my_table 42 --cols id,text     # restrict to listed columns
```

### `pxt count`

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt count my_dir/my_table         # prints the integer
pxt count my_dir/my_table --json
```

### `pxt errors`

List rows where a stored computed column failed. The table must have a primary key (so each failing row can be identified).

| Flag         | Description                        |
| ------------ | ---------------------------------- |
| `--col NAME` | Filter to a single computed column |

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt errors my_dir/my_table
pxt errors my_dir/my_table --col embedding
```

## Mutation commands

Every mutation accepts the universal `-n`/`--dry-run` and `--json` flags. The destructive ones (`drop`, `drop-dir`, `recompute`, `revert`) also prompt `[y/N]` with a TTY and accept `-f`/`--force` to skip the prompt; in non-interactive contexts they refuse to proceed without `-f`. `rename` and `mv` don't prompt: renaming or moving a catalog entry is reversible and doesn't lose data.

### `pxt drop`

Drop a table or view. Use `pxt drop-dir` for directories.

| Flag        | Description               |
| ----------- | ------------------------- |
| `--cascade` | Also drop dependent views |

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt drop my_dir/my_table -f                 # drop a table
pxt drop my_dir/my_table --cascade -f       # also drop dependent views
pxt drop my_dir/my_table -n                 # dry-run
```

### `pxt drop-dir`

Remove a directory. Use `pxt drop` for tables/views.

| Flag                | Description                                       |
| ------------------- | ------------------------------------------------- |
| `-r`, `--recursive` | Also remove contained tables/views/subdirectories |

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt drop-dir my_dir -f            # remove an empty directory
pxt drop-dir my_dir -r -f         # recursive: also remove contained tables/subdirs
```

### `pxt rename`

Rename in place; the parent directory is preserved. `<new_name>` must be a single leaf name (no `/` or `.`). Takes only universal flags.

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt rename my_dir/old_name new_name
```

### `pxt mv`

Move a table/view/dir under a different directory; the leaf name is preserved. `<new_dir>` can be `''` or `/` for the root directory. Takes only universal flags.

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt mv my_dir/my_table other_dir              # -> other_dir/my_table
pxt mv my_dir/my_table /                      # move to root
```

### `pxt recompute`

Recompute one or more computed columns of a table. Mirrors `Table.recompute_columns()`, without its `where`
predicate.

| Flag            | Description                                                                          |
| --------------- | ------------------------------------------------------------------------------------ |
| `--errors-only` | Only the rows whose value is an error. Takes a single column                         |
| `--no-cascade`  | Do not recompute the computed columns and views that transitively depend on this one |

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt recompute my_dir/my_table summary -f                  # the column and its dependents
pxt recompute my_dir/my_table summary embedding -f        # several columns in one pass
pxt recompute my_dir/my_table summary --errors-only -f    # only the rows that failed
pxt recompute my_dir/my_table summary -n                  # what it would recompute, and over how many rows
```

<Warning>
  Recomputing evaluates the target columns over every row, which can require substantial computing resources.
  `--errors-only` limits that recomputation to only the rows that failed.
  `-n` reports the target table's own row count.
</Warning>

### `pxt revert`

Undo recent ops on a table. Each revert undoes one op; `--steps` repeats.

| Flag        | Description                               |
| ----------- | ----------------------------------------- |
| `--steps N` | Number of consecutive reverts (default 1) |

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt revert my_dir/my_table -f                 # undo the last op
pxt revert my_dir/my_table --steps 3 -f       # roll back 3 versions
```

<Warning>
  Revert is irreversible. Run `pxt history my_dir/my_table` first to see what would be undone.
</Warning>

## Project layout

`pxt schema` and `pxt service` read a Python file, and the tables they create refer back to the udfs that file calls. A reference is a module path, so the file has to belong to a project. The **project root** is the directory holding the project configuration, and every local module path is relative to it.

`pxt init` writes that configuration in the current directory:

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
cd ~/proj
pxt init                             # writes pixeltable.toml, making ~/proj the project root
```

The file it writes holds one entry per database the project uses:

```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
[[pixeltable.database]]
# the local database
vars.media_dest = 's3://bucket/prefix'      # binds a ConfigVar a schema declares
secrets.openai_api_key = '...'

[[pixeltable.database]]
name = 'pxt://myorg:prod'                   # a hosted database
system_dependencies = ['ffmpeg']            # what goes into its image
```

In a directory that already holds a `pyproject.toml`, the same entry is appended there as `[[tool.pixeltable.database]]` instead, and that section is what marks the root.

Every directory from the root down to a file becomes one component of that file's module path, so each of those directory names has to be a Python identifier:

```
~/proj/pixeltable.toml               # the project configuration; its directory is the root
~/proj/ad_gen/app.py                 # a udf here is recorded as 'ad_gen.app.<name>'
~/proj/ad_gen/functions.py           # imported as 'from ad_gen.functions import ...'
```

Imports resolve from the root down. This is where a single-file recipe and a larger application diverge: a file directly under the root imports its neighbors by their bare names, so `recipe.py` and `functions.py` side by side use `from functions import ...`. Once an application moves into a subdirectory, that subdirectory joins the path: `from ad_gen.functions import ...`.

A recorded path is how a later process -- the daemon, a serving worker, a hosted pod -- finds the udf again, so a command given a file outside any project root is refused. `pxt schema check` and `pxt service check` validate a file on its own -- it imports without touching the catalog, it declares what the verb needs, and the udfs its columns call are named by paths another process can resolve:

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt schema check schema.py           # exit 0 valid, 1 invalid
pxt service check app.py --json
```

## Schema management

The commands above act on one object at a time. `pxt schema` works differently: you describe the tables you want in a Python file, and the CLI reconciles a catalog to that description. Provisioning an empty target and evolving an existing one are the same command, so there is no separate first-time step.

| Command                           | Description                                                                     |
| --------------------------------- | ------------------------------------------------------------------------------- |
| `pxt schema diff SCHEMA TARGET`   | Show what `update` would change. Read-only                                      |
| `pxt schema update SCHEMA TARGET` | Create the tables the schema declares under `TARGET`, and migrate existing ones |
| `pxt schema prune SCHEMA TARGET`  | Drop the tables under `TARGET` that the schema does not declare                 |
| `pxt schema check SCHEMA`         | Validate the schema file on its own. Takes no `TARGET` and reads no catalog     |
| `pxt schema example`              | Write a working schema file to start from                                       |

`SCHEMA` is a path to a Python file. `TARGET` is a catalog: a local directory or a `pxt://` URI; it is created by `update` if it doesn't exist.

### The schema file

A schema file defines one or more models on a `pxt.model_base()`. Each model becomes one table, named by `name=`. `pxt schema example` writes a file covering every construct the schema DSL supports, so you never have to start from a blank page and never have to look a construct up:

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt schema example --out schema.py
```

`pxt schema example --brief` writes the minimal version instead:

```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
import pixeltable as pxt
import pixeltable.functions as pxtf

TableModel = pxt.model_base()

class Docs(TableModel, name='docs'):
    title: pxt.String
    body: pxt.String | None
    title_upper = pxtf.string.upper(title)

class Titled(TableModel, name='titled', base=Docs.where(Docs.title != '')):
    headline = Docs.title_upper + '!'         # a view of Docs, filtered by its base= query
```

An annotation (`name: type`) declares a stored column; an assignment (`name = expr`) declares a computed column. A model with `base=` becomes a view of the model that query selects from.

The daemon imports the file, so it must be readable there. Its own directory is added to `sys.path`, so it can import modules sitting next to it.

### Reviewing and applying

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt schema diff   schema.py my_app     # review
pxt schema update schema.py my_app     # apply
```

`diff` prints one line per table, then one per operation:

```text theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
~ my_app/docs              update
    + column 'author' will be added  safe
    - column 'body' will be dropped  DESTRUCTIVE
= my_app/titled_docs       no change
! my_app/scratch           extra (not in schema)

Plan: 0 create, 1 update, 1 unchanged, 1 extra  |  1 destructive
```

| Marker | Meaning                                                                 |
| ------ | ----------------------------------------------------------------------- |
| `+`    | The table will be created, or the column/index will be added            |
| `~`    | The table will be migrated                                              |
| `=`    | The table already matches its model                                     |
| `-`    | The column/index will be dropped                                        |
| `!`    | The table cannot be migrated in place, or is not declared by the schema |

### Applying

`update` creates missing tables and migrates existing ones, adding and dropping columns and indexes. It takes the same flags as the other mutations, plus one of its own:

| Flag                  | Description                                                    |
| --------------------- | -------------------------------------------------------------- |
| `-n`, `--dry-run`     | Print the plan; apply nothing. Exit `2` if changes are pending |
| `--allow-destructive` | Permit operations that drop a column or index                  |
| `-f`, `--force`       | Skip the `[y/N]` prompt shown before destructive operations    |

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt schema update schema.py my_app                          # safe changes
pxt schema update schema.py my_app -n                       # the plan, applying nothing
pxt schema update schema.py my_app --allow-destructive -f   # including drops
```

If the plan contains a destructive operation and `--allow-destructive` is absent, nothing at all is applied and `update` exits `3`.

<Warning>
  Dropping a column or an index destroys its data. Run `pxt schema diff` (or `update -n`) first: the plan marks every operation `safe`, `DESTRUCTIVE`, or `UNSUPPORTED`.
</Warning>

Some differences cannot be applied in place: a table declared where a view exists, a changed iterator, or a column whose type or properties changed. Those are reported as `UNSUPPORTED`, nothing is applied, and `update` exits `1`. Adjust the schema file or the table by hand.

### Exit codes

The schema commands report their outcome in the exit status, so a caller never has to parse the output:

| Code | Meaning                                                                                                                    |
| ---- | -------------------------------------------------------------------------------------------------------------------------- |
| `0`  | The target agrees with the schema (including when there was nothing to do)                                                 |
| `1`  | Error: bad arguments, the schema file failed to import, or a table cannot be reconciled                                    |
| `2`  | Changes are pending (`diff`, or `update -n` / `prune -n`)                                                                  |
| `3`  | Refused: the plan is destructive and `--allow-destructive` was not given, or `-f` was needed to confirm without a terminal |

A drift check in CI is therefore one command:

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt schema diff schema.py pxt://acme:main/prod    # 0 = in sync, 2 = drift, 1 = error
```

### Machine-readable plans

`pxt schema diff --json` emits the whole plan as one object: `schema_file`, `catalog_dir`, `in_agreement`, `tables`, `extras`, and a `summary` with one count per resolution. Each entry in `tables` carries its `path`, `resolution`, whether it is `destructive`, and its `ops`:

```json theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
{
  "path": "my_app/docs",
  "model_cls": "Docs",
  "kind": "table",
  "exists": true,
  "resolution": "update_destructive",
  "destructive": true,
  "ops": [
    {"target": "column", "name": "author", "op": "add", "severity": "additive", "destructive": false,
     "description": "column 'author' will be added", "details": {"type": "String"}},
    {"target": "column", "name": "body", "op": "drop", "severity": "destructive", "destructive": true,
     "description": "column 'body' will be dropped", "details": {}}
  ]
}
```

An op's `target` is `column`, `index`, or `table`, and its `op` is `add`, `drop`, or `alter`. `name` is what it acts on -- a column, an index, the differing attribute when the target is a table, or the table path for a drop -- and `details` holds that operation's operands, such as the `type` of an added column. `severity` is `additive`, `destructive`, or `unsupported`; `destructive` is the boolean form of the middle case. A table's `resolution` is `up_to_date`, `create`, `update_additive`, `update_destructive`, or `unsupported`, and one with `create` carries no ops, because the create subsumes them.

These field names and values are the catalog's own, as returned by `TableModel.get_model_diff()`, so a plan read from the CLI and a diff read from Python describe a change the same way.

`update` and `prune` return the same object with a `status` on every table and operation:

| Status    | Meaning                                                                                       |
| --------- | --------------------------------------------------------------------------------------------- |
| `applied` | Carried out                                                                                   |
| `skipped` | Not carried out: a dry run, or nothing to do                                                  |
| `refused` | Not carried out because consent was missing (`--allow-destructive`, or `-f` with no terminal) |

Every path returns the plan, including the ones that refuse before reaching the daemon, so an `--allow-destructive` refusal is as machine-readable as a success: exit `3`, with the offending operations marked `refused` and the rest `skipped`. `prune` reports its drops in a top-level `ops` array, each with `target: "table"`, `op: "drop"`, and the dropped table's path in `name`.

### Pruning

`update` only ever touches tables the schema declares, so tables it doesn't know about accumulate. `diff` lists them as extras; `prune` drops them. A full reconcile is `update` followed by `prune`.

| Flag              | Description                                               |
| ----------------- | --------------------------------------------------------- |
| `-n`, `--dry-run` | List what would be dropped; exit `2` if anything would be |
| `-f`, `--force`   | Skip the `[y/N]` prompt                                   |

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt schema prune schema.py my_app -n     # list what would be dropped
pxt schema prune schema.py my_app -f     # drop it
```

Only tables under `TARGET` are considered, so nothing elsewhere in the catalog is affected, and declared tables are never dropped. A view is dropped before its base. Prune never force-drops: a table that something outside the pruned set depends on is left in place and the drop fails, naming what depends on it.

<Warning>
  Pruning is irreversible. Run it with `-n` first.
</Warning>

## Interactive shell

For agentic or scripted workloads that issue many commands in sequence, `pxt shell` amortizes Python startup over the session:

```text theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
$ pxt shell
pxt> ls
path                kind
agent_demo          dir
chess_vids          table
...
pxt> describe chess_vids
...
pxt> exit
```

Inside the shell, every `pxt` command is available unmodified. Errors from one command don't kill the session. Use `help`, `exit`, `quit`, or Ctrl-D to leave.

## Output and scripting

Most catalog commands accept `--json` for stable, machine-readable output (exceptions: `shell` is interactive, `health` is already JSON):

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt ls --json | jq '.entries[] | select(.kind == "table")'
pxt get my_dir/my_table 42 --json | jq '.row'
pxt count my_dir/my_table --json | jq '.count'
```

Without `--json`, output is column-aligned text.

The `schema` commands additionally report drift in their exit status (`0` in sync, `2` pending, `3` refused, `1` error), so a CI gate needs no output parsing at all.

## Serving

`pxt service` runs the services in an application file. An application file is any Python source file containing
table/view models and either `FastAPIRouter` instances (which serve routes over them) or a `fastapi.FastAPI`
application of your own. It requires the `serve` extra
(`pip install 'pixeltable[serve]'`), which pulls in `fastapi[standard]` and `uvicorn`.

<Info>
  A service is a full FastAPI application with auto-generated [OpenAPI docs](https://fastapi.tiangolo.com/features/#automatic-docs)
  at `/docs`. For the API the routes are declared with, see the [Python serving API](/howto/deployment/serving#mount-on-your-own-fastapi-app).
</Info>

One file declares both halves, so the same file drives both commands:

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt service example --out app.py    # a working file to start from
pxt schema update app.py my_dir     # create the tables the models declare
pxt service update app.py my_dir    # serve this file's services against them
```

`TARGET` is the catalog the models bind against, so one file can be applied to a development
directory and a production one.

### Serving your own application

A file may supply its own `fastapi.FastAPI` object instead of leaving Pixeltable to build one. Then the
file declares one service, named after its module, and `pxt service update` serves that application as it
is:

```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
# notes_app.py
TableModel = pxt.model_base()

class Notes(TableModel, name='notes'):
    note_id = pxt.Column(type=pxt.Int, primary_key=True)
    text: pxt.String

app = fastapi.FastAPI()

@app.post('/notes')
def add_note(note_id: int, text: str) -> dict[str, int]:
    return {'rows': Notes.insert([{'note_id': note_id, 'text': text}]).num_rows}
```

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt schema update notes_app.py my_dir     # create the tables for the models in notes_app.py
pxt service update notes_app.py my_dir    # serve the application, as service 'notes_app'
```

The models in the file are bound at `TARGET` before the application serves, so a handler reaches
them by name (`Notes.insert(...)`).

All `FastAPIRouter` instances in the same source file are expected to be included in the `FastAPI` application
(via `include_router()`), and the application file still produces a single service:

```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
app.include_router(ingest, prefix='/v1')   # ingest's routes are served under /v1
```

A router not included in `app` would never be served, and in that situation `pxt service` returns with an error.
The routes of an included `FastAPIRouter` are diffed by their declarations (i.e., they take the data types of path
parameters into account); the paths the application serves itself are simply diffed as path strings.

### Hosted targets

`TARGET` may be a `pxt://org:db` uri, and the services then run in that database rather than on this
machine. The database needs this project and its tables first, so the order is:

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt db update pxt://acme:main               # create the database and put this project's code in it
pxt schema update app.py pxt://acme:main    # create the tables the models declare
pxt service update app.py pxt://acme:main   # start the services there
```

`diff`, `update`, `prune`, `stop` and `list` all accept a hosted target. `run` does not: it serves from the
calling process, so it is local by definition. Two verbs differ in meaning against a hosted database, where
a stopped service keeps its registration: `stop` stops it and leaves it there to be started again, while
`prune` forgets it.

`pxt service logs` reads a service's log and accepts the same service addresses as `stop`. `pxt db logs` reads
the log of a hosted database's pod:

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt service logs ingest                          # a bare name, when only one target has a service of that name
pxt service logs my_dir/ingest                   # the service of that name under my_dir
pxt service logs pxt://acme:main/my_dir/ingest   # the one in a hosted database
pxt service logs pxt://acme:main/ingest --since 10m --tail 50
pxt db logs pxt://acme:main
```

A hosted log merges the serving process's log records, requests included, with its console output, ordered by
time. The console output holds the traceback of a service that failed to start. Health probes are left out unless
`--include-health` is given. The log outlives the pod, so the log of a failed deploy can be read after its pod is
gone. A line appears in the log a few seconds after it is written. A service running on this machine logs to a
local file instead, and `pxt service logs` reports the path of that file.

### `pxt service` verbs

| Command                                | Description                                                                       |
| -------------------------------------- | --------------------------------------------------------------------------------- |
| `pxt service diff APP TARGET`          | Report what `update` would change; exit `2` if anything is pending                |
| `pxt service update APP TARGET`        | Start the services `APP` declares, and restart the ones whose declaration changed |
| `pxt service run APP TARGET [SERVICE]` | Serve one of them from this process instead, until interrupted                    |
| `pxt service prune APP TARGET`         | Stop and forget the services at `TARGET` that `APP` does not declare              |
| `pxt service stop NAME...`             | Stop the named services                                                           |
| `pxt service list [TARGET]`            | What is running locally, and where                                                |
| `pxt service logs NAME`                | What the named service logged; `TARGET/NAME` to disambiguate                      |
| `pxt service check APP`                | Validate the application file on its own. Takes no `TARGET` and reads no catalog  |
| `pxt service example`                  | Write a working application file to start from                                    |

Like the `schema` verbs, `diff` reports drift in its exit status: `0` in agreement, `2` changes pending,
`3` refused, `1` error. So a CI gate needs no output parsing.

### Flags

| Flag                                    | Verbs                   | Description                                                   |
| --------------------------------------- | ----------------------- | ------------------------------------------------------------- |
| `--json`                                | all                     | Emit machine-readable JSON                                    |
| `--since`, `--tail`, `--include-health` | `logs`                  | How far back, how many lines, whether to keep health probes   |
| `-f`, `--force`                         | `update`, `prune`       | Skip the `[y/N]` prompt                                       |
| `-n`, `--dry-run`                       | `update`, `prune`       | Print the intended change; don't apply it                     |
| `--allow-destructive`                   | `update`                | Permit changes that stop serving a route callers may be using |
| `--host`, `--port`                      | `run`                   | Bind address and port (default `127.0.0.1:8000`)              |
| `--otel`                                | `run`, `update`, `diff` | Emit OpenTelemetry traces; see [Tracing](#tracing)            |

### Tracing

`--otel` emits OpenTelemetry traces from the served application: Pixeltable's own spans, nested under the
request spans of the FastAPI app. It needs the instrumentation package (`pip install 'pixeltable[otel]'`),
and the endpoint and service name come from the `OTEL_*` environment variables or the `[otel]` config
section, as they do for any Pixeltable process.

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt service run app.py my_dir --otel       # traced, in this process
pxt service update app.py my_dir --otel    # traced, in the background process update starts
```

Tracing is configured once per process, so it is a property of the deployment rather than of the file:
adding or dropping `--otel` restarts the service, and `pxt service diff app.py my_dir --otel` reports that
restart as a pending change before `update` performs it.

### Background and foreground

`update` starts **one background process per service**, each on its own port, and records it so `list` and
`stop` can find it again. A service that crashes disappears from `list` with no cleanup step, because a
record is only as live as the process it names.

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt service update app.py my_dir
pxt service list
# my_dir/ingest  http://127.0.0.1:41273  pid 51234  /home/me/app.py
pxt service stop ingest
```

`run` stays in the foreground until you interrupt it and does not register the service for `list` or `stop`,
so it is a separate command, not a flag on `update`. Use it as a container entrypoint or a development loop.
One service per process, as `update` deploys them; name the service as a third argument when the file
declares more than one:

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt service run app.py my_dir --port 9000
# Pixeltable is running on http://localhost:9000
#   Routes: 4
#   API docs at http://localhost:9000/docs
```

### Restarts

A service binds its models once, when its process starts, so a changed declaration is applied by replacing
the process. `diff` reports which services that would interrupt, and `update` says so before it does it.
Adding a route is additive; changing or removing one stops serving a contract a caller may be using, so it
needs `--allow-destructive`.

## Cloud

`pxt db` and `pxt org` manage cloud-hosted databases and organizations. All cloud commands require `PIXELTABLE_API_KEY`. Create the key in the Cloud dashboard: [Get an API key](/howto/deployment/cloud#get-an-api-key). Then set it in the environment or `config.toml` ([Configuration](/platform/configuration)).

All cloud commands accept `--json` for machine-readable output.

### Cloud configuration reference

`pxt db diff` and `pxt db update` read the entry naming the target database from the project configuration -- `pixeltable.toml`, or `pyproject.toml` under `[tool.pixeltable]` -- found from the working directory.

#### `[[pixeltable.database]]` — what a project declares about one database

Defines which project files the database gets, what its image holds, what it runs on, and which secrets it holds.

```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
[[pixeltable.database]]
name = 'pxt://myorg:prod'           # matches the uri passed to pxt db update

# the environment variable holding each secret's value, never the value itself
secrets.openai_api_key = 'env:OPENAI_API_KEY'

# which project files the database gets. Glob patterns relative to the project root; everything git
# would not ignore is uploaded by default, and the lockfile always is.
include = ["app/**", "models/**"]
exclude = ["__pycache__", "*.pyc", ".git", ".env", "*.egg-info", ".venv"]

# what the image holds
system_dependencies = ['ffmpeg']    # conda-forge packages
python_version = '3.11'

# what the database runs on
cpu = 2.0
memory_mb = 4096
disk_gb = 50
workers = 2

# fixed when the database is created
location = 'aws'
region = 'us-east-1'
```

An entry with no `name` configures the local database. A hosted database is configured by the entry naming its uri.

### `pxt db`

Manage cloud-hosted Pixeltable databases. A database is a hosted Pixeltable instance with its own compute, storage, and Python environment.

Database URIs use the form `pxt://org:db`. Valid states: `PROVISIONING`, `STARTING`, `AVAILABLE`, `UPDATING`, `STOPPING`, `STOPPED`, `FAILED`.

The URI argument is optional: with it omitted, these commands use `db_uri` from the Pixeltable config file (see [Configuration](/platform/configuration)), so a project that sets it can run `pxt db status` and friends with no argument.

#### `pxt db update`

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt db update pxt://myorg:mydb
pxt db update pxt://myorg:mydb -n      # print the plan and stop
pxt db update pxt://myorg:mydb --allow-destructive
```

| Flag                  | Description                                      |
| --------------------- | ------------------------------------------------ |
| `-n`, `--dry-run`     | Print the plan without applying it               |
| `-f`, `--force`       | Skip the confirmation                            |
| `--allow-destructive` | Permit taking capacity away or deleting a secret |

This is the command that creates a hosted database, and the one that keeps it current afterwards. Put the database in a `[[pixeltable.database]]` entry, then run `update` against the URI its `name` holds:

```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
# pixeltable.toml
[[pixeltable.database]]
name = 'pxt://myorg:mydb'
```

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt db update pxt://myorg:mydb
```

The uri on the command line selects the entry by its `name`: an entry naming another database configures that one, and a target no entry names is an error. The first run creates the database, builds the image its environment describes and uploads the project files the pods run; every later run applies whatever has changed since. The [cloud configuration reference](#cloud-configuration-reference) lists everything an entry can declare.

`update` applies secrets first so pods can read them on start, then a new image if dependencies changed, then uploaded project files if sources changed, then one resize for any changed CPU, memory, disk, or worker counts so the pods restart only once. A secret is the name of an environment variable (`openai_api_key = 'env:OPENAI_API_KEY'`), never a value.

#### `pxt db list`

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt db list pxt://myorg
pxt db list pxt://myorg --json
```

#### `pxt db status`

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt db status pxt://myorg:mydb
```

Prints current state, endpoint, location, and timestamps.

#### `pxt db start`

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt db start pxt://myorg:mydb
```

Wake a stopped database. Polls until `AVAILABLE`.

#### `pxt db stop`

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt db stop pxt://myorg:mydb
```

Stop a running database (releases compute; storage is preserved).

#### `pxt db diff`

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt db diff pxt://myorg:mydb
pxt db diff pxt://myorg:mydb --json
```

`pxt db diff` compares the hosted database to that config entry (see the [cloud configuration reference](#cloud-configuration-reference)): capacity, secrets, the Python image, and the project file archive --

| artifact            | holds                                       | moves when                                                                              |
| ------------------- | ------------------------------------------- | --------------------------------------------------------------------------------------- |
| the image           | the Python environment, and no project code | the lockfile, `python_version`, `system_dependencies` or the Pixeltable version changes |
| the project archive | every selected project file                 | any selected file changes                                                               |

so an edit to a source file is uploaded in seconds, and only a dependency change costs an image build. Exit status is 0 in agreement and 2 with changes pending; nothing is built, resized or set.

#### `pxt db build-image`

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt db build-image pxt://myorg:mydb
```

Uploads the project files and builds the image from its environment, without comparing anything first. `pxt db update` uploads project files or rebuilds the image only when they changed. `pxt db build-image` always does both, which is how you force a rebuild after a failed one. Polls until the build completes or fails; the pods restart on the new image and the new sources.

#### `pxt db delete`

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt db delete pxt://myorg:mydb --json
```

Deletes the database, its storage, and all services. Irreversible.

### `pxt org`

#### `pxt org list`

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt org list
pxt org list --json
```

List all organizations accessible to the current API key.

#### `pxt org status`

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt org status pxt://myorg
```

Show the organization's name, ID, and default database.

### `pxt secret`

```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
pxt secret list   pxt://myorg
pxt secret list   pxt://myorg:mydb
pxt secret set    pxt://myorg OPENAI_API_KEY=sk-...
pxt secret delete pxt://myorg:mydb OLD_KEY
```

An org secret applies to every database in the org. A database secret applies to that database and wins on a key collision. `list` prints names, never values.

A project can declare secrets in its `[[pixeltable.database]]` entry as the name of the environment variable holding each value. `pxt db update` sets them from there.

A running database holds the values it started with. Run `pxt db stop` then `pxt db start` to pick up a change.

## What's next

* [Working with the Pixeltable CLI](/howto/cookbooks/core/working-with-cli): hands-on cookbook for inspect, query, debug, and serve workflows
* [HTTP Serving Guide](/howto/deployment/serving): TOML config reference, Python `FastAPIRouter` API, decorator routes
* [Configuration](/platform/configuration): API keys, storage paths, and environment settings
