← Files PixeltableARCHIVED FILE

references/cli.md

14.7 KB · Sep 30, 2026 · 23:16 UTC

↓ Download file

# Pixeltable CLI Reference (`pxt`)

Agent-focused map of the `pxt` CLI. Official source: [platform/cli.md](https://docs.pixeltable.com/platform/cli.md). Always run `pxt <command> --help` for version-specific flags -- never guess.

Python 3.11+. There is no `pxt serve`, no `pxt deploy`, no `pxt app`, no `pxt db create` (the CLI's own help string wrongly lists one -- `pxt db update` creates), and no `[tool.pixeltable.service]` TOML.

## Two surfaces

| Surface | Purpose | Requires |
|---------|---------|----------|
| **Catalog** | Inspect, query, mutate tables/views/dirs | `pip install pixeltable` |
| **Schema / service** | Apply a `TableModel` file; run `FastAPIRouter` services | `pip install 'pixeltable[serve]'` for `pxt service` |

Verify: `pxt --help` and `pxt health`.

## Project root

`pxt init` marks this directory as a project root. Schema and service refuse an application file with no project root. Fresh dir: writes `pixeltable.toml`. Already configured: no-op. Existing `pyproject.toml`: appends `[[tool.pixeltable.database]]` (does not write `pixeltable.toml`). Nested under another root: refused (exit 3). Start from `pxt service example --out app.py` (models plus routes) or `pxt schema example --brief --out app.py` (models only).

```bash
pxt init                          # project root; see cases above
pxt schema update app.py my_app   # creates catalog dir + tables; does NOT start HTTP
pxt service update app.py my_app  # starts local HTTP; does NOT create tables
pxt service update app.py my_app -f   # CI / no TTY when changes are pending
```

`my_app` is a catalog directory, not a folder on disk. After apply: `t = pxt.get_table('my_app.docs')`. Cloud: `t = pxt.get_table('pxt://org:db/docs')`. Tables live under `~/.pixeltable`, not in the repo. Directory names from the project root to `app.py` must be Python identifiers. Do not `python app.py` if the file only declares models and routers. After a schema change, run `pxt service update` again if routes exist.

## Daemon

On the first catalog command, `pxt` auto-spawns a daemon at `127.0.0.1:22089` (~40 ms per command after warm-up). Override with `PXT_PORT`. Lifecycle: `pxt daemon status`, `pxt daemon stop`, `pxt daemon start`.

## Command categories

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

`cd` / `pwd` set and print a working directory prepended to relative paths. It is scoped to the invoking shell's process, so it does **not** survive between separate tool calls: always pass full catalog paths instead. `localproxy` manages the daemons behind `pxt://local:<db>` URIs and is not part of the normal app loop.

## Universal flags

| Flag | Description |
|------|-------------|
| `-h`, `--help` | Every command |
| `--json` | Machine-readable output on catalog commands, `schema` / `service` verbs, `db`, `org`, `secret`, `daemon status`. Not on `shell`, `dashboard`, or `daemon start`/`stop`. `health` is always JSON. |
| `-n`, `--dry-run` | Catalog mutations (`drop`, `drop-dir`, `rename`, `mv`, `revert`) plus `schema update`, `schema prune`, `service update`, `service prune`, and `db update` |
| `-f`, `--force` | Skip `[y/N]` on `drop`, `drop-dir`, `revert`, schema/service update and prune, and `db update`. Use it in non-interactive runs when a pending plan can prompt. Additive or no-op schema updates do not prompt. Not on `rename`/`mv`. |

## Agent workflows

| Task | Prefer CLI | Example |
|------|-----------|---------|
| Mark a project root | `pxt init` | no-op if already configured; exit 3 if nested |
| Write a starting file | `pxt service example` | `pxt service example --out app.py`. Models only: `pxt schema example --brief --out app.py` |
| Validate a file | `pxt schema check`, `pxt service check` | no `TARGET`; reads no catalog |
| Apply tables | `pxt schema update` | `pxt schema update app.py my_app` |
| Review schema drift | `pxt schema diff` | exit `0` in sync, `2` pending |
| Start HTTP | `pxt service update` | `pxt service update app.py my_app -f` (force pending changes in CI) |
| Inspect catalog | `pxt ls -l`, `pxt describe`, `pxt columns --computed` | `pxt ls --json \| jq '.entries[] \| select(.kind == "table")'` |
| Debug failed columns | `pxt errors`, `pxt rows --cols` | `pxt errors my_app/docs --col embedding` |
| Debug a service | `pxt service logs` | `pxt service logs my_app/ingest --since 10m --tail 50` |
| Check runtime/config | `pxt status`, `pxt config` | `pxt config --section openai` |
| Many commands in sequence | `pxt shell` | amortizes startup; errors don't kill session |
| Visual inspection | `pxt dashboard` | read-only UI at daemon port |
| Hosted database | `pxt db update` | `pxt db update pxt://myorg:mydb -f`, then schema, then service |

**SDK vs CLI:** Notebooks and one-off REPL use the Python SDK (`create_table`, `add_computed_column`). Apps use a `TableModel` file plus `pxt schema` / `pxt service`. Use CLI for inspect, debug, and CI drift checks.

## Quick reference

```bash
# project, then schema, then service
pxt init
pxt service example --out app.py
pxt schema update app.py my_app
pxt service update app.py my_app

# inspect
pxt ls -l
pxt describe my_app/docs
pxt rows my_app/docs -n 5

# query / debug
pxt get my_app/docs 42
pxt count my_app/docs
pxt errors my_app/docs

# mutations (use -f in CI)
pxt drop my_app/docs -f
pxt revert my_app/docs --steps 3 -f

# interactive
pxt shell
pxt dashboard
```

## Inspection highlights

- **`pxt ls`**: `-l` (metadata), `--counts` (row counts), `--tree`
- **`pxt describe`**: schema; `--json` returns full `get_metadata()` dict
- **`pxt computed`**: shorthand for `pxt columns --computed`
- **`pxt idxs`**: `--embedding` for embedding indexes only
- **`pxt history`**: `-n N` for last N versions (run before `revert`)
- **`pxt status`**: daemon PID, version, total errors; `--sizes` for disk usage

## Query highlights

- **`pxt rows`**: `-n N` (default 10), `--cols a,b,c`. Unstored computed columns skipped unless listed in `--cols` (forces eval).
- **`pxt get`**: PK lookup; composite PKs in declared order. Table must have a primary key.
- **`pxt errors`**: rows where stored computed columns failed; `--col NAME` to filter. Table must have a primary key.

## Mutation highlights

- **`pxt drop`**: tables/views; `--cascade` drops dependent views; use `pxt drop-dir` for directories
- **`pxt schema prune`**: never force-drops, and drops a view before its base; a table something outside the pruned set depends on is left in place
- **`pxt drop-dir`**: `-r` for recursive directory removal
- **`pxt revert`**: irreversible -- run `pxt history` first

Table paths accept `my_app/docs` or `my_app.docs`.

## Schema (`pxt schema`)

Reconcile a catalog directory with the `TableModel` classes in a Python file. Provisioning an empty target and evolving an existing one are the same command.

| Command | Description |
|---------|-------------|
| `pxt schema diff APP TARGET` | What `update` would change. Read-only. Exit `2` if pending |
| `pxt schema update APP TARGET` | Create the catalog dir + tables; migrate existing ones. Does **not** start HTTP |
| `pxt schema prune APP TARGET` | Drop tables under `TARGET` that the file does not declare |
| `pxt schema check APP` | Validate the file only. No `TARGET`. Reads no catalog |
| `pxt schema example` | Write a working file (`--brief` for the minimal one) |

```bash
pxt schema example --out app.py
pxt schema check  app.py
pxt schema diff   app.py my_app
pxt schema update app.py my_app
pxt schema update app.py my_app -n                       # plan only; exit 2 if pending
pxt schema update app.py my_app --allow-destructive -f   # including column/index drops
pxt schema prune  app.py my_app -n
```

`TARGET` is a catalog directory or a `pxt://org:db/...` URI.

Reading a diff: `+` created / added, `-` dropped, `~` migrated, `=` already matches, `!` cannot be migrated in place. Each op is marked safe, DESTRUCTIVE, or UNSUPPORTED.

- **DESTRUCTIVE** (dropping a column or index) needs `--allow-destructive`; exit `3` without it. Applying is **all-or-nothing** -- without the flag a destructive plan applies *nothing at all*, not the safe parts.
- **UNSUPPORTED** cannot be applied by any flag: a kind or iterator mismatch, or a column whose type or **value expression** changed. One unsupported table aborts the whole update, including other models' pending additive changes. Rename the column, or drop it and re-add it in a second pass. See [core-api.md](core-api.md#tables).

The daemon imports the application file, so it must be readable there; the file's own directory joins `sys.path`, so it can import modules sitting next to it.

**Run `pxt schema check APP` before the first update.** It validates the file with no catalog access, confirms every udf a column calls resolves to a module path another process can import, and warns when a top-level name in the project is shadowed:

```
app.py: an import of 'app' reads /.../site-packages/app/__init__.py, so this project
cannot record a udf under 'app'; rename it
```

The project root goes on `sys.path` *after* installed packages, so an installed distribution of the same name wins. `check` warns and still exits `0`; `schema update`, `service update` and `service run` do **not** warn -- they import the wrong module silently. Generic single-file names collide most often, so heed the warning and rename.

A CI drift check:

```bash
pxt schema diff app.py pxt://acme:main/prod    # 0 = in sync, 2 = drift, 1 = error
```

## Serving (`pxt service`)

Runs the `FastAPIRouter` instances an application file declares. Requires `pip install 'pixeltable[serve]'`. Same file as the models: apply tables first, then start HTTP.

| Command | Description |
|---------|-------------|
| `pxt service diff APP TARGET` | What `update` would change. Exit `2` if pending |
| `pxt service update APP TARGET` | Start declared services in the background; restart those that changed. Does **not** create tables |
| `pxt service run APP TARGET [SERVICE]` | Serve one service in the foreground until interrupted. For a container entrypoint, which must not return; **not** the command to recommend otherwise -- use `update` |
| `pxt service prune APP TARGET` | Stop and forget services at `TARGET` that the file does not declare |
| `pxt service stop NAME...` | Stop named services (`ingest` or `my_app/ingest`) |
| `pxt service list [TARGET]` | What is running, and where |
| `pxt service check APP` | Validate the file only. No `TARGET`. Reads no catalog |
| `pxt service example` | Write a working application file |

```bash
pxt service example --out app.py
pxt service check app.py
pxt schema update app.py my_app
pxt service update app.py my_app -f
pxt service list
pxt service logs ingest --since 10m --tail 50
pxt service stop ingest
```

`update` starts one background process per service, each on its own port, and is the serving command to use. A no-op or dry run exits without prompting; pass `-f` when a pending update runs without a TTY. Adding a route is additive; changing or removing one needs `--allow-destructive`. OpenAPI docs are at `/docs`. `pxt service run` refuses a `pxt://` TARGET and does not record anything, so `list` and `stop` cannot find it.

`pxt service logs NAME` accepts a bare service name or `TARGET/NAME`; use a full `pxt://org:db/path/name` for hosted services. `--since` accepts values such as `10m`, `--tail` is capped at 10,000 lines, and `--include-health` keeps health-probe requests. Local services report their log-file path. Hosted logs include request records and console output, including startup tracebacks.

**Tracing.** `service diff`, `service update` and `service run` take `--otel`, which emits OpenTelemetry traces and needs `pip install 'pixeltable[otel]'` (`serve` and `otel` are the only two extras). The setting belongs to the running service, not to the file: a service already running without it restarts when `update` is given the flag, dropping the flag restarts it again, and `diff --otel` reports tracing that is off but was asked for as a pending change.

Do **not** write `[tool.pixeltable.service]` TOML or call `pxt serve`.

## Cloud (`pxt db`, `pxt org`, `pxt secret`)

Require `PIXELTABLE_API_KEY`. URIs are `pxt://org` or `pxt://org:db`.

```bash
pxt db update pxt://myorg:mydb -f  # also: list, status, logs, start, stop, diff, build-image, delete
pxt db logs pxt://myorg:mydb --since 10m --tail 50
pxt org status pxt://myorg         # also: list
```

`pxt db update pxt://org:db` selects `[[pixeltable.database]]` by `name = 'pxt://org:db'`. A URI with no matching entry is an error. First `update` creates the hosted database.

Hosted order: `pxt db update pxt://org:db -f` sets secrets, image, and workers, then `pxt schema update app.py pxt://org:db -f`, then `pxt service update app.py pxt://org:db -f`. Database capacity or secret changes can also require `--allow-destructive`. If `pxt db diff` says the database project is behind the working copy, run `pxt db update` first.

Every Cloud database stores inserted and computed media in its managed home bucket by default. Set a column `destination=` to send that output elsewhere, or configure `input_media_dest` / `output_media_dest` to change the database defaults.

A UDF is recorded as a module path relative to the project root (`app.excerpt`), not a raw file path. `pxt db update` packs the project so Cloud can import it.

### Secrets

```bash
pxt secret set pxt://myorg OPENAI_API_KEY=sk-...    # also: list, delete
```

An org secret applies to every database in the org; a database secret wins on a key collision. A project declares database secrets under the `secrets` mapping, for example `secrets.openai_api_key = 'env:OPENAI_API_KEY'`; `pxt db update` sets them. A running database keeps the values it started with. Run `pxt db stop` then `pxt db start` to pick up a change.

## Scripting with `--json`

```bash
pxt ls --json | jq '.entries[] | select(.kind == "table")'
pxt get my_app/docs 42 --json | jq '.row'
pxt count my_app/docs --json | jq '.count'
pxt schema diff app.py my_app --json
pxt service diff app.py my_app --json
```

## Related references

- [core-api.md → Serving](core-api.md#serving) -- `FastAPIRouter` Python API
- [workflows.md](workflows.md) -- application-file example
- [Configuration](https://docs.pixeltable.com/platform/configuration) -- API keys, paths, env vars

SHA-256: 6085b339d367c0e2a1c3314bbac54372a4e19c26e98819e0ec0122f81d535202