# CLI reference

One Python file, standard library only, the same REST API a curl call would use. Output is JSON when stdout is not a terminal (or with --json), a table when it is. Anywhere a project_id or a catalog's warehouse_id is accepted, the name works too. The CLI says catalog where the API says warehouse: lhbox catalog … is the command group (lhbox warehouse … still works), and every --catalog flag takes --warehouse as well.

## Install

```
curl -fsSL https://lakehousebox.com/install.sh | sh         # ~/.local/bin/lhbox, SHA-256 verified, no sudo
export LHBOX_URL=https://api.lakehousebox.com
```

## Configuration

| what | how |
|---|---|
| LHBOX_URL / --url | API base URL. The built-in default is https://api.lakehousebox.com (the service); set it to your own address for a self-hosted or local stack, e.g. http://localhost:8080. |
| LHBOX_API_KEY / --api-key | The key. Otherwise read from the credentials file. |
| LHBOX_CREDENTIALS | Path of the credentials file (0600, keyed by API URL; written by signup --save and login; a relative path is taken from the current directory). Without it: $XDG_CONFIG_HOME/lhbox/credentials.json when XDG_CONFIG_HOME is an absolute path, else ~/.config/lhbox/credentials.json. |
| LHBOX_PROFILE / --profile | Which saved login to use, one per account or organisation on this machine (lhbox profile list); otherwise the one lhbox profile use chose, else default. |
| LHBOX_NO_BROWSER | Set: lhbox login prints the approval link and never opens a browser. |
| LHBOX_NO_VERSION_CHECK / --no-version-check | Set: no daily line saying a newer lhbox is published. |

## Exit codes

| code | meaning | what to do |
|---|---|---|
| 0 | ok |  |
| 1 | invalid input | the values you sent are wrong; fix them, never retry unchanged |
| 2 | limit or conflict | a real quota or a real 409; stop and change the plan |
| 3 | auth | key missing, wrong or revoked |
| 4 | not found | no such project, catalog, namespace or table |
| 5 | server or unreachable | ours or the network's; safe to retry with backoff |
| 6 | usage error | the command line itself is malformed; fix the command, never retry |

The server's typed error body goes to stderr verbatim: it names the limit, the current value and the remedy. The errors met most, what each wants, and every code: Errors and help (https://lakehousebox.com/docs/errors/).

## Commands

Rendered from the CLI's own --help at build time: the lhbox published at /cli/lhbox. An older lhbox may lack a command or flag shown here, and refuses it as a usage error: lhbox --version names yours, lhbox update --check compares it with the published one, lhbox update installs it.

### lhbox

```
usage: lhbox [-h] [--version] [--url URL] [--api-key API_KEY]
             [--profile PROFILE] [--json] [--human]
             {signup,terms,account,login,new,claim,status,discard,recover,api-key,org,agent,integration,token,whoami,doctor,bug,update,logout,profile,env,query,usage,audit,admin,project,catalog,file,namespace,table,credentials,connect,duckdb,sink,index,slice,mcp} ...

LakehouseBox: an open lakehouse you control.

positional arguments:
  {signup,terms,account,login,new,claim,status,discard,recover,api-key,org,agent,integration,token,whoami,doctor,bug,update,logout,profile,env,query,usage,audit,admin,project,catalog,file,namespace,table,credentials,connect,duckdb,sink,index,slice,mcp}
    signup              create your account and catalog: email -> code -> API
                        key
    terms               the Terms version in force and whether you accepted it
    account             your account
    login               sign this computer in as you: allow it in your browser
                        (or with a code: --no-browser). The key acts as you,
                        as on the website, for 30 days; it is saved here,
                        never printed. A key limited to some catalogs is made
                        on the website (Settings > Access keys)
    new                 no account yet: a catalog for this agent now; a person
                        claims it later
    claim               a person keeps the catalog `lhbox new` made: a link
                        and a code for them
    status              the time an unclaimed catalog (lhbox new) has left
    discard             end an unclaimed catalog (lhbox new) now: it is
                        deleted with its data and every key, as at its expiry,
                        and this machine forgets the key
    recover             retired: says how to get back in (reset the password,
                        or `lhbox login` again)
    api-key             API keys of the caller
    org                 organisations: members, domains, invitations
    agent               hosted agents, in preview for the organisations it is
                        enabled for (tokens, which this command managed until
                        0.4.0, are `lhbox token`)
    integration         integrations: MCP servers your hosted agents may use,
                        as you; in preview for the organisations hosted agents
                        are enabled for
    token               tokens: machine identities of the organisation, each
                        with its own key, its own grants and an organisation
                        role
    whoami              who am I: identity, organisation, my level on each
                        catalog, limits with headroom and whether each is
                        enforced, the next step -- brief lines on a terminal,
                        a brief JSON object when piped
    doctor              check the whole path to a DuckDB session: API,
                        credential, catalog (FAIL while it is still
                        provisioning), recipe and level, a read through the
                        catalog, write-policy (HEAD and a one-key list: the
                        bucket accepts the identity, no object written), the
                        DuckDB here, and whether a newer lhbox is published
                        (WARN); PASS/FAIL/SKIP/WARN per line, exit 1 on any
                        FAIL. Writes nothing unless --write-probe
    bug                 report a bug in LakehouseBox: opens the public
                        tracker's bug form
                        (github.com/lakehousebox/lakehousebox) filled in with
                        this machine's lhbox, OS, Python and DuckDB versions;
                        sends nothing itself. Piped: the link and the details
                        as JSON, for an agent to show its person
    update              install the lhbox the site publishes (its install
                        script) when this one is older; --check only says.
                        lhbox also prints one stderr line a day when it is
                        behind (silence: LHBOX_NO_VERSION_CHECK=1 or --no-
                        version-check)
    logout              revoke the key this machine's login holds and remove
                        it from the credentials file (--local: only remove it)
    profile             the saved logins on this machine, one per account or
                        organisation
    env                 the environment lhbox reads
    query               run one read statement (SELECT, DESCRIBE, SUMMARIZE,
                        EXPLAIN) on the server over the catalogs you can read:
                        20 s, at most 2,000 rows and about 50,000 characters
                        -- a quick look; lhbox duckdb for complete results
    usage               limits, current usage and headroom (which limits are
                        enforced), one row per catalog -- a brief JSON object
                        when piped, tables on a terminal; --catalog for one
                        catalog's storage and traffic by day
    audit               who did what: your events, or your organisation's
                        (owner/admin)
    admin               install administration (a self-hosted install: status,
                        first-run setup) and the operator's plans and limits;
                        LHBOX_OPERATOR_KEY or, where named, --login with an
                        install administrator's own password and code; never
                        the agent key
    project             projects (placement is immutable)
    catalog             catalogs: one Iceberg catalog per lakehouse, with its
                        own storage and identity (the API names one by
                        catalog_id, under /v1/catalogs)
    file                the catalog's files (its file bucket, beside the
                        tables): cp, sync, put, get, ls, rm, aws s3 style,
                        with the catalog side written lhbox://<catalog>/<key>
                        -- no aws CLI or boto3 needed
                        (https://lakehousebox.com/docs/files/)
    namespace           namespaces (created on demand by table create)
    table               tables
    credentials         short-lived, table-scoped storage credentials: `lhbox
                        credentials <catalog>.<ns>.<table>`; the secret key
                        and session token are ******** unless --show-secrets
    connect             a paste-ready recipe for your own engine, carrying the
                        identity that matches your level (read holders get the
                        read-only one; snowflake does unless --write); with
                        one catalog visible no --catalog is needed; for duckdb
                        the DuckDB on this machine is checked against 1.5.5
    duckdb              open the `duckdb` shell already attached to the
                        catalog (the recipe goes in through a 0600 init file,
                        deleted at once; the credential is never on screen or
                        in argv); --persist instead writes DuckDB persistent
                        secrets so a bare ATTACH works in any session
    sink                ingest sinks: one HTTP intake per table for a device,
                        a webhook or an agent without an engine; JSON arrays
                        in, Iceberg rows out on a rolling policy
                        (https://lakehousebox.com/docs/ingest/)
    index               blob index rules: every file uploaded under a prefix
                        of a catalog's blob bucket becomes a row of a table
                        (https://lakehousebox.com/docs/ingest/)
    slice               table slices (enterprise): one copy of a table per
                        value of a column, each in its own namespace, kept up
                        to date; grant each audience its namespace
    mcp                 the MCP server for Claude Desktop, Claude Code and
                        other hosts: install its configuration with the saved
                        key, or run it

options:
  -h, --help            show this help message and exit
  --version             show program's version number and exit
  --url URL             API base URL (env LHBOX_URL, default
                        https://api.lakehousebox.com)
  --api-key API_KEY     API key (env LHBOX_API_KEY). Kept for scripts, but a
                        key on a command line is visible to other local users
                        in the process list (ps) while the command runs:
                        prefer LHBOX_API_KEY or the login `lhbox login` saves
  --profile PROFILE     the saved login to use, one per account or
                        organisation on this machine (env LHBOX_PROFILE;
                        `lhbox profile use` sets the default; see `lhbox
                        profile list`)
  --json                force JSON output
  --human               force human-readable output

Output is JSON when piped and a table on a terminal. Piped, stdout is one JSON document or nothing;
every error is one JSON envelope on stderr {"error": {code, message, remedy, ...}}.
Anywhere a project_id or a catalog's warehouse_id is accepted, the name works too.

Exit codes, one meaning each:
  0 ok
  1 invalid input          the values you sent are wrong; fix them, do not retry
  2 limit/conflict         a real quota or a real 409; change the plan
  3 auth                   key missing, wrong or revoked
  4 not found              no such project, catalog, namespace or table
  5 server or unreachable  retry with backoff, unless the error's outcome is "unknown":
                           the request may have completed; check first (the remedy says how)
  6 usage error            the command line is malformed; fix it, do not retry

A bug in LakehouseBox itself: lhbox bug --title "..." (the public tracker's form, filled in; nothing sent).
```

### lhbox signup

```
usage: lhbox signup [-h] --email EMAIL [--workspace WORKSPACE]
                    [--full-name FULL_NAME] [--organization ORGANIZATION]
                    [--handle HANDLE] [--code CODE]
                    [--engine {duckdb,pyiceberg,spark}] [--save] [--show-key]
                    [--accept-terms]

options:
  -h, --help            show this help message and exit
  --email EMAIL
  --workspace WORKSPACE
                        workspace name (default: the part before @)
  --full-name FULL_NAME
                        your name, for the account
  --organization ORGANIZATION
                        organisation name; with --handle the organisation is
                        reserved now
  --handle HANDLE       the organisation's handle
                        (lakehousebox.com/app/<handle>; lowercase letters,
                        digits, dashes; fixed after signup)
  --code CODE           the code you received; prompted for if omitted
  --engine {duckdb,pyiceberg,spark}
  --save                store the API key in ~/.config/lhbox/credentials.json;
                        with it the key is NOT printed
  --show-key            print the API key even when it was saved (default: a
                        saved key stays out of the terminal)
  --accept-terms        accept the Terms of Service and Privacy Policy
```

### lhbox terms

```
usage: lhbox terms [-h] [--accept]

options:
  -h, --help  show this help message and exit
  --accept
```

### lhbox account

```
usage: lhbox account [-h] {delete} ...

positional arguments:
  {delete}
    delete    delete your account: keys revoked now, data purged within 30
              days

options:
  -h, --help  show this help message and exit
```

### lhbox account delete

```
usage: lhbox account delete [-h] --confirm CONFIRM [--password @FILE|@-]
                            [--password-env VAR] [--code CODE]

DELETE /v1/me. A person's account asks for its current password, and for a
code when two-factor authentication is on: give them with --password and
--code, or type them when asked at a terminal. A token confirms with its
principal_id and needs neither.

options:
  -h, --help           show this help message and exit
  --confirm CONFIRM    your email, typed out
  --password @FILE|@-  your current password from a file or stdin, never
                       inline; asked for at a terminal when needed
  --password-env VAR   the environment variable holding your current password
  --code CODE          a code from your authenticator app (or a backup code),
                       when two-factor is on
```

### lhbox login

```
usage: lhbox login [-h] [--issuer [ISSUER]] [--sso EMAIL] [--label LABEL]
                   [--name NAME] [--no-browser]
                   [--campaign SOURCE[/MEDIUM[/CAMPAIGN[/CONTENT]]]] [--new]
                   [--no-wait] [--resume] [--wait] [--engine ENGINE] [--save]
                   [--show-key] [--accept-terms]

options:
  -h, --help            show this help message and exit
  --issuer [ISSUER]     log in through an external OpenID Connect issuer
                        instead of the LakehouseBox login (the first
                        configured one, or the one named); for self-hosted
                        deployments with their own identity provider
  --sso EMAIL           log in with your organisation's own identity provider
                        (e.g. Microsoft Entra ID), found from your work
                        address
  --label LABEL         API key label on the --issuer and --sso paths (the
                        LakehouseBox login labels it cli <hostname>)
  --name NAME           the token's name shown on the consent screen and in
                        Connections (default: this machine's hostname)
  --no-browser          sign in with a code instead of opening a browser here
                        (the browser is on another computer: an SSH session, a
                        server). Either way the key acts as you, for 30 days
  --campaign SOURCE[/MEDIUM[/CAMPAIGN[/CONTENT]]]
                        the campaign tags of the link that brought you here
                        (the site writes them into the command it shows); an
                        account created on the approval page is attributed to
                        them
  --new                 start a new authorisation even when the saved key
                        still works
  --no-wait             print the page and the code, keep the pending login in
                        the credentials file (pending_device) and exit 0;
                        finish it later with --resume
  --resume              poll the pending login left by --no-wait once:
                        connected (exit 0), or still pending (exit 3, the
                        credential is still missing); --resume --wait polls
                        until the person decides
  --wait                with --resume: wait for the approval instead of
                        polling once (the default `lhbox login` waits anyway,
                        up to the code's 15 minutes)
  --engine ENGINE
  --save                kept for compatibility: the key is always saved to
                        ~/.config/lhbox/credentials.json
  --show-key            print the API key as well (default: it is saved and
                        never shown; an agent should not need it)
  --accept-terms        accept the Terms of Service on first login (--issuer
                        path)
```

### lhbox new

```
usage: lhbox new [-h] [--force]
                 [--campaign SOURCE[/MEDIUM[/CAMPAIGN[/CONTENT]]]]

no account yet: a catalog for this agent now, write access, the key saved
here; a person claims it later or it is deleted with its data after 72 h.
Running it accepts the Terms

options:
  -h, --help            show this help message and exit
  --force               replace the key already saved for this API (default:
                        refused)
  --campaign SOURCE[/MEDIUM[/CAMPAIGN[/CONTENT]]]
                        the campaign tags of the link that brought you here
                        (the site writes them into the command it shows)
```

### lhbox claim

```
usage: lhbox claim [-h] [--wait] [--resume]

a person keeps the catalog `lhbox new` made: prints a link and a code for
them; --wait waits for their decision and saves this machine's new key

options:
  -h, --help  show this help message and exit
  --wait      wait for the person's decision (default: print and exit;
              --resume later)
  --resume    collect a claim started earlier (polls once; with --wait until
              decided)
```

### lhbox status

```
usage: lhbox status [-h]

whether this machine's catalog is still unclaimed (lhbox new), and the time it
has left

options:
  -h, --help  show this help message and exit
```

### lhbox discard

```
usage: lhbox discard [-h] [--yes]

DELETE /v1/agent/identity: the unclaimed organisation `lhbox new` made, its
catalog with every table and file, and its keys, deleted now instead of at
expires_at. For a key that leaked, or a catalog nobody needs. A catalog a
person has claimed is theirs: refused.

options:
  -h, --help  show this help message and exit
  --yes       do not ask: discarding the catalog and its data cannot be
              undone. Asked on a terminal; without one (or with --json) --yes
              will be required from the next minor version of lhbox (a warning
              today)
```

### lhbox recover

```
usage: lhbox recover [-h]

options:
  -h, --help  show this help message and exit
```

### lhbox api-key

```
usage: lhbox api-key [-h] {create,list,revoke} ...

positional arguments:
  {create,list,revoke}

options:
  -h, --help            show this help message and exit
```

### lhbox api-key create

```
usage: lhbox api-key create [-h] [--label LABEL]

options:
  -h, --help     show this help message and exit
  --label LABEL
```

### lhbox api-key list

```
usage: lhbox api-key list [-h]

options:
  -h, --help  show this help message and exit
```

### lhbox api-key revoke

```
usage: lhbox api-key revoke [-h] --key-id KEY_ID [--yes [NAME]]

options:
  -h, --help       show this help message and exit
  --key-id KEY_ID
  --yes [NAME]     do not ask: the revocation cannot be undone. Asked on a
                   terminal; without one (or with --json) --yes will be
                   required from the next minor version of lhbox (a warning
                   today)
```

### lhbox org

```
usage: lhbox org [-h]
                 {list,members,invite,accept,domain,offers,join,set,role} ...

positional arguments:
  {list,members,invite,accept,domain,offers,join,set,role}
    domain              claim your organisation's email domain (admin; it
                        takes effect only once a DNS TXT record verifies it),
                        --verify the claim, or --remove it
    offers              organisations you may join because they verified and
                        opened your address's domain
    join                accept a domain offer: join that organisation as a
                        member (nothing joins you otherwise)
    policy              what the organisation's members may do: publish
                        catalogs, connect agents, create hosted agents (read
                        it; an admin changes it with the flags)
    role                change a member's role: admin or member (an
                        organisation keeps at least one admin)

options:
  -h, --help            show this help message and exit
```

### lhbox org list

```
usage: lhbox org list [-h]

options:
  -h, --help  show this help message and exit
```

### lhbox org members

```
usage: lhbox org members [-h] [--org ORG]

options:
  -h, --help  show this help message and exit
  --org ORG
```

### lhbox org invite

```
usage: lhbox org invite [-h] [--org ORG] --email EMAIL [--role {member,admin}]

options:
  -h, --help            show this help message and exit
  --org ORG
  --email EMAIL
  --role {member,admin}
```

### lhbox org accept

```
usage: lhbox org accept [-h] --token TOKEN

options:
  -h, --help     show this help message and exit
  --token TOKEN
```

### lhbox org domain

```
usage: lhbox org domain [-h] [--org ORG] --domain DOMAIN [--verify | --remove]

options:
  -h, --help       show this help message and exit
  --org ORG
  --domain DOMAIN
  --verify         look up the TXT record _lakehousebox.<domain> and verify
                   the claim
  --remove         release the domain instead of claiming it (admin); domain
                   join switches off when no verified domain remains
```

### lhbox org offers

```
usage: lhbox org offers [-h]

options:
  -h, --help  show this help message and exit
```

### lhbox org join

```
usage: lhbox org join [-h] [--org ORG]

options:
  -h, --help  show this help message and exit
  --org ORG   the offering organisation's handle (optional when you have
              exactly one offer)
```

### lhbox org set

```
usage: lhbox org set [-h] [--org ORG] [--name NAME] [--domain-join {on,off}]
                     [--deprovision {tokens,everything}]

options:
  -h, --help            show this help message and exit
  --org ORG
  --name NAME
  --domain-join {on,off}
                        offer membership to people on a verified domain (they
                        join only if they accept)
  --deprovision {tokens,everything}
                        what removing a member stops: their tokens and
                        connections (tokens, the default), or also the sinks,
                        blob uploaders and hosted agents they made
                        (everything)
```

### lhbox org policy

```
usage: lhbox org policy [-h] [--org ORG]
                        [--publish-catalogs {admins,members,nobody}]
                        [--connect-agents {members,admins}]
                        [--hosted-agents {members,admins}]

options:
  -h, --help            show this help message and exit
  --org ORG             org_id or the organisation's handle (lhbox org list
                        shows both); default: your only organisation
  --publish-catalogs {admins,members,nobody}
                        who may make a catalog public: admins (the default),
                        members too (on a catalog they can write), or nobody;
                        catalogs public now stay public
  --connect-agents {members,admins}
                        who may connect agents: tokens, `lhbox login`, MCP
                        clients, personal API keys (default members)
  --hosted-agents {members,admins}
                        who may create hosted agents (default members)
```

### lhbox org role

```
usage: lhbox org role [-h] [--org ORG] --principal PRINCIPAL
                      --role {admin,member}

options:
  -h, --help            show this help message and exit
  --org ORG             org_id or the organisation's handle (lhbox org list
                        shows both); default: your only organisation
  --principal PRINCIPAL
  --role {admin,member}
```

### lhbox agent

```
usage: lhbox agent [-h]
                   {create,list,show,update,run,runs,subscribe,unsubscribe,delete} ...

Hosted agents: a prompt LakehouseBox runs for you next to your data, on the
provided model, reading the catalogs you grant it; runs count against your
organisation's monthly tokens and runs per day.

positional arguments:
  {create,list,show,update,run,runs,subscribe,unsubscribe,delete}
    create              a new agent with its own key-less identity reading the
                        catalogs given
    list                your organisation's agents and its allowance (tokens
                        this month, runs today)
    models              the models LakehouseBox provides: where they run,
                        whether they see images, price
    show                one agent: its definition, catalogs and last run
    update              a new version of the agent (only what you pass
                        changes), or --pause / --resume
    run                 run the agent now (one run at a time; counts against
                        today's runs and the month's tokens)
    brief               what needs you first (its flags, decisions due,
                        failures, missed runs), then its memory as it sees it
                        and its recent runs; no model
    talk                talk to the agent: it answers from its memory and
                        runs, and may propose a new prompt or items to mark
                        done, which change nothing until you say yes in your
                        next message
    runs                the agent's runs: state, tokens, model requests,
                        errors
    subscribe           email me this agent's notices (you need read on every
                        catalog it reads and a verified address)
    unsubscribe         stop emailing me this agent's notices
    delete              delete the agent for good: its identity is suspended,
                        its runs stay readable

options:
  -h, --help            show this help message and exit
```

### lhbox agent create

```
usage: lhbox agent create [-h] --prompt TEXT [--catalog CATALOG]
                          [--state-key KEY] [--topic TOPIC] [--notify-owner |
                          --no-notify-owner] [--run-seconds S] [--model MODEL]
                          [--schedule CRON] [--tz ZONE]
                          [--integration NAME:TOOL[,TOOL]]
                          [--write CATALOG.NS.TABLE] [--allow-host HOST]
                          [--host-credential HOST=INTEGRATION] [--org ORG_ID]
                          NAME

positional arguments:
  NAME                  the agent's name (lowercase letters, digits and _)

options:
  -h, --help            show this help message and exit
  --prompt TEXT         what the agent does: inline, @file or @- (stdin)
  --catalog CATALOG     a catalog the agent may read (repeat; its grants
                        become exactly these)
  --state-key KEY       a small value it may remember between runs (repeat)
  --topic TOPIC         a memory topic: the agent keeps notes about items in
                        it (note, resolve, flag_owner) and starts each run
                        from their view (repeat; up to 12)
  --notify-owner        it may email you (its creator's person) once per run
  --no-notify-owner     it may not email
  --run-seconds S       one run's wall time (at most the organisation's limit)
  --model MODEL         a provided model (default deepseek-v4-flash)
  --schedule CRON       when it runs by itself: five cron fields, e.g. "0 7 *
                        * *" (at most hourly and the organisation's runs per
                        day)
  --tz ZONE             the schedule's time zone, e.g. Europe/Madrid (default
                        UTC)
  --integration NAME:TOOL[,TOOL]
                        an integration you connected and the tools the agent
                        may call there (repeat; the list becomes exactly
                        these), e.g. github:search_issues,get_file_contents.
                        Your own sign-in only: a token (an lhbox login
                        connection included) is refused
                        integration_needs_person; attach it on the account
                        page
  --write CATALOG.NS.TABLE
                        a table the agent may append rows to (repeat; the list
                        becomes exactly these): in a catalog it reads, and you
                        need write there
  --allow-host HOST     a web host the agent may fetch (https GET; repeat):
                        its web hosts become exactly these, with the keys
                        given by --host-credential in the same command (no
                        other key is kept)
  --host-credential HOST=INTEGRATION
                        send that web key (lhbox integration add NAME --web-
                        key) to HOST; the host is allowed too (alone on
                        update: added to the agent's hosts and keys). Like
                        --integration, your own sign-in only (a token is
                        refused)
  --org ORG_ID          the organisation (when you belong to several)
```

### lhbox agent list

```
usage: lhbox agent list [-h] [--org ORG_ID]

options:
  -h, --help    show this help message and exit
  --org ORG_ID  the organisation (when you belong to several)
```

### lhbox agent models

```
usage: lhbox agent models [-h]

options:
  -h, --help  show this help message and exit
```

### lhbox agent show

```
usage: lhbox agent show [-h] NAME

positional arguments:
  NAME        the agent's name, or its agent_id (needed when you belong to two
              organisations with an agent of that name)

options:
  -h, --help  show this help message and exit
```

### lhbox agent update

```
usage: lhbox agent update [-h] [--prompt TEXT] [--catalog CATALOG]
                          [--state-key KEY] [--topic TOPIC] [--notify-owner |
                          --no-notify-owner] [--run-seconds S] [--model MODEL]
                          [--schedule CRON] [--tz ZONE]
                          [--integration NAME:TOOL[,TOOL]]
                          [--write CATALOG.NS.TABLE] [--allow-host HOST]
                          [--host-credential HOST=INTEGRATION] [--no-catalogs]
                          [--no-state-keys] [--no-topics] [--no-schedule]
                          [--no-integrations] [--no-writes] [--no-web]
                          [--confirm-definition] [--pause | --resume]
                          [--restore-version VERSION]
                          NAME

positional arguments:
  NAME                  the agent's name, or its agent_id (needed when you
                        belong to two organisations with an agent of that
                        name)

options:
  -h, --help            show this help message and exit
  --prompt TEXT         what the agent does: inline, @file or @- (stdin)
  --catalog CATALOG     a catalog the agent may read (repeat; its grants
                        become exactly these)
  --state-key KEY       a small value it may remember between runs (repeat)
  --topic TOPIC         a memory topic: the agent keeps notes about items in
                        it (note, resolve, flag_owner) and starts each run
                        from their view (repeat; up to 12)
  --notify-owner        it may email you (its creator's person) once per run
  --no-notify-owner     it may not email
  --run-seconds S       one run's wall time (at most the organisation's limit)
  --model MODEL         a provided model (default deepseek-v4-flash)
  --schedule CRON       when it runs by itself: five cron fields, e.g. "0 7 *
                        * *" (at most hourly and the organisation's runs per
                        day)
  --tz ZONE             the schedule's time zone, e.g. Europe/Madrid (default
                        UTC)
  --integration NAME:TOOL[,TOOL]
                        an integration you connected and the tools the agent
                        may call there (repeat; the list becomes exactly
                        these), e.g. github:search_issues,get_file_contents.
                        Your own sign-in only: a token (an lhbox login
                        connection included) is refused
                        integration_needs_person; attach it on the account
                        page
  --write CATALOG.NS.TABLE
                        a table the agent may append rows to (repeat; the list
                        becomes exactly these): in a catalog it reads, and you
                        need write there
  --allow-host HOST     a web host the agent may fetch (https GET; repeat):
                        its web hosts become exactly these, with the keys
                        given by --host-credential in the same command (no
                        other key is kept)
  --host-credential HOST=INTEGRATION
                        send that web key (lhbox integration add NAME --web-
                        key) to HOST; the host is allowed too (alone on
                        update: added to the agent's hosts and keys). Like
                        --integration, your own sign-in only (a token is
                        refused)
  --no-catalogs         take away every catalog grant (with no --catalog)
  --no-state-keys       forget every state key (with no --state-key)
  --no-topics           turn its memory topics off (its notes are kept)
  --no-schedule         remove the schedule: it runs on demand only
  --no-integrations     detach every integration (with no --integration)
  --no-writes           the agent appends to no table (with no --write)
  --no-web              the agent fetches no web host (with no --allow-host)
  --confirm-definition  approve the agent's current definition as yours, so it
                        may use your integrations (needed when another
                        credential, e.g. a token, wrote it; your own sign-in
                        only)
  --pause               no run starts until --resume
  --resume              runs may start again
  --restore-version VERSION
                        put an earlier definition back whole (its id is in the
                        mail sent when a key changes the agent)
```

### lhbox agent run

```
usage: lhbox agent run [-h] [--dry-run] [--wait] [--timeout S] NAME

positional arguments:
  NAME         the agent's name, or its agent_id (needed when you belong to
               two organisations with an agent of that name)

options:
  -h, --help   show this help message and exit
  --dry-run    a test run: its tools read for real, its email and memory
               writes are shown, not performed; outside today's runs (its own
               daily cap), tokens still count
  --wait       wait for the run to end and print its final message
  --timeout S  how long --wait waits (default 600 s)
```

### lhbox agent brief

```
usage: lhbox agent brief [-h] NAME

positional arguments:
  NAME        the agent's name, or its agent_id (needed when you belong to two
              organisations with an agent of that name)

options:
  -h, --help  show this help message and exit
```

### lhbox agent talk

```
usage: lhbox agent talk [-h] [--about TOPIC/ITEM] [--limit N] NAME [MESSAGE]

positional arguments:
  NAME                the agent's name, or its agent_id (needed when you
                      belong to two organisations with an agent of that name)
  MESSAGE             what you say (omit it to list the latest exchanges)

options:
  -h, --help          show this help message and exit
  --about TOPIC/ITEM  reply to an entry of its brief ([topic/item] there)
  --limit N           how many exchanges to list (default 5)
```

### lhbox agent runs

```
usage: lhbox agent runs [-h] [--limit N] [--detail] NAME

positional arguments:
  NAME        the agent's name, or its agent_id (needed when you belong to two
              organisations with an agent of that name)

options:
  -h, --help  show this help message and exit
  --limit N
  --detail    each run's tool calls (with their SQL), emails and memory
              changes
```

### lhbox agent subscribe

```
usage: lhbox agent subscribe [-h] NAME

positional arguments:
  NAME        the agent's name, or its agent_id (needed when you belong to two
              organisations with an agent of that name)

options:
  -h, --help  show this help message and exit
```

### lhbox agent unsubscribe

```
usage: lhbox agent unsubscribe [-h] NAME

positional arguments:
  NAME        the agent's name, or its agent_id (needed when you belong to two
              organisations with an agent of that name)

options:
  -h, --help  show this help message and exit
```

### lhbox agent delete

```
usage: lhbox agent delete [-h] [--yes] NAME

positional arguments:
  NAME        the agent's name, or its agent_id (needed when you belong to two
              organisations with an agent of that name)

options:
  -h, --help  show this help message and exit
  --yes       do not ask: the deletion cannot be undone. Asked on a terminal;
              without one (or with --json) --yes will be required from the
              next minor version of lhbox (a warning today)
```

### lhbox agent revoke

```
usage: lhbox agent revoke [-h] [ref]

positional arguments:
  ref

options:
  -h, --help  show this help message and exit
```

### lhbox integration

```
usage: lhbox integration [-h] {add,list,show,reconnect,wait,delete} ...

Integrations connect any MCP server to your organisation: with its sign-in
(you approve in a browser where you are signed in to LakehouseBox), an API
key, or none; the CLI never receives a token. Attach one to an agent on the
account page, or with `lhbox agent update NAME --integration NAME:tool,...`
signed in as yourself (a token cannot attach your integrations; lhbox
integration show NAME lists its tools).

positional arguments:
  {add,list,show,reconnect,wait,delete}
    add                 connect an MCP server: prints the link to approve in
                        your browser
    list                your organisation's integrations and their state
    show                one integration: its server, tools, scopes and state
    reconnect           approve again (after `needs_reauth`, or to switch
                        accounts)
    wait                wait until the person approved (after add or
                        reconnect; their answer's wait_command)
    delete              delete the integration for good: its tokens are
                        revoked and wiped

options:
  -h, --help            show this help message and exit
```

### lhbox integration add

```
usage: lhbox integration add [-h] [--mcp URL] [--api-key-stdin]
                             [--header NAME] [--web-key] [--host HOST]
                             [--key-param NAME] [--scope SCOPE] [--org ORG_ID]
                             [--wait] [--timeout S] [--open]
                             NAME

positional arguments:
  NAME                  the integration's name (e.g. linear; lowercase
                        letters, digits and _)

options:
  -h, --help            show this help message and exit
  --mcp URL             the MCP server's URL (https; for the name github:
                        GitHub's MCP server by default)
  --api-key-stdin       the server takes an API key: read it from stdin (it is
                        stored sealed and never shown again)
  --header, --header-name NAME
                        with --api-key-stdin: the header the key goes in
                        (default Authorization, sent as 'Bearer <key>')
  --web-key             a web API's key for an agent's fetch_url (with --host
                        and --api-key-stdin), not an MCP server
  --host HOST           with --web-key: the one host the key is sent to
  --key-param NAME      with --web-key: send the key as this query parameter
                        instead of a header
  --scope SCOPE         narrow the scopes asked for (repeat); default: the
                        server's own
  --org ORG_ID          the organisation (when you belong to several)
  --wait                wait until you approved in the browser (or it failed)
  --timeout S           how long --wait waits (default 600 s)
  --open                also open the link in this machine's browser
```

### lhbox integration list

```
usage: lhbox integration list [-h] [--org ORG_ID]

options:
  -h, --help    show this help message and exit
  --org ORG_ID  the organisation (when you belong to several)
```

### lhbox integration show

```
usage: lhbox integration show [-h] NAME

positional arguments:
  NAME        the integration's name or id

options:
  -h, --help  show this help message and exit
```

### lhbox integration reconnect

```
usage: lhbox integration reconnect [-h] [--wait] [--timeout S] [--open] NAME

positional arguments:
  NAME         the integration's name or id

options:
  -h, --help   show this help message and exit
  --wait       wait until you approved in the browser (or it failed)
  --timeout S  how long --wait waits (default 600 s)
  --open       also open the link in this machine's browser
```

### lhbox integration wait

```
usage: lhbox integration wait [-h] [--previous CONNECTED_AT] [--timeout S]
                              NAME

positional arguments:
  NAME                  the integration's name or id

options:
  -h, --help            show this help message and exit
  --previous CONNECTED_AT
                        its connected_at before the approval ('-' for never),
                        from the add or reconnect answer
  --timeout S           how long to wait (default 600 s)
```

### lhbox integration delete

```
usage: lhbox integration delete [-h] [--yes] NAME

positional arguments:
  NAME        the integration's name or id

options:
  -h, --help  show this help message and exit
  --yes       do not ask: the deletion cannot be undone. Asked on a terminal;
              without one (or with --json) --yes will be required from the
              next minor version of lhbox (a warning today)
```

### lhbox token

```
usage: lhbox token [-h] {create,update,list,revoke} ...

positional arguments:
  {create,update,list,revoke}
    create              a new token; it holds exactly the grants given (none
                        until granted otherwise) and the role given (member
                        unless --role admin)
    update              rename a token, replace its grants and/or change its
                        role (admin, or the member who created it, within
                        their own role)
    list                the organisation's tokens; grants as [{catalog, level,
                        warehouse_id}] in JSON, catalog:level on a terminal
    revoke              revoke a token's key at once (admin, or the member who
                        created it): lhbox token revoke <name or id>

options:
  -h, --help            show this help message and exit
```

### lhbox token create

```
usage: lhbox token create [-h] [--org ORG] --name NAME [--role {admin,member}]
                          [--grant CATALOG:LEVEL] [--send SINK]

options:
  -h, --help            show this help message and exit
  --org ORG             org_id or the organisation's handle (lhbox org list
                        shows both); default: your only organisation
  --name NAME
  --role {admin,member}
                        organisation role of the token (default member): what
                        it may do to the organisation, its people and its
                        tokens; capped by your own role. Data access is its
                        grants
  --grant CATALOG:LEVEL
                        <catalog name or id>:<read|write>, repeatable (a name
                        is resolved here against lhbox catalog list); capped
                        by your own level on each catalog
  --send SINK           a sink (id or name) the token may send batches to with
                        its own key (`lhbox sink send` without a send key),
                        repeatable; needs your write on the sink's catalog,
                        and the token needs a --grant <catalog>:read on it
```

### lhbox token update

```
usage: lhbox token update [-h] [--org ORG] [--token TOKEN] [--name NAME]
                          [--role {admin,member}] [--grant CATALOG:LEVEL]
                          [--send SINK]
                          [TOKEN]

positional arguments:
  TOKEN                 the token's id or its name (lhbox token list shows
                        both)

options:
  -h, --help            show this help message and exit
  --org ORG             org_id or the organisation's handle (lhbox org list
                        shows both); default: your only organisation
  --token, --token-id TOKEN
                        the token's id or its name (lhbox token list shows
                        both); the same as the positional
  --name NAME           the new name
  --role {admin,member}
                        new organisation role; capped by your own
  --grant CATALOG:LEVEL
                        <catalog name or id>:<read|write>, repeatable; the
                        list REPLACES the current grants
  --send SINK           a sink (id or name) the token may send to, repeatable;
                        the list REPLACES the current one (--send '' clears
                        it)
```

### lhbox token list

```
usage: lhbox token list [-h] [--org ORG]

options:
  -h, --help  show this help message and exit
  --org ORG   org_id or the organisation's handle (lhbox org list shows both);
              default: your only organisation
```

### lhbox token revoke

```
usage: lhbox token revoke [-h] [--org ORG] [--token TOKEN] [--yes [NAME]]
                          [TOKEN]

positional arguments:
  TOKEN                 the token's id or its name (lhbox token list shows
                        both)

options:
  -h, --help            show this help message and exit
  --org ORG             org_id or the organisation's handle (lhbox org list
                        shows both); default: your only organisation
  --token, --token-id TOKEN
                        the token's id or its name (lhbox token list shows
                        both); the same as the positional
  --yes [NAME]          do not ask: the revocation cannot be undone. Asked on
                        a terminal; without one (or with --json) --yes will be
                        required from the next minor version of lhbox (a
                        warning today)
```

### lhbox whoami

```
usage: lhbox whoami [-h] [--full]

options:
  -h, --help  show this help message and exit
  --full      the whole answer: every limit, project and organisation as
              tables on a terminal; piped or --json, GET /v1/usage as it comes
              plus the per-catalog levels (the default JSON is the brief
              object)
```

### lhbox doctor

```
usage: lhbox doctor [-h] [--catalog CATALOG] [--write-probe]

options:
  -h, --help         show this help message and exit
  --catalog CATALOG  the catalog's warehouse_id (UUID) or its name; list both
                     with: lhbox catalog list; default: your only catalog
                     (with several, name one)
  --write-probe      also write and delete a 0-byte
                     _lhbox/_probe/data/probe.parquet in the table bucket as
                     the recipe's identity: the one check that sees a store
                     refusing writes (the store's error is quoted verbatim on
                     FAIL)
```

### lhbox bug

```
usage: lhbox bug [-h] [--title TITLE] [--what WHAT] [--command COMMAND]
                 [--no-open]

Builds the bug form's URL; you review and submit it. The tracker is public:
what you pass is scrubbed of key and secret shapes, and no organisation,
catalog or home directory is added. A security vulnerability goes to a private
report instead
(https://github.com/lakehousebox/lakehousebox/security/advisories/new).

options:
  -h, --help         show this help message and exit
  --title TITLE      one line: what is wrong
  --what WHAT        what happened: the error as printed (its `code` above
                     all)
  --command COMMAND  what you ran: the command, SQL or MCP tool call
  --no-open          print the link, do not open a browser
```

### lhbox update

```
usage: lhbox update [-h] [--check] [--site URL]

options:
  -h, --help  show this help message and exit
  --check     say which version is published, install nothing
  --site URL  install from this site instead of https://lakehousebox.com (a
              self-hosted deployment's own): https, or http on this machine
              only; never taken from --url / LHBOX_URL, since its install
              script runs here
```

### lhbox logout

```
usage: lhbox logout [-h] [--local] [--org ORG]

options:
  -h, --help  show this help message and exit
  --local     remove the saved login without revoking its key at the API
  --org ORG   the organisation that holds the token (only needed when you
              belong to several)
```

### lhbox profile

```
usage: lhbox profile [-h] {list,use,delete} ...

positional arguments:
  {list,use,delete}
    list             the profiles, the one in force marked
    use              make a profile the default for every command (--profile
                     and LHBOX_PROFILE still win)
    delete           remove a profile's saved login from this machine (logout
                     --profile NAME revokes its key first)

options:
  -h, --help         show this help message and exit
```

### lhbox profile list

```
usage: lhbox profile list [-h]

options:
  -h, --help  show this help message and exit
```

### lhbox profile use

```
usage: lhbox profile use [-h] name

positional arguments:
  name

options:
  -h, --help  show this help message and exit
```

### lhbox profile delete

```
usage: lhbox profile delete [-h] [--yes] name

positional arguments:
  name

options:
  -h, --help  show this help message and exit
  --yes       do not ask: forgetting the saved login cannot be undone. Asked
              on a terminal; without one (or with --json) --yes will be
              required from the next minor version of lhbox (a warning today)
```

### lhbox env

```
usage: lhbox env [-h] {check} ...

positional arguments:
  {check}
    check     `set` or `missing` for LHBOX_API_KEY, LHBOX_URL, the saved login
              and the rest; never a value

options:
  -h, --help  show this help message and exit
```

### lhbox env check

```
usage: lhbox env check [-h]

options:
  -h, --help  show this help message and exit
```

### lhbox query

```
usage: lhbox query [-h] [--max-rows MAX_ROWS] [--require-complete] [--dry-run]
                   [--engine {1.5,2.0}]
                   sql

A quick-query and preview interface: one statement on LakehouseBox's shared
engine, 20 seconds, and two independent caps on the answer, rows (--max-rows,
default 200, at most 2000) and characters (about 50,000 characters of JSON in
all, whatever --max-rows says). An answer cut by either still exits 0 and says
so: truncated: true, truncated_by: rows or characters, and local_compute, how
to get the whole result (aggregate or filter in SQL, or the same SQL in your
own DuckDB: lhbox duckdb --catalog <catalog>, no caps). --require-complete
makes a cut answer exit 2.

positional arguments:
  sql                  the SQL (DuckDB dialect), tables named
                       catalog.namespace.table; '-' reads it from stdin

options:
  -h, --help           show this help message and exit
  --max-rows MAX_ROWS  rows to return (default 200, at most 2000); the
                       character cap (about 50,000) can still cut a wide
                       answer sooner
  --require-complete   exit 2 (code incomplete_result on stderr) when the
                       answer was cut by either cap; the answer is still
                       printed. Without it a cut answer exits 0
  --dry-run            run nothing: the tables it reads with their rows, files
                       and bytes (an upper bound), and whether the cache would
                       answer
  --engine {1.5,2.0}   DuckDB engine version: 2.0 (DuckDB 2.0.0, default) or
                       1.5 (DuckDB 1.5.6, the fallback, for claimed accounts)
```

### lhbox usage

```
usage: lhbox usage [-h] [--catalog CATALOG] [--days DAYS] [--full]

options:
  -h, --help         show this help message and exit
  --catalog CATALOG  one catalog's view, day by day: stored bytes and objects,
                     S3 requests and bytes in/out; the catalog's warehouse_id
                     (UUID) or its name; list both with: lhbox catalog list
  --days DAYS        length of the daily history, 1..30 (default 7)
  --full             piped or --json: GET /v1/usage as it comes
                     (limits_detail, every project's warehouse_storage with
                     history[]) instead of the brief object
```

### lhbox audit

```
usage: lhbox audit [-h] [--org ORG] [--since SINCE] [--action ACTION]
                   [--limit LIMIT]

options:
  -h, --help       show this help message and exit
  --org ORG        org_id: the organisation's events (owners and admins)
                   instead of only yours
  --since SINCE    24h, 7d, 30m, or an ISO-8601 timestamp
  --action ACTION  one action, e.g. credentials.vend, or a prefix like org.*
  --limit LIMIT    at most this many events (max 1000)
```

### lhbox admin

```
usage: lhbox admin [-h] {status,setup,plans,org,person,client,news} ...

positional arguments:
  {status,setup,plans,org,person,client,news}
    status              the install: release, security settings and where each
                        is set, sign-in, SIEM export, backups, storage,
                        administrators (read-only)
    setup               first-run setup of a self-hosted install: the install
                        administrator and the first organisation, with the
                        one-time token `./al setup-link` printed on the host
    plans               every plan by name with its limits and features
    org                 one organisation's plan and limits
    person              a person's access: suspend, reactivate, revoke every
                        session and key
    client              approved OAuth/MCP clients: they connect with dynamic
                        registration off
    news                the product-news mailing list: status, write an issue,
                        test it, send it

options:
  -h, --help            show this help message and exit
```

### lhbox admin status

```
usage: lhbox admin status [-h] [--login EMAIL]

options:
  -h, --help     show this help message and exit
  --login EMAIL  as this install administrator: password and authenticator
                 code asked on the terminal, the session ended after the
                 command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin setup

```
usage: lhbox admin setup [-h] --token TOKEN --email EMAIL --name NAME
                         --org ORG [--handle HANDLE] [--password-stdin |
                         --no-password]

options:
  -h, --help        show this help message and exit
  --token TOKEN     the setup token (lhbsetup_...)
  --email EMAIL
  --name NAME       your name
  --org ORG         the first organisation's name
  --handle HANDLE   its handle (default: from the name)
  --password-stdin  read the password from standard input
  --no-password     no password: you sign in with the install's identity
                    provider (AL_OIDC_ISSUERS)
```

### lhbox admin plans

```
usage: lhbox admin plans [-h] [--login EMAIL]

options:
  -h, --help     show this help message and exit
  --login EMAIL  as this install administrator: password and authenticator
                 code asked on the terminal, the session ended after the
                 command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin org

```
usage: lhbox admin org [-h] {limits,set,create,list,admin} ...

positional arguments:
  {limits,set,create,list,admin}
    limits              the organisation's plan, overrides, effective limits
                        and features, and last changes
    set                 put the organisation on a plan and/or override limits;
                        a flag not passed leaves that limit as it is
    create              a new organisation and its first admin: an existing
                        person at once, any other address by an admin
                        invitation (its link printed once)
    list                every organisation, newest first: handle, members,
                        catalogs, stored
    admin               an organisation's admins

options:
  -h, --help            show this help message and exit
```

### lhbox admin org limits

```
usage: lhbox admin org limits [-h] [--login EMAIL] org

positional arguments:
  org            org_id or handle

options:
  -h, --help     show this help message and exit
  --login EMAIL  as this install administrator: password and authenticator
                 code asked on the terminal, the session ended after the
                 command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin org set

```
usage: lhbox admin org set [-h] [--plan PLAN] [--storage-gb STORAGE_GB]
                           [--public-storage-gb PUBLIC_STORAGE_GB]
                           [--objects OBJECTS] [--catalogs CATALOGS]
                           [--sql-sinks SQL_SINKS] [--sql-min-every SECONDS]
                           [--sql-run-seconds SQL_RUN_SECONDS]
                           [--sql-requests-per-run SQL_REQUESTS_PER_RUN]
                           [--sql-download-mb-per-run SQL_DOWNLOAD_MB_PER_RUN]
                           [--projects PROJECTS] [--clear FIELD]
                           [--feature NAME=VALUE] [--note NOTE]
                           [--login EMAIL]
                           org

positional arguments:
  org                   org_id or handle

options:
  -h, --help            show this help message and exit
  --plan PLAN           the plan name (lhbox admin plans)
  --storage-gb STORAGE_GB
                        private storage limit in decimal GB (x 1000^3 bytes)
  --public-storage-gb PUBLIC_STORAGE_GB
                        public storage limit in decimal GB
  --objects OBJECTS     objects per catalog (a guideline today)
  --catalogs CATALOGS   catalogs per project
  --sql-sinks SQL_SINKS
                        scheduled SQL sinks of the organisation; 0 = not
                        enabled (the default), N enables the feature with at
                        most N sinks
  --sql-min-every SECONDS
                        the shortest schedule (default 300)
  --sql-run-seconds SQL_RUN_SECONDS
                        one run's wall time (default 60)
  --sql-requests-per-run SQL_REQUESTS_PER_RUN
                        HTTP requests per run (default 1000)
  --sql-download-mb-per-run SQL_DOWNLOAD_MB_PER_RUN
                        MiB downloaded per run (default 1024)
  --projects PROJECTS   projects per account
  --clear FIELD         remove one override by its API name (lhbox admin plans
                        lists them, e.g. storage_bytes); repeatable
  --feature NAME=VALUE  switch a plan feature or set any limit by its API
                        name: on/off or a number, e.g. public_images=on
                        (photos served from public folders) or
                        device_sinks=20; repeatable (lhbox admin plans lists
                        the names)
  --note NOTE           why (kept with the change in the audit log)
  --login EMAIL         as this install administrator: password and
                        authenticator code asked on the terminal, the session
                        ended after the command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin org create

```
usage: lhbox admin org create [-h] --admin EMAIL [--name NAME] [--login EMAIL]
                              handle

positional arguments:
  handle         the organisation's handle (its address and the prefix of its
                 storage names)

options:
  -h, --help     show this help message and exit
  --admin EMAIL  the first admin's address
  --name NAME    its display name (default: the handle)
  --login EMAIL  as this install administrator: password and authenticator
                 code asked on the terminal, the session ended after the
                 command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin org list

```
usage: lhbox admin org list [-h] [--q Q] [--limit LIMIT] [--login EMAIL]

options:
  -h, --help     show this help message and exit
  --q Q          search handle or name
  --limit LIMIT
  --login EMAIL  as this install administrator: password and authenticator
                 code asked on the terminal, the session ended after the
                 command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin org admin

```
usage: lhbox admin org admin [-h] {add,remove} ...

positional arguments:
  {add,remove}
    add         make an address an admin (a member is promoted, an existing
                person added, anyone else invited)
    remove      an admin becomes a member (the last admin is refused); an open
                admin invitation is withdrawn

options:
  -h, --help    show this help message and exit
```

### lhbox admin org admin add

```
usage: lhbox admin org admin add [-h] [--login EMAIL] org email

positional arguments:
  org            org_id or handle
  email

options:
  -h, --help     show this help message and exit
  --login EMAIL  as this install administrator: password and authenticator
                 code asked on the terminal, the session ended after the
                 command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin org admin remove

```
usage: lhbox admin org admin remove [-h] [--login EMAIL] org member

positional arguments:
  org            org_id or handle
  member         email or principal_id

options:
  -h, --help     show this help message and exit
  --login EMAIL  as this install administrator: password and authenticator
                 code asked on the terminal, the session ended after the
                 command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin person

```
usage: lhbox admin person [-h] {suspend,reactivate,revoke-all} ...

positional arguments:
  {suspend,reactivate,revoke-all}
    suspend             refuse their login and every credential of theirs and
                        of their tokens, until reactivated
    reactivate          lift a suspension (not one the organisation's
                        directory made)
    revoke-all          revoke every session, API key and token of the person,
                        in every organisation (the account stays; not
                        reversible)

options:
  -h, --help            show this help message and exit
```

### lhbox admin person suspend

```
usage: lhbox admin person suspend [-h] [--reason REASON] [--login EMAIL]
                                  person

positional arguments:
  person           email or principal_id

options:
  -h, --help       show this help message and exit
  --reason REASON  kept in the audit log
  --login EMAIL    as this install administrator: password and authenticator
                   code asked on the terminal, the session ended after the
                   command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin person reactivate

```
usage: lhbox admin person reactivate [-h] [--login EMAIL] person

positional arguments:
  person         email or principal_id

options:
  -h, --help     show this help message and exit
  --login EMAIL  as this install administrator: password and authenticator
                 code asked on the terminal, the session ended after the
                 command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin person revoke-all

```
usage: lhbox admin person revoke-all [-h] [--yes] [--login EMAIL] person

positional arguments:
  person         email or principal_id

options:
  -h, --help     show this help message and exit
  --yes          do not ask: revoking every credential of the person cannot be
                 undone. Asked on a terminal; without one (or with --json)
                 --yes will be required from the next minor version of lhbox
                 (a warning today)
  --login EMAIL  as this install administrator: password and authenticator
                 code asked on the terminal, the session ended after the
                 command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin client

```
usage: lhbox admin client [-h] {add,list,disable,enable,remove} ...

positional arguments:
  {add,list,disable,enable,remove}
    add                 register a client (public by default; --confidential
                        prints its secret once)
    list                the clients install administrators registered, and
                        whether dynamic registration is on
    disable             no new sign-in and no refresh (reversible)
    enable              undo disable
    remove              remove it and revoke every connection made through it

options:
  -h, --help            show this help message and exit
```

### lhbox admin client add

```
usage: lhbox admin client add [-h] --name NAME --redirect-uri URI
                              [--client-id CLIENT_ID]
                              [--client-uri CLIENT_URI] [--confidential]
                              [--login EMAIL]

options:
  -h, --help            show this help message and exit
  --name NAME
  --redirect-uri URI    https, loopback http or a native app's scheme;
                        repeatable
  --client-id CLIENT_ID
                        the client's own fixed id (default: one is generated)
  --client-uri CLIENT_URI
                        its home page (https), shown on the consent page
  --confidential        a client with a secret (HTTP Basic or
                        client_secret_post)
  --login EMAIL         as this install administrator: password and
                        authenticator code asked on the terminal, the session
                        ended after the command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin client list

```
usage: lhbox admin client list [-h] [--all] [--login EMAIL]

options:
  -h, --help     show this help message and exit
  --all          the self-registered (dynamic) clients too
  --login EMAIL  as this install administrator: password and authenticator
                 code asked on the terminal, the session ended after the
                 command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin client disable

```
usage: lhbox admin client disable [-h] [--login EMAIL] client_id

positional arguments:
  client_id

options:
  -h, --help     show this help message and exit
  --login EMAIL  as this install administrator: password and authenticator
                 code asked on the terminal, the session ended after the
                 command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin client enable

```
usage: lhbox admin client enable [-h] [--login EMAIL] client_id

positional arguments:
  client_id

options:
  -h, --help     show this help message and exit
  --login EMAIL  as this install administrator: password and authenticator
                 code asked on the terminal, the session ended after the
                 command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin client remove

```
usage: lhbox admin client remove [-h] [--yes] [--login EMAIL] client_id

positional arguments:
  client_id

options:
  -h, --help     show this help message and exit
  --yes          do not ask: removing the client cannot be undone. Asked on a
                 terminal; without one (or with --json) --yes will be required
                 from the next minor version of lhbox (a warning today)
  --login EMAIL  as this install administrator: password and authenticator
                 code asked on the terminal, the session ended after the
                 command (default: LHBOX_OPERATOR_KEY)
```

### lhbox admin news

```
usage: lhbox admin news [-h] {status,create,show,edit,test,send} ...

positional arguments:
  {status,create,show,edit,test,send}
    status              subscribers, withdrawals, whether sending is
                        configured, the issues
    create              a draft issue from a Markdown file
    show                one issue: state, delivery counts, text
    edit                change a draft's subject or text (needs a new test)
    test                send the issue to one address, subject prefixed [test]
    send                send the issue to every subscriber, once (needs a test
                        after the last edit); on an issue already sending,
                        resumes a send the relay paused

options:
  -h, --help            show this help message and exit
```

### lhbox admin news status

```
usage: lhbox admin news status [-h]

options:
  -h, --help  show this help message and exit
```

### lhbox admin news create

```
usage: lhbox admin news create [-h] --subject SUBJECT --file FILE

options:
  -h, --help         show this help message and exit
  --subject SUBJECT
  --file FILE        Markdown file, or - for stdin
```

### lhbox admin news show

```
usage: lhbox admin news show [-h] issue

positional arguments:
  issue

options:
  -h, --help  show this help message and exit
```

### lhbox admin news edit

```
usage: lhbox admin news edit [-h] [--subject SUBJECT] [--file FILE] issue

positional arguments:
  issue

options:
  -h, --help         show this help message and exit
  --subject SUBJECT
  --file FILE        Markdown file, or - for stdin
```

### lhbox admin news test

```
usage: lhbox admin news test [-h] --to TO issue

positional arguments:
  issue

options:
  -h, --help  show this help message and exit
  --to TO
```

### lhbox admin news send

```
usage: lhbox admin news send [-h] [--retry-failed] issue

positional arguments:
  issue

options:
  -h, --help      show this help message and exit
  --retry-failed  also put the rows the relay refused for good back in line
```

### lhbox project

```
usage: lhbox project [-h] {create,list} ...

positional arguments:
  {create,list}

options:
  -h, --help     show this help message and exit
```

### lhbox project create

```
usage: lhbox project create [-h] --name NAME [--provider PROVIDER]
                            [--region REGION]
                            [--idempotency-key IDEMPOTENCY_KEY]

options:
  -h, --help            show this help message and exit
  --name NAME
  --provider PROVIDER   default: the deployment's own
  --region REGION       default: the deployment's own placement
  --idempotency-key IDEMPOTENCY_KEY
                        any string; safe to reuse when retrying the same call
```

### lhbox project list

```
usage: lhbox project list [-h]

options:
  -h, --help  show this help message and exit
```

### lhbox catalog

```
usage: lhbox catalog [-h]
                     {create,list,get,measure,update,rotate,grants,grant,revoke,delete,restore,publish,unpublish,public-url,uploader,lifecycle,agents-md} ...

positional arguments:
  {create,list,get,measure,update,rotate,grants,grant,revoke,delete,restore,publish,unpublish,public-url,uploader,lifecycle,agents-md}
    create              a new catalog: `lhbox catalog create <name>` puts it
                        in the organisation's default project; usually
                        seconds, up to ~2 minutes on a busy host, progress on
                        stderr
    list                every catalog you can see: name, id (warehouse_id in
                        the API), your level, project, status and the format
                        version its new tables get, side by side
    get                 one catalog by name or id: the row of `catalog list`
                        plus its bucket, storage figure, default format
                        version and status (ready, or provisioning while its
                        storage has not accepted a write yet)
    measure             measure a catalog's storage now (bytes and objects of
                        its two buckets) instead of waiting for the next
                        reading (every 5 minutes); once a minute per catalog
    update              change a catalog's settings: the format version
                        LakehouseBox gives its new tables (admin, or a write
                        holder) and its tier policy, hot or cold (admin)
    rotate              replace the catalog's credentials (read/write and
                        read-only): `lhbox catalog rotate <name>`; the old
                        keys and their tokens are refused at once, vended
                        storage sessions run out within an hour; slow
                        (identity writes at the store), progress on stderr
    grants              who holds read or write on a catalog, and how (admin,
                        membership, creator, explicit): `lhbox catalog grants
                        <name>`
    grant               grant read or write on a catalog to a member or a
                        token (admin, or a write holder up to write): `lhbox
                        catalog grant <name> --principal <id> --level
                        read|write`
    revoke              revoke a grant (admin, or whoever made it): `lhbox
                        catalog revoke <name> --principal <id>`; revoking a
                        member's membership read records level none
    delete              delete a catalog (admin): `lhbox catalog delete <name>
                        --confirm <name>` (or --yes); it stays restorable for
                        the service's grace period (`lhbox catalog restore`),
                        then tables, buckets and identities are purged;
                        --purge deletes for good at once
    restore             restore a catalog in its deletion grace period
                        (admin): `lhbox catalog restore <id or name>`; same
                        tables and buckets, a new key pair if the old one was
                        revoked, comes back private
    publish             make a catalog PUBLIC (admin, the name typed out):
                        every data file and every retained metadata.json
                        becomes readable by anyone without credentials, and
                        anyone can attach it read-only by name
                        (<handle>/<catalog>), no account needed; its bytes
                        count against the public allowance (5 GB; 50 GB once
                        reviewed)
    unpublish           make a public catalog private again (admin): `lhbox
                        catalog unpublish <name>`; anonymous reads are refused
                        at once
    public-url          the public URL of a catalog and the no-credentials
                        DuckDB recipe: `lhbox catalog public-url <name>`; with
                        --table, that table's current metadata URL (the thing
                        to paste into iceberg_scan)
    uploader            write-only credentials for the catalog's blob bucket:
                        a relay, a camera, a field device or a partner
                        dropping files under one prefix, never able to read,
                        list (unless asked) or touch tables
                        (https://lakehousebox.com/docs/uploaders/)
    lifecycle           expiry rules on the catalog's blob bucket: delete
                        camera/ after 90 days, abort stale multipart uploads;
                        never on tables
                        (https://lakehousebox.com/docs/lifecycle/)
    agents-md           the catalog's AGENTS.md: context for agents at the
                        root of its file bucket (read, --set, --remove)

options:
  -h, --help            show this help message and exit
```

### lhbox catalog create

```
usage: lhbox catalog create [-h] [--project PROJECT]
                            [--default-format-version {2,3}] [--no-wait]
                            [--idempotency-key IDEMPOTENCY_KEY]
                            [CATALOG]

POST /v1/catalogs. `lhbox catalog create <name>` (lowercase letters, digits,
_; a letter first). Usually done in a few seconds; on a busy host it can take
up to two minutes: a line every ~10 s says so; a timeout is exit 5 with the
remedy to check `catalog list` first. Should the server answer 202 status:
provisioning, the CLI polls until ready (--no-wait returns at once).

positional arguments:
  CATALOG               the catalog's name

options:
  -h, --help            show this help message and exit
  --project PROJECT     project_id (UUID) or project name; list both with:
                        lhbox project list (default: the organisation's
                        default project)
  --default-format-version {2,3}
                        the Iceberg format version (2 or 3) LakehouseBox gives
                        tables IT creates in this catalog: table create
                        without --format-version and table import; an engine's
                        own CREATE TABLE is honoured as sent (default 2)
  --no-wait             do not poll a 202 provisioning answer until ready
  --idempotency-key IDEMPOTENCY_KEY
                        any string; safe to reuse when retrying the same call
```

### lhbox catalog list

```
usage: lhbox catalog list [-h] [--project PROJECT]

options:
  -h, --help         show this help message and exit
  --project PROJECT  project_id (UUID) or project name; list both with: lhbox
                     project list (default: every project you can see)
```

### lhbox catalog get

```
usage: lhbox catalog get [-h] [CATALOG]

GET /v1/catalogs/{id}. `lhbox catalog get <name>`; without a name, your only
catalog.

positional arguments:
  CATALOG     the catalog's name (or its id; lhbox catalog list shows both);
              default: your only catalog (with several, name one)

options:
  -h, --help  show this help message and exit
```

### lhbox catalog measure

```
usage: lhbox catalog measure [-h] [CATALOG]

POST /v1/catalogs/{id}/measure. `lhbox catalog measure <name>`. Reads the
store's own figure for the catalog's table bucket and blob bucket and stores
it; GET /v1/usage and `catalog list` show it until the next reading
(stale_after). Read level suffices.

positional arguments:
  CATALOG     the catalog's name (or its id; lhbox catalog list shows both);
              default: your only catalog (with several, name one)

options:
  -h, --help  show this help message and exit
```

### lhbox catalog update

```
usage: lhbox catalog update [-h] [--default-format-version {2,3}]
                            [--tier {hot,cold}]
                            [--snapshot-retention-days N|default]
                            [--edge-reads {on,off}] [--virtual-host {on,off}]
                            [CATALOG]

PATCH /v1/catalogs/{id}. `lhbox catalog update <name> --default-format-version
2|3` and/or `--tier hot|cold` (the catalog is always named). the Iceberg
format version (2 or 3) LakehouseBox gives tables IT creates in this catalog:
table create without --format-version and table import; an engine's own CREATE
TABLE is honoured as sent. Tables an engine creates itself keep the version
the engine chose; existing tables are unchanged. GET /v1/config/formats (no
login) lists which engines write each version. Tier policy: cold (the default)
lets volumes untouched for the quiet period move to cold storage, where a
table's first read is slower; hot never moves them. The sample catalog and
public catalogs stay hot.

positional arguments:
  CATALOG               the catalog's name (or its id; lhbox catalog list
                        shows both)

options:
  -h, --help            show this help message and exit
  --default-format-version {2,3}
                        2 (written by every engine) or 3 (geometry/geography
                        columns; not every engine writes it yet)
  --tier {hot,cold}     hot: never moved to cold storage; cold: quiet volumes
                        move to cold storage (admin only)
  --snapshot-retention-days N|default
                        keep every snapshot of the last N days (1-90; at most
                        500 per table) so tables can be read or rolled back to
                        any point in them; `default` returns to 20 snapshots /
                        7 days, whichever is tighter (admin only; retained
                        snapshots count as storage)
  --edge-reads {on,off}
                        edge caching for this private catalog: its readers'
                        lhbox duckdb sessions read through our European CDN
                        (an admin; /docs/edge-caching/)
  --virtual-host {on,off}
                        serve https://<bucket>.s3.<domain> for this catalog
                        (Snowflake needs it; an admin asking for its recipe
                        turns it on). Its TLS certificate lists the bucket
                        names in the public Certificate Transparency logs
                        (admin only; off by default, public catalogs included)
```

### lhbox catalog rotate

```
usage: lhbox catalog rotate [-h] [--yes [NAME]] [--show-secrets] [CATALOG]

positional arguments:
  CATALOG         the catalog's name (or its id; lhbox catalog list shows
                  both)

options:
  -h, --help      show this help message and exit
  --yes [NAME]    do not ask: a rotation cannot be undone. Asked on a
                  terminal; without one (or with --json) --yes will be
                  required from the next minor version of lhbox (a warning
                  today)
  --show-secrets  print the two new secrets; without it they are ********
                  (fetch one later with `lhbox connect --show-secrets`)
```

### lhbox catalog grants

```
usage: lhbox catalog grants [-h] [CATALOG]

positional arguments:
  CATALOG     the catalog's name (or its id; lhbox catalog list shows both);
              default: your only catalog (with several, name one)

options:
  -h, --help  show this help message and exit
```

### lhbox catalog grant

```
usage: lhbox catalog grant [-h] --principal PRINCIPAL --level {read,write}
                           [--namespace NS]
                           [CATALOG]

positional arguments:
  CATALOG               the catalog's name (or its id; lhbox catalog list
                        shows both)

options:
  -h, --help            show this help message and exit
  --principal PRINCIPAL
                        principal_id of the member or token (lhbox org members
                        / token list)
  --level {read,write}
  --namespace NS        grant only this namespace of the catalog, to a
                        principal holding nothing on the catalog itself (an
                        enterprise feature; admins only); it gets its own
                        credentials, confined to the namespace
```

### lhbox catalog revoke

```
usage: lhbox catalog revoke [-h] --principal PRINCIPAL [--namespace NS]
                            [--yes [NAME]]
                            [CATALOG]

positional arguments:
  CATALOG               the catalog's name (or its id; lhbox catalog list
                        shows both)

options:
  -h, --help            show this help message and exit
  --principal PRINCIPAL
  --namespace NS        revoke the grant on this one namespace (the catalog-
                        level grant stays as it is)
  --yes [NAME]          do not ask: the revocation cannot be undone. Asked on
                        a terminal; without one (or with --json) --yes will be
                        required from the next minor version of lhbox (a
                        warning today)
```

### lhbox catalog delete

```
usage: lhbox catalog delete [-h] [--confirm [NAME]] [--purge] [CATALOG]

positional arguments:
  CATALOG               the catalog's name (or its id; lhbox catalog list
                        shows both)

options:
  -h, --help            show this help message and exit
  --confirm, --yes [NAME]
                        the catalog's name, exactly (a bare --yes confirms the
                        NAME given positionally); this cannot be undone
  --purge               skip the grace period: purge tables, buckets and
                        identities now, irreversibly (also ends the grace
                        period of a catalog already deleted; name it with
                        --confirm <name>)
```

### lhbox catalog restore

```
usage: lhbox catalog restore [-h] [CATALOG]

positional arguments:
  CATALOG     the catalog's name (or its id; lhbox catalog list shows both)

options:
  -h, --help  show this help message and exit
```

### lhbox catalog publish

```
usage: lhbox catalog publish [-h] [--confirm [NAME]] [--files PREFIX |
                             --no-files]
                             [CATALOG]

Puts one plain S3 bucket policy on the catalog's bucket (anyone may GET and
LIST its objects, unsigned; writes and deletes stay refused). Public means the
whole catalog: every table, every data file, every retained metadata.json
including the table's history, and the list of object names. Readers with no
account attach the whole catalog by name, read-only, through the public
catalog: ATTACH '<handle>/<catalog>' (TYPE iceberg, ENDPOINT <the public
catalog>, AUTHORIZATION_TYPE 'none'), its answers and files served from the
CDN; `lhbox catalog public-url <name>` prints that line, and a table's
metadata URL with --table. Any account also attaches it by name in lhbox
duckdb (a shared public-reader key: catalog reads only, replaced at
unpublish). The catalog's FILE bucket stays private unless --files <prefix>/
names one folder of it to publish too (GET, range requests and listing under
that folder only). Undo with: lhbox catalog unpublish <name> (tables and
files). `lhbox catalog publish <name> --confirm <name> [--files public/]` (or
--yes).

positional arguments:
  CATALOG               the catalog's name (or its id; lhbox catalog list
                        shows both)

options:
  -h, --help            show this help message and exit
  --confirm, --yes [NAME]
                        the catalog's name, exactly (a bare --yes confirms the
                        NAME given positionally); this cannot be undone
  --files PREFIX        also publish ONE folder of the catalog's file bucket,
                        e.g. public/ (ends in /): anyone may GET its data
                        files (range requests included; photos only for
                        organisations approved for it) and list the files
                        under it at public_files_url; the rest of the file
                        bucket stays private. Publishing again with another
                        folder replaces it; without --files the published
                        folder, if any, stays as it is
  --no-files            stop publishing the file bucket's folder; the
                        catalog's tables stay public
```

### lhbox catalog unpublish

```
usage: lhbox catalog unpublish [-h] [CATALOG]

positional arguments:
  CATALOG     the catalog's name (or its id; lhbox catalog list shows both)

options:
  -h, --help  show this help message and exit
```

### lhbox catalog public-url

```
usage: lhbox catalog public-url [-h] [--table NS.TABLE] [CATALOG]

positional arguments:
  CATALOG           the catalog's name (or its id; lhbox catalog list shows
                    both); default: your only catalog (with several, name one)

options:
  -h, --help        show this help message and exit
  --table NS.TABLE  <namespace>.<table>: also print its current metadata_url
                    and version_hint_url
```

### lhbox catalog uploader

```
usage: lhbox catalog uploader [-h] {create,list,revoke} ...

positional arguments:
  {create,list,revoke}
    create              a new uploader: a key that can only upload under its
                        prefix of the catalog's file bucket; the secret is
                        shown once (--show-key) or written to a 0600 file
                        (--save)
    list                the catalog's uploaders without their secrets (never
                        stored); --all includes revoked ones
    revoke              revoke an uploader by id or name: its key is deleted
                        at the store first and refused at once (revoke, then
                        create, to replace a lost or leaked secret)

options:
  -h, --help            show this help message and exit
```

### lhbox catalog uploader create

```
usage: lhbox catalog uploader create [-h] --name NAME [--prefix PREFIX]
                                     [--list] [--expires-at ISO8601]
                                     [--protocols LIST] [--save FILE]
                                     [--show-key]
                                     [CATALOG]

POST /v1/catalogs/{id}/uploaders. Write level. Without --save or --show-key
the command stops before calling the API: no identity is created whose secret
nobody received. The uploader cannot read what it uploads; consume the files
from the catalog side with the catalog's own credential (lhbox connect).
`lhbox catalog uploader create <catalog> --name ...`.

positional arguments:
  CATALOG               the catalog's name (or its id; lhbox catalog list
                        shows both); default: your only catalog (with several,
                        name one)

options:
  -h, --help            show this help message and exit
  --name NAME           who uploads: relay, camera-north, partner-acme (a
                        lowercase letter, then lowercase, digits, _ -)
  --prefix PREFIX       key prefix the credential is confined to, e.g.
                        photos/2026/ (default: the whole blob bucket)
  --list                also allow ListBucket under the prefix (an s3:prefix
                        condition: the device must list with ?prefix=<its
                        prefix>); without it the uploader cannot list at all
  --expires-at ISO8601  shown as the status 'expired' after that time; within
                        the hour after it the key is removed and a drop-box
                        login refused, as if revoked
  --protocols LIST      also a drop-box login for devices that speak only FTP:
                        a comma list of ftps, sftp, ftp (plain, unencrypted);
                        the login and password are generated and shown once
                        like the secret
  --save FILE           write the pair, endpoint, bucket and prefix to FILE
                        (mode 0600) and do not print the secret
  --show-key            print the secret once (as well as saving it, with
                        --save)
```

### lhbox catalog uploader list

```
usage: lhbox catalog uploader list [-h] [--all] [CATALOG]

positional arguments:
  CATALOG     the catalog's name (or its id; lhbox catalog list shows both);
              default: your only catalog (with several, name one)

options:
  -h, --help  show this help message and exit
  --all       include revoked uploaders
```

### lhbox catalog uploader revoke

```
usage: lhbox catalog uploader revoke [-h] --uploader UPLOADER [--yes [NAME]]
                                     [CATALOG]

positional arguments:
  CATALOG              the catalog's name (or its id; lhbox catalog list shows
                       both); default: your only catalog (with several, name
                       one)

options:
  -h, --help           show this help message and exit
  --uploader UPLOADER  the uploader_id or the name (lhbox catalog uploader
                       list)
  --yes [NAME]         do not ask: the revocation cannot be undone. Asked on a
                       terminal; without one (or with --json) --yes will be
                       required from the next minor version of lhbox (a
                       warning today)
```

### lhbox catalog lifecycle

```
usage: lhbox catalog lifecycle [-h] {set,get,clear} ...

positional arguments:
  {set,get,clear}
    set            add or replace ONE rule (by id; the default id is the
                   prefix), keeping the others
    get            the rules configured on the blob bucket, including any set
                   with boto3
    clear          remove every lifecycle rule from the blob bucket

options:
  -h, --help       show this help message and exit
```

### lhbox catalog lifecycle set

```
usage: lhbox catalog lifecycle set [-h] [--prefix PREFIX] [--expire-days N]
                                   [--expire-date YYYY-MM-DD]
                                   [--abort-multipart-days N] [--id ID]
                                   [--disabled] [--replace-all]
                                   [CATALOG]

PUT /v1/catalogs/{id}/lifecycle. Write level. Reads the current rules first
and re-puts them with this one (the S3 API replaces the whole document);
--replace-all keeps none. Objects are deleted at the store's next daily pass
after they are due (up to 24 h late), permanently. Transitions do not exist
here (no storage classes); --expire-days 0 is refused (the store would accept
it and never fire it).

positional arguments:
  CATALOG               the catalog's name (or its id; lhbox catalog list
                        shows both); default: your only catalog (with several,
                        name one)

options:
  -h, --help            show this help message and exit
  --prefix PREFIX       key prefix the rule applies to, e.g. camera/ (default:
                        the whole blob bucket)
  --expire-days N       delete objects N days (24 h each) after their last
                        write; at least 1
  --expire-date YYYY-MM-DD
                        delete every matching object at the first pass on or
                        after this date (a past date: the next pass)
  --abort-multipart-days N
                        abort multipart uploads not completed N days after
                        they began
  --id ID               the rule's id (default: the prefix without slashes, or
                        'bucket'); a rule with the same id is replaced
  --disabled            store the rule disabled (kept, not evaluated)
  --replace-all         drop every other rule, this one alone remains
```

### lhbox catalog lifecycle get

```
usage: lhbox catalog lifecycle get [-h] [--xml] [CATALOG]

positional arguments:
  CATALOG     the catalog's name (or its id; lhbox catalog list shows both);
              default: your only catalog (with several, name one)

options:
  -h, --help  show this help message and exit
  --xml       print the stored S3 LifecycleConfiguration document instead of
              the table
```

### lhbox catalog lifecycle clear

```
usage: lhbox catalog lifecycle clear [-h] [--yes [NAME]] [CATALOG]

positional arguments:
  CATALOG       the catalog's name (or its id; lhbox catalog list shows both);
                default: your only catalog (with several, name one)

options:
  -h, --help    show this help message and exit
  --yes [NAME]  do not ask: removing every rule cannot be undone. Asked on a
                terminal; without one (or with --json) --yes will be required
                from the next minor version of lhbox (a warning today)
```

### lhbox catalog agents-md

```
usage: lhbox catalog agents-md [-h] [--set FILE] [--base-sha256 SHA256]
                               [--remove] [--raw]
                               [CATALOG]

GET, PUT or DELETE /v1/catalogs/{id}/agents-md. The object AGENTS.md at the
root of the catalog's file bucket; readable anonymously while the catalog is
public. Reading needs read level; --set and --remove need write level. At most
64 KB of UTF-8; every write is audited.

positional arguments:
  CATALOG               the catalog's name (or its id; lhbox catalog list
                        shows both); default: your only catalog (with several,
                        name one)

options:
  -h, --help            show this help message and exit
  --set FILE            write FILE (or - for stdin) as the catalog's AGENTS.md
  --base-sha256 SHA256  with --set: write only if the current AGENTS.md has
                        this sha256 (none: only if there is none yet)
  --remove              delete the catalog's AGENTS.md
  --raw                 print the text alone, no header
```

### lhbox file

```
usage: lhbox file [-h] {cp,sync,put,get,ls,rm} ...

positional arguments:
  {cp,sync,put,get,ls,rm}
    cp                  copy files in or out, as aws s3 cp: `lhbox file cp
                        ./tiles.pmtiles lhbox://garden/public/`, `lhbox file
                        cp lhbox://garden/data/a.csv .`, `lhbox file cp
                        ./exports lhbox://garden/exports/ --recursive`
    sync                copy only what changed, as aws s3 sync: `lhbox file
                        sync ./exports lhbox://garden/exports/` (or
                        lhbox://garden/exports/ ./exports); never deletes
    put                 upload a file, or a directory recursively, to the
                        catalog's files: `lhbox file put tiles.pmtiles public/
                        --cache-control "public, max-age=86400"` (the same as
                        `lhbox file cp tiles.pmtiles
                        lhbox://<catalog>/public/`)
    get                 download one file: `lhbox file get data/a.csv [DEST]`
                        (-o PATH works too; - for stdout)
    ls                  list the catalog's files: `lhbox file ls public/` or
                        `lhbox file ls lhbox://garden/public/` (folders first;
                        --recursive for every key)
    rm                  delete files: `lhbox file rm data/a.csv` or `lhbox
                        file rm lhbox://garden/data/ --recursive` (asks only
                        on a terminal)

options:
  -h, --help            show this help message and exit
```

### lhbox file cp

```
usage: lhbox file cp [-h] [--catalog CATALOG] [--recursive] [--exclude GLOB]
                     [--include GLOB] [--content-type CONTENT_TYPE]
                     [--cache-control CACHE_CONTROL]
                     [--content-disposition CONTENT_DISPOSITION]
                     [--content-language CONTENT_LANGUAGE] [--meta NAME=VALUE]
                     [--part-size MIB] [--parallel N] [--skip-refused]
                     [--skip-unchanged] [--dry-run] [--progress] [--no-edge]
                     SRC DST

Exactly one side is the catalog, written lhbox://<catalog>/<key or prefix>
(the catalog's name or id); the other is a local path. Up (local -> catalog)
is `file put`: one file goes to the key, or under it when the key ends with /
(or is empty); a directory's contents go under the prefix, relative paths
kept, with or without --recursive (hidden files skipped). Down (catalog ->
local) is `file get` for one key (DEST a file, a directory, or - for stdout);
a key ending with /, or --recursive, downloads everything under it as a folder
into the DEST directory. s3:// URLs are not taken: the bucket is the
platform's name. --exclude/--include filter by path relative to the source.

positional arguments:
  SRC                   a local file or directory, or lhbox://<catalog>/<key
                        or prefix>
  DST                   lhbox://<catalog>/<key or prefix>, or a local path

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list; default: your only
                        catalog (with several, name one); a
                        lhbox://<catalog>/… address names it too
  --recursive           a folder: download everything under the prefix (an
                        upload of a directory is recursive either way)
  --exclude GLOB        leave out paths matching GLOB, relative to the source
                        folder or prefix (* crosses /); --exclude and
                        --include apply in order and the last match wins, as
                        in aws s3
  --include GLOB        take back paths matching GLOB after an --exclude (e.g.
                        --exclude '*' --include '*.csv')
  --content-type CONTENT_TYPE
                        the media type (default: from the file's name; a
                        published file is served with the type its name says
                        either way)
  --cache-control CACHE_CONTROL
                        the file's Cache-Control, kept and passed to readers
                        by the CDN of published files (default there: public,
                        max-age=3600)
  --content-disposition CONTENT_DISPOSITION
                        e.g. attachment; filename="report.csv"
  --content-language CONTENT_LANGUAGE
                        e.g. en, or es-ES
  --meta, --metadata NAME=VALUE
                        custom metadata, sent as x-amz-meta-NAME: name=value,
                        several as a=1,b=2 (aws's shorthand) or JSON {"a":
                        "1"}; repeat the flag for more
  --part-size MIB       files larger than this go up in parts of this size
                        (MiB, at least 5; default 64)
  --parallel N          parts uploaded at once (default 4)
  --skip-refused        upload what the catalog's files accept and list the
                        rest as skipped (web pages, scripts, programs, and
                        files over 100 kB of a type not on the list); without
                        it such a file stops the upload before anything is
                        sent
  --skip-unchanged      skip files whose stored copy has the same size and
                        ETag (what sync does)
  --dry-run, --dryrun   list what would be copied and send nothing
  --progress            progress lines on stderr even when it is not a
                        terminal
  --no-edge             download from the storage even where the catalog has
                        edge caching (a network that cannot reach the CDN)
```

### lhbox file sync

```
usage: lhbox file sync [-h] [--catalog CATALOG] [--exclude GLOB]
                       [--include GLOB] [--content-type CONTENT_TYPE]
                       [--cache-control CACHE_CONTROL]
                       [--content-disposition CONTENT_DISPOSITION]
                       [--content-language CONTENT_LANGUAGE]
                       [--meta NAME=VALUE] [--part-size MIB] [--parallel N]
                       [--skip-refused] [--dry-run] [--progress] [--no-edge]
                       SRC DST

Recursive cp that skips every file whose other copy has the same size and ETag
(its content: a second run sends nothing); any other file is copied over, so
the source wins (a file edited on the destination side is replaced, whatever
its age). Up: the directory's contents go under the prefix. Down: everything
under the prefix into the directory, relative paths kept. A key ending with /
or not, SRC/DST under lhbox:// is a folder. Nothing is ever deleted on either
side (no --delete): lhbox file rm --recursive removes files from the catalog.

positional arguments:
  SRC                   a local directory, or lhbox://<catalog>/<prefix>
  DST                   lhbox://<catalog>/<prefix>, or a local directory

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list; default: your only
                        catalog (with several, name one); a
                        lhbox://<catalog>/… address names it too
  --exclude GLOB        leave out paths matching GLOB, relative to the source
                        folder or prefix (* crosses /); --exclude and
                        --include apply in order and the last match wins, as
                        in aws s3
  --include GLOB        take back paths matching GLOB after an --exclude (e.g.
                        --exclude '*' --include '*.csv')
  --content-type CONTENT_TYPE
                        the media type (default: from the file's name; a
                        published file is served with the type its name says
                        either way)
  --cache-control CACHE_CONTROL
                        the file's Cache-Control, kept and passed to readers
                        by the CDN of published files (default there: public,
                        max-age=3600)
  --content-disposition CONTENT_DISPOSITION
                        e.g. attachment; filename="report.csv"
  --content-language CONTENT_LANGUAGE
                        e.g. en, or es-ES
  --meta, --metadata NAME=VALUE
                        custom metadata, sent as x-amz-meta-NAME: name=value,
                        several as a=1,b=2 (aws's shorthand) or JSON {"a":
                        "1"}; repeat the flag for more
  --part-size MIB       files larger than this go up in parts of this size
                        (MiB, at least 5; default 64)
  --parallel N          parts uploaded at once (default 4)
  --skip-refused        upload what the catalog's files accept and list the
                        rest as skipped (web pages, scripts, programs, and
                        files over 100 kB of a type not on the list); without
                        it such a file stops the upload before anything is
                        sent
  --dry-run, --dryrun   list what would be copied and send nothing
  --progress            progress lines on stderr even when it is not a
                        terminal
  --no-edge             download from the storage even where the catalog has
                        edge caching (a network that cannot reach the CDN)
```

### lhbox file put

```
usage: lhbox file put [-h] [--catalog CATALOG] [--content-type CONTENT_TYPE]
                      [--cache-control CACHE_CONTROL]
                      [--content-disposition CONTENT_DISPOSITION]
                      [--content-language CONTENT_LANGUAGE]
                      [--meta NAME=VALUE] [--part-size MIB] [--parallel N]
                      [--skip-refused] [--skip-unchanged] [--recursive]
                      [--exclude GLOB] [--include GLOB] [--dry-run]
                      [--progress]
                      PATH [KEY]

Uploads through URLs the API signs (POST /v1/catalogs/{id}/blobs/upload; a
file over --part-size goes up in parts, in parallel, each retried on its own:
POST …/blobs/multipart). Write level. KEY: the key for one file (KEY ending in
/ = KEY + the file's name; default: its name), or the prefix for a directory
(default: the directory's name), under which relative paths are kept; hidden
files are skipped. KEY may be lhbox://<catalog>/<key>. Prints JSON {uploaded,
skipped, count, bytes, seconds} when piped. Keys starting with _ at the root
and AGENTS.md are the platform's.

positional arguments:
  PATH                  a local file or directory
  KEY                   the key (one file) or prefix (a directory) in the
                        catalog's files

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list; default: your only
                        catalog (with several, name one); a
                        lhbox://<catalog>/… address names it too
  --content-type CONTENT_TYPE
                        the media type (default: from the file's name; a
                        published file is served with the type its name says
                        either way)
  --cache-control CACHE_CONTROL
                        the file's Cache-Control, kept and passed to readers
                        by the CDN of published files (default there: public,
                        max-age=3600)
  --content-disposition CONTENT_DISPOSITION
                        e.g. attachment; filename="report.csv"
  --content-language CONTENT_LANGUAGE
                        e.g. en, or es-ES
  --meta, --metadata NAME=VALUE
                        custom metadata, sent as x-amz-meta-NAME: name=value,
                        several as a=1,b=2 (aws's shorthand) or JSON {"a":
                        "1"}; repeat the flag for more
  --part-size MIB       files larger than this go up in parts of this size
                        (MiB, at least 5; default 64)
  --parallel N          parts uploaded at once (default 4)
  --skip-refused        upload what the catalog's files accept and list the
                        rest as skipped (web pages, scripts, programs, and
                        files over 100 kB of a type not on the list); without
                        it such a file stops the upload before anything is
                        sent
  --skip-unchanged      skip files whose stored copy has the same size and
                        ETag
  --recursive           accepted as in aws s3; a directory is uploaded
                        recursively with or without it
  --exclude GLOB        leave out paths matching GLOB, relative to the source
                        folder or prefix (* crosses /); --exclude and
                        --include apply in order and the last match wins, as
                        in aws s3
  --include GLOB        take back paths matching GLOB after an --exclude (e.g.
                        --exclude '*' --include '*.csv')
  --dry-run, --dryrun   list what would be uploaded and send nothing
  --progress            progress lines on stderr even when it is not a
                        terminal
```

### lhbox file get

```
usage: lhbox file get [-h] [--catalog CATALOG] [-o PATH] [--progress]
                      [--no-edge]
                      KEY [DEST]

positional arguments:
  KEY                the file's key, or lhbox://<catalog>/<key>
  DEST               where to write it (a directory keeps the name; - =
                     stdout; default: the file's name here)

options:
  -h, --help         show this help message and exit
  --catalog CATALOG  the catalog's warehouse_id (UUID) or its name; list both
                     with: lhbox catalog list; default: your only catalog
                     (with several, name one); a lhbox://<catalog>/… address
                     names it too
  -o, --output PATH  the same as DEST
  --progress         progress lines on stderr even when it is not a terminal
  --no-edge          download from the storage even where the catalog has edge
                     caching (a network that cannot reach the CDN)
```

### lhbox file ls

```
usage: lhbox file ls [-h] [--catalog CATALOG] [--recursive] [PREFIX]

positional arguments:
  PREFIX             list under this prefix (end a folder with /);
                     lhbox://<catalog>/<prefix> works too

options:
  -h, --help         show this help message and exit
  --catalog CATALOG  the catalog's warehouse_id (UUID) or its name; list both
                     with: lhbox catalog list; default: your only catalog
                     (with several, name one); a lhbox://<catalog>/… address
                     names it too
  --recursive        every key under the prefix, no folders
```

### lhbox file rm

```
usage: lhbox file rm [-h] [--catalog CATALOG] [--recursive] [--exclude GLOB]
                     [--include GLOB] [--dry-run] [--yes]
                     KEY [KEY ...]

positional arguments:
  KEY                  the keys (or prefixes, with --recursive);
                       lhbox://<catalog>/<key> works too

options:
  -h, --help           show this help message and exit
  --catalog CATALOG    the catalog's warehouse_id (UUID) or its name; list
                       both with: lhbox catalog list; default: your only
                       catalog (with several, name one); a lhbox://<catalog>/…
                       address names it too
  --recursive          delete every file under each KEY taken as a folder
  --exclude GLOB       leave out paths matching GLOB, relative to the source
                       folder or prefix (* crosses /); --exclude and --include
                       apply in order and the last match wins, as in aws s3
  --include GLOB       take back paths matching GLOB after an --exclude (e.g.
                       --exclude '*' --include '*.csv')
  --dry-run, --dryrun  list what would be deleted and delete nothing
  --yes                do not ask: the deletion cannot be undone. Asked [y/N]
                       only when a person is at a terminal; a script or an
                       agent (no terminal) deletes without it, as aws s3 rm
                       does
```

### lhbox namespace

```
usage: lhbox namespace [-h] {list,delete} ...

positional arguments:
  {list,delete}
    delete       drop one EMPTY namespace: `lhbox namespace delete <ns>
                 --catalog <name>`; asks unless --yes; a namespace with tables
                 is refused (drop them first with `table delete`), a public
                 catalog wants --force

options:
  -h, --help     show this help message and exit
```

### lhbox namespace list

```
usage: lhbox namespace list [-h] [--catalog CATALOG]

options:
  -h, --help         show this help message and exit
  --catalog CATALOG  the catalog's warehouse_id (UUID) or its name; list both
                     with: lhbox catalog list; default: your only catalog
                     (with several, name one)
```

### lhbox namespace delete

```
usage: lhbox namespace delete [-h] [--catalog CATALOG] [--yes] [--force]
                              NAMESPACE

positional arguments:
  NAMESPACE

options:
  -h, --help         show this help message and exit
  --catalog CATALOG  the catalog's warehouse_id (UUID) or its name; list both
                     with: lhbox catalog list; default: your only catalog
                     (with several, name one)
  --yes              do not ask (required without a terminal or with --json);
                     the drop cannot be undone
  --force            drop it even in a PUBLIC catalog (an admin only), whose
                     public URL then stops serving it
```

### lhbox table

```
usage: lhbox table [-h]
                   {list,get,delete,create,import,add-files,set-properties,document} ...

positional arguments:
  {list,get,delete,create,import,add-files,set-properties,document}
    list                every table in a catalog, or in one namespace
    get                 schema, real format-version, snapshot and row count:
                        `lhbox table get <catalog>.<ns>.<table>` (or
                        <ns>.<table> --catalog <name>)
    delete              drop one table and its data files: `lhbox table delete
                        <ns>.<table> --catalog <name>`; asks unless --yes; a
                        public catalog wants --force; cannot be undone
    create              a new table from --column specs or a schema file:
                        `lhbox table create <catalog>.<ns>.<table> --column
                        id:long:required …` (or <ns>.<table> --catalog <name>)
    import              import Parquet or CSV files into a table as ONE
                        commit: DuckDB on this machine runs one CREATE TABLE …
                        AS SELECT (or INSERT INTO … SELECT) over
                        read_parquet([all the files]) or read_csv([…]);
                        GeoParquet metadata is kept as geo.* table properties
    add-files           register Parquet files staged in the catalog's files
                        (lhbox file put) in a table AS THEY ARE: copied in the
                        store into the table, ONE commit (no read of the rows,
                        no sort, no rewrite)
    set-properties      set or remove table properties (one commit through the
                        catalog): --property key=value …, --remove key …
    document            describe a table and its columns for the people and
                        agents who read it (one commit): --doc TEXT, --column
                        name=TEXT …, --from FILE

options:
  -h, --help            show this help message and exit
```

### lhbox table list

```
usage: lhbox table list [-h] [--catalog CATALOG] [--namespace NAMESPACE]

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list; default: your only
                        catalog (with several, name one)
  --namespace NAMESPACE
                        restrict to one namespace
```

### lhbox table get

```
usage: lhbox table get [-h] [--catalog CATALOG] [[CATALOG.]NAMESPACE.TABLE]

positional arguments:
  [CATALOG.]NAMESPACE.TABLE
                        the table: catalog.namespace.table, or namespace.table
                        with --catalog

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list
```

### lhbox table delete

```
usage: lhbox table delete [-h] [--catalog CATALOG] [--yes] [--force]
                          [CATALOG.]NAMESPACE.TABLE

positional arguments:
  [CATALOG.]NAMESPACE.TABLE
                        the table: catalog.namespace.table, or namespace.table
                        with --catalog

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list; default: your only
                        catalog (with several, name one)
  --yes                 do not ask (required without a terminal or with
                        --json); the drop cannot be undone
  --force               drop it even in a PUBLIC catalog (an admin only),
                        whose public URL then stops serving it
```

### lhbox table create

```
usage: lhbox table create [-h] [--catalog CATALOG] [--column COLUMN]
                          [--schema-file SCHEMA_FILE] [--format-version {2,3}]
                          [--partition TRANSFORM(COLUMN)]
                          [--idempotency-key IDEMPOTENCY_KEY]
                          [[CATALOG.]NAMESPACE.TABLE]

positional arguments:
  [CATALOG.]NAMESPACE.TABLE
                        the table: catalog.namespace.table, or namespace.table
                        with --catalog

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list
  --column COLUMN       name:type[:required][:identifier], repeatable
  --schema-file SCHEMA_FILE
                        a full Iceberg schema as JSON, e.g.
                        {"type":"struct","schema-id":0,"identifier-field-ids":
                        [1],"fields":[{"id":1,"name":"id","required":true,"typ
                        e":"long"}]}
  --format-version {2,3}
                        2 or 3; default: the catalog's default_format_version
                        (2 unless changed with lhbox catalog update). 3 holds
                        geometry/geography columns; GET /v1/config/formats
                        says which engines write each. The version actually
                        used is printed on stderr.
  --partition TRANSFORM(COLUMN)
                        partition the table, repeatable: day(ts), hour(ts),
                        month(ts), year(ts), a column name (identity),
                        bucket[16](id), truncate[4](code). A sink feeds a
                        format-version 3 table partitioned by time (day(<time
                        column>) for positions: one day per partition, ordered
                        in space by the maintenance). Writing it from DuckDB
                        yourself: SET TimeZone = 'UTC' first (DuckDB 1.5.6
                        files a timestamptz under its day in the session's
                        zone)
  --idempotency-key IDEMPOTENCY_KEY
                        any string; safe to reuse when retrying the same call
```

### lhbox table import

```
usage: lhbox table import [-h] [--catalog CATALOG]
                          [--format {auto,csv,parquet}] [--python PATH]
                          [--full] [--mode {create,append}]
                          [--format-version {2,3}] [--order-by COLUMN |
                          --no-order] [--no-version-check]
                          [--row-group-rows N]
                          [--idempotency-key IDEMPOTENCY_KEY]
                          [[CATALOG.]NAMESPACE.TABLE]
                          FILE_OR_GLOB [FILE_OR_GLOB ...]

Imports many files in one statement, so one Iceberg commit however many files
(44 files one by one were 44 commits and 44 waits). Parquet (.parquet) or CSV
(.csv, .csv.gz, .tsv; header and types auto-detected by DuckDB's read_csv) by
extension, or --format; one format per run. Globs are expanded here; s3:// and
https:// sources are read by DuckDB's httpfs. DuckDB is the python module of
the interpreter running lhbox when importable (--python PATH or LHBOX_PYTHON
names another interpreter's), else the `duckdb` binary on PATH (1.5.5 or
newer). The catalog credential goes recipe -> DuckDB and is never printed. A
format-version 3 table (--format-version 3, or the catalog's default) is
created through the API with the schema DuckDB inferred (geometry columns
typed), then filled with one INSERT; a format-version 2 table is created empty
by DuckDB with its own column types (geometry as BLOB, written as WKB), then
filled with one INSERT. Every table gets the Iceberg property write.target-
file-size-bytes = 134217728 (128 MiB) before its first row, and the rows are
written ORDER BY the first DATE/TIMESTAMP column (--order-by names another,
--no-order keeps the files' order) with preserve_insertion_order on, so a
query on that column skips whole files (docs/performance). Files with a
geometry column are written in Hilbert order of the primary geometry by ONE
writer thread, with 8 MB row groups for that INSERT (the property is removed
after it); the thread and row-group settings are temporary DuckDB 1.5
workarounds (the report's spatial_layout says so). Query such a table with
`geometry && <shape> AND ST_Intersects(geometry, <shape>)`: DuckDB skips files
and row groups for && only. When a file carries GeoParquet metadata, the table
gets geo.encoding, geo.crs, geo.columns, geo.primary_column (docs/engines).

positional arguments:
  [CATALOG.]NAMESPACE.TABLE
                        the table, [catalog.]namespace.table (the namespace is
                        created on demand); then the files
  FILE_OR_GLOB          Parquet or CSV files, globs (quote them:
                        'data/**/*.parquet') or URLs (s3://…, https://…)

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list; default: your only
                        catalog (with several, name one)
  --format {auto,csv,parquet}
                        what the files are (default auto: by extension; an
                        extension nobody recognises is a usage error)
  --python PATH         the interpreter whose duckdb module runs the import
                        (env LHBOX_PYTHON; default: the python running lhbox,
                        else the duckdb binary on PATH)
  --full                include the generated SQL statement (every column and
                        file) in the JSON
  --mode {create,append}
                        create (default; the table must not exist: 409 -> exit
                        2 otherwise) or append into an existing table (columns
                        matched by name; missing ones become NULL)
  --format-version {2,3}
                        for --mode create: 2 or 3; default the catalog's
                        default_format_version. 3 goes through the API (POST
                        /v1/tables with the inferred schema, then INSERT); 2
                        is DuckDB's own CREATE TABLE, then INSERT
  --order-by COLUMN     write the rows sorted by this column (ORDER BY,
                        insertion order preserved), so a query filtering on it
                        skips the files outside its range; default: files with
                        a geometry column in Hilbert order of their primary
                        geometry, others by their first DATE/TIMESTAMP column
  --no-order            keep the files' own row order (no ORDER BY): for files
                        already sorted, e.g. by gpio sort hilbert
  --no-version-check    import with a DuckDB older than 1.5.5 anyway (its
                        manifests are not maintained by the catalog)
  --row-group-rows N    write N rows per Parquet row group, through PyIceberg
                        (0.12+, with pyarrow and duckdb, in the --python
                        interpreter): for tables of few, large rows (e.g. an
                        image tile per row: use 1), where DuckDB's row groups
                        hold the whole file and a point query reads all of it.
                        Rows in --order-by order; the table's creation (or the
                        property) and the rows are ONE commit; format-version
                        2, no geometry columns. Known limitation: memory grows
                        with the source file, not bounded yet (a 988 MB file
                        written as one row group peaked at 3.5 GB); import a
                        large set in parts (--mode append) until it is, or lay
                        the files out yourself and use table add-files
  --idempotency-key IDEMPOTENCY_KEY
                        any string; safe to reuse when retrying the same call
```

### lhbox table add-files

```
usage: lhbox table add-files [-h] [--catalog CATALOG] [--mode {create,append}]
                             [--sorted-by COLUMN] [--keep-staged]
                             [--python PATH] [--full]
                             [[CATALOG.]NAMESPACE.TABLE] KEY [KEY ...]

For data whose producer lays it out itself (row groups, order, compression;
files written with your own tools): stage the files with lhbox file put, then
add-files copies them in the store into the table's data/ (no byte passes
through this machine) and registers them with ONE PyIceberg add_files, so one
commit and one manifest, and readers skip files by the column bounds in their
footers. Then the staged files are deleted (--keep-staged keeps them). Only
the footers are read: every file must have the same schema (and, with --mode
append, fit the table's); files carrying Iceberg field ids are refused; layout
warnings (row groups over 64 MB or of wide rows, unsigned columns, --sorted-by
ranges that overlap) are reported, never acted on. Format-version 2 tables;
needs pyiceberg 0.12+ with pyarrow in the --python interpreter, no DuckDB. A
failure before the commit deletes what it copied and keeps the staged files.
To rewrite rows (local files, CSV, URLs, sorting, small row groups) use table
import.

positional arguments:
  [CATALOG.]NAMESPACE.TABLE
                        the table, [catalog.]namespace.table (the namespace is
                        created on demand); then the staged files
  KEY                   files in the catalog's files: a key
                        (staging/a.parquet), a folder (staging/iberia/: every
                        .parquet under it) or a glob (quoted:
                        'staging/iberia/*.parquet'; ** crosses folders)

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list; default: your only
                        catalog (with several, name one)
  --mode {create,append}
                        create (default; the table must not exist, its schema
                        is the first file's) or append into an existing
                        format-version 2 table whose schema the files fit
  --sorted-by COLUMN    the column the files are sorted by: the report warns
                        when files' ranges of it overlap (or lack statistics),
                        since a filter on it then reads more files; nothing is
                        reordered
  --keep-staged         keep the staged files after the commit (default:
                        deleted once the table has its copies)
  --python PATH         the interpreter with pyiceberg 0.12+ and pyarrow (env
                        LHBOX_PYTHON; default: the python running lhbox)
  --full                include every staged file's footer summary (key, rows,
                        row groups, --sorted-by range) in the JSON
```

### lhbox table set-properties

```
usage: lhbox table set-properties [-h] [--catalog CATALOG]
                                  [--property KEY=VALUE] [--remove KEY]
                                  [[CATALOG.]NAMESPACE.TABLE]

positional arguments:
  [CATALOG.]NAMESPACE.TABLE
                        the table: catalog.namespace.table, or namespace.table
                        with --catalog

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list
  --property KEY=VALUE  repeatable; values are strings
  --remove KEY          repeatable
```

### lhbox table document

```
usage: lhbox table document [-h] [--catalog CATALOG] [--doc TEXT]
                            [--column NAME=TEXT] [--from FILE]
                            [[CATALOG.]NAMESPACE.TABLE]

positional arguments:
  [CATALOG.]NAMESPACE.TABLE
                        the table: catalog.namespace.table, or namespace.table
                        with --catalog

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list
  --doc TEXT            the table's description: its grain, where it comes
                        from, how fresh it is
  --column NAME=TEXT    a column's description; repeatable
  --from FILE           JSON {"doc": "...", "columns": {"name": "..."}}, or -
                        for stdin
```

### lhbox credentials

```
usage: lhbox credentials [-h] [--catalog CATALOG] [--show-secrets]
                         [[CATALOG.]NAMESPACE.TABLE]

positional arguments:
  [CATALOG.]NAMESPACE.TABLE
                        the table: catalog.namespace.table, or namespace.table
                        with --catalog

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list
  --show-secrets        print the secret key and the session token (for boto3
                        or the aws CLI); without it they are ********
```

### lhbox connect

```
usage: lhbox connect [-h] [--catalog CATALOG]
                     [--engine {duckdb,pyiceberg,spark,snowflake,trino,polars,clickhouse}]
                     [--write | --readonly] [--no-version-check]
                     [--show-secrets]

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list; default: your only
                        catalog (with several, name one); or
                        <handle>/<catalog> for another organisation's PUBLIC
                        catalog, attached read-only as <handle>_<catalog>
                        (--engine duckdb only)
  --engine {duckdb,pyiceberg,spark,snowflake,trino,polars,clickhouse}
                        trino prints a catalog properties file; polars a
                        PyIceberg-backed Python snippet; clickhouse reads only
                        (icebergS3, one table at a time); snowflake reads
                        unless --write
  --write               the WRITING recipe, asked for explicitly (you must
                        hold write on the catalog). For snowflake: the
                        read/write key pair in an external volume with
                        ALLOW_WRITES = TRUE plus a catalog-linked database, so
                        Snowflake creates and writes Iceberg tables here; the
                        pair is static and stays in Snowflake
  --readonly            the catalog's READ-ONLY recipe whatever level you hold
                        (identity t-<uuid>-ro: the catalog refuses its
                        commits, the store its PUT/DELETE) -- with --readonly
                        --show-secrets, the recipe to send a colleague who
                        should read your catalog, or to paste under 'Add
                        catalog' in the account page's explorer (the secret is
                        masked otherwise)
  --no-version-check    do not run `duckdb --version` / `python3 -c 'import
                        duckdb'` (writes need DuckDB 1.5.5 or newer: older
                        versions write manifests the catalog's maintenance
                        cannot read)
  --show-secrets        print the catalog credential's secret; without it the
                        secret is ******** wherever the recipe repeats it (the
                        key id is shown) and a DuckDB recipe starts with an
                        error() naming this flag. Pipe it rather than show it:
                        `lhbox connect --show-secrets --human | duckdb`;
                        `lhbox duckdb` attaches without showing it; the sample
                        code reads LHBOX_<ALIAS>_CLIENT_ID / _CLIENT_SECRET
```

### lhbox duckdb

```
usage: lhbox duckdb [-h] [--catalog CATALOG] [--no-edge] [--persist]
                    [--python PATH] [--no-version-check]
                    ...

Without --persist: runs the duckdb binary with -init pointing at a private
0600 file holding the connect recipe (secrets and ATTACH), removes the file a
second later, and hands you the shell with the catalog attached under its
alias. Arguments after -- go to duckdb. With --persist: runs CREATE OR REPLACE
PERSISTENT SECRET lhbox_<alias> (TYPE ICEBERG) and lhbox_<alias>_s3 (TYPE S3,
scoped to the catalog's bucket) in DuckDB, so any later session -- the shell
or python -- needs only the printed ATTACH line. Trade-off: the persistent
secrets keep the catalog credential on disk in DuckDB's secret directory
(~/.duckdb/stored_secrets, 0600), readable by any DuckDB this user runs;
revoke with lhbox catalog rotate (for a catalog lhbox new made, once a person
has claimed it) or DROP PERSISTENT SECRET.

positional arguments:
  -- DUCKDB_ARGS      passed to the duckdb binary, e.g. -- -readonly or --
                      mydb.duckdb

options:
  -h, --help          show this help message and exit
  --catalog CATALOG   the catalog's warehouse_id (UUID) or its name; list both
                      with: lhbox catalog list; default: your only catalog
                      (with several, name one); or <handle>/<catalog> for
                      another organisation's PUBLIC catalog, attached read-
                      only as <handle>_<catalog> (not with --persist)
  --no-edge           read from the storage even where the catalog has edge
                      caching on (a network that reaches the storage but not
                      the CDN)
  --persist           write persistent DuckDB secrets and print the ATTACH
                      line instead of opening a shell
  --python PATH       with --persist: the interpreter whose duckdb module
                      writes the secrets (env LHBOX_PYTHON)
  --no-version-check  do not warn about a duckdb older than 1.5.5
```

### lhbox sink

```
usage: lhbox sink [-h]
                  {create,list,get,delete,send,device-token,mapping,test,preview,run,runs,pause,resume,connection,dedupe,update} ...

positional arguments:
  {create,list,get,delete,send,device-token,mapping,test,preview,run,runs,pause,resume,connection,dedupe,update}
    create              a new sink on an existing table (unpartitioned, or a
                        format-version 3 table partitioned by time); the send
                        key is shown once (--show-key) or written to a 0600
                        file (--save)
    list                the sinks of every catalog you can see (or of one),
                        with what is waiting and the last roll; never a key
    get                 one sink by id or name: state, roll policy, lag, last
                        roll, rejects, totals
    delete              delete a sink: its send key is refused from now on;
                        batches still waiting are discarded (--force when
                        there are any); the table is untouched
    send                POST one batch (a JSON array of objects in a file, or
                        - for stdin) to a sink with its send key, or with your
                        own key when it holds the sink's send grant
    device-token        give a sink a device route (a new URL; the old one is
                        refused at once), or --revoke it
    mapping             show a sink's mapping, or set it (--preset / --mapping
                        FILE) or remove it (--clear)
    test                dry-run a device body through a sink (the pinned
                        secret, the strip list, the mapping) with your own
                        key; nothing is stored
    preview             run a scheduled-SQL query once, now, and write
                        nothing: rows, columns and their Iceberg types, and
                        whether they fit a table
    run                 run a SQL sink's query now, outside its schedule (a
                        paused sink too)
    runs                a SQL sink's runs, newest first: state, rows,
                        requests, bytes, time, errors
    pause               stop a SQL sink's schedule (lhbox sink run still runs
                        it by hand)
    resume              restart a paused SQL sink's schedule
    connection          credentials a scheduled-SQL query sends to an
                        authenticated source (bearer, header, basic): write-
                        only, bound to a URL prefix
    dedupe              keep only the latest row per key in a keyed sink's
                        whole table, once (rows stored before the key was set
                        keep their duplicates otherwise)
    update              change a SQL sink's query or schedule (a new query is
                        previewed against the table first), or any sink's key

options:
  -h, --help            show this help message and exit
```

### lhbox sink create

```
usage: lhbox sink create [-h] [--catalog CATALOG] --table NS.TABLE --name NAME
                         [--roll-seconds N] [--roll-bytes N]
                         [--inactivity-seconds N] [--key COL[,COL]]
                         [--save FILE] [--show-key] [--device]
                         [--require-field FIELD=VALUE] [--strip FIELD]
                         [--preset NAME] [--param NAME=VALUE] [--mapping FILE]
                         [--sql SQL] [--every DURATION] [--offset DURATION]
                         [--create-table] [--format-version {2,3}]
                         [--partition TRANSFORM(COLUMN)] [--paused]
                         [--engine {2.0,1.5}] [--mode {append,replace}]
                         [--min-rows N] [--replace-existing]

POST /v1/sinks. Write level on the catalog. Nothing is committed until the
first roll, which adds the optional __ingest_ts column and the metadata-
retention properties. One sink per table. Without --save or --show-key the
command stops before calling the API: no sink is created whose key nobody
received. `lhbox sink create --catalog <catalog> --table <ns>.<table> --name
<name> --save sink.json`.

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list; default: your only
                        catalog (with several, name one)
  --table NS.TABLE      the table the sink feeds, <namespace>.<table>; it must
                        exist (lhbox table create)
  --name NAME           the sink's name (the catalog rule: lowercase letter,
                        then lowercase, digits, _)
  --roll-seconds N      commit what is waiting every N seconds (default 300,
                        floor 60); a roll is a commit, 300-900 suits a small
                        producer
  --roll-bytes N        also roll when N bytes wait (32-64 MiB; default 64
                        MiB)
  --inactivity-seconds N
                        also roll when the sink has been quiet for N seconds
  --key COL[,COL]       replace the rows that share these columns at each roll
                        instead of appending them again (a format-version 2
                        table; e.g. --key observation_id): the latest row per
                        key wins
  --save FILE           write the send key, the send URL and the sink id to
                        FILE (mode 0600) and do not print the key
  --show-key            print the send key once (as well as saving it, with
                        --save)

device intake:
  --device              also give the sink a device route: a URL a device
                        posts to with no header (form, JSON, NDJSON or CSV);
                        saved with the send key
  --require-field FIELD=VALUE
                        a body field the device must send with this value
                        (e.g. PASSKEY=...); hashed here, stripped before
                        storage
  --strip FIELD         a body field never stored (repeatable)
  --preset NAME         a mapping shipped with the platform: ecowitt,
                        ispindel, ttn (The Things Stack webhooks), sensor-
                        community
  --param NAME=VALUE    a preset's parameter, e.g. WS=ws-01 (repeatable)
  --mapping FILE        a mapping document (JSON), validated against the table

scheduled SQL: the sink runs a DuckDB query on a schedule; no send key:
  --sql SQL             one DuckDB SELECT reading public http(s) URLs, inline,
                        @file or @- (stdin)
  --every DURATION      how often it runs: 1m, 20m, 1h, 300 (at least the
                        plan's minimum, 1 min)
  --offset DURATION     where in the interval it runs (every 20m offset 2m:
                        :02, :22, :42)
  --create-table        create the table from the query's columns when it does
                        not exist, in --format-version (default: the catalog's
                        default format version)
  --format-version {2,3}
                        with --create-table: the new table's Iceberg format
                        version; 3 for a geometry column or --partition, 2 for
                        --key. A query that does not fit it is refused before
                        the table is made
  --partition TRANSFORM(COLUMN)
                        with --create-table and format version 3: partition
                        the new table by time, repeatable: day(ts), hour(ts),
                        month(ts), year(ts) of a date or timestamp column
  --paused              create it paused (run it by hand with lhbox sink run)
  --engine {2.0,1.5}    the DuckDB the query runs on: 2.0 (DuckDB 2.0.0, the
                        default) or 1.5 (DuckDB 1.5.6); a sink keeps its
                        engine until you change it, whatever the default
                        becomes
  --mode {append,replace}
                        what each run does to the table: append its rows (the
                        default) or replace every row with them, in one
                        snapshot per run (a format-version 2 table; time
                        travel reads the versions the snapshot retention
                        keeps). Fixed at creation
  --min-rows N          with --mode replace: a run with fewer than N rows
                        (default 1) fails and leaves the table as it is, so an
                        empty source never empties it
  --replace-existing    with --mode replace on a table that already holds
                        rows: replace them at the first run
```

### lhbox sink list

```
usage: lhbox sink list [-h] [--catalog CATALOG] [--all]

options:
  -h, --help         show this help message and exit
  --catalog CATALOG  the catalog's warehouse_id (UUID) or its name; list both
                     with: lhbox catalog list
  --all              include deleted sinks
```

### lhbox sink get

```
usage: lhbox sink get [-h] SINK

positional arguments:
  SINK        the sink_id or the name

options:
  -h, --help  show this help message and exit
```

### lhbox sink delete

```
usage: lhbox sink delete [-h] [--force] [--yes] SINK

positional arguments:
  SINK                  the sink_id or the name

options:
  -h, --help            show this help message and exit
  --force, --discard-pending
                        delete even if batches are waiting for a roll (they
                        are discarded)
  --yes                 do not ask: the deletion cannot be undone. Asked on a
                        terminal; without one (or with --json) --yes will be
                        required from the next minor version of lhbox (a
                        warning today)
```

### lhbox sink send

```
usage: lhbox sink send [-h] [--send-key-file FILE] [--send-key SEND_KEY]
                       [--batch-id ID]
                       [SINK] FILE

The send route, POST /v1/sinks/{id}: the key from --send-key-file (what `sink
create --save` wrote), --send-key or LHBOX_SEND_KEY; without one, this
machine's own key (a token given the sink with `lhbox token create --send`, or
write on its catalog); the batch id from --batch-id or a fresh UUID. The same
batch id again answers duplicate without storing twice. 202 accepted; 400 not
an array; 413 over 16 MiB.

positional arguments:
  SINK                  the sink_id (omit it when --send-key-file names one; a
                        name works with your own key)
  FILE                  a file holding a JSON array of objects, or - for stdin

options:
  -h, --help            show this help message and exit
  --send-key-file FILE  the file `sink create --save` wrote
  --send-key SEND_KEY   the send key itself (prefer --send-key-file or
                        LHBOX_SEND_KEY)
  --batch-id ID         the Idempotency-Key: 1-128 chars of A-Z a-z 0-9 . _ :
                        - (default: a UUID)
```

### lhbox sink device-token

```
usage: lhbox sink device-token [-h] [--save FILE] [--show-key] [--revoke] SINK

POST /v1/sinks/{id}/device-token: the URL a device that sets no header posts
to. Shown once: --save FILE (0600) or --show-key. Counts against the plan's
device_sinks when the sink had no route.

positional arguments:
  SINK         the sink_id or the name

options:
  -h, --help   show this help message and exit
  --save FILE  write the device URL and token to FILE (mode 0600)
  --show-key   print the device URL once
  --revoke     remove the device route instead (DELETE)
```

### lhbox sink mapping

```
usage: lhbox sink mapping [-h] [--preset NAME] [--param NAME=VALUE]
                          [--mapping FILE] [--clear]
                          SINK

GET /v1/sinks/{id}?include_mapping=true, or PATCH /v1/sinks/{id} {mapping}. A
mapping turns what a device sends into the table's rows at each roll: rename,
drop, constants, a timestamp, and melt (one wide record into many long rows
with unit conversions). Validated against the table.

positional arguments:
  SINK                the sink_id or the name

options:
  -h, --help          show this help message and exit
  --preset NAME       a mapping shipped with the platform: ecowitt, ispindel,
                      ttn (The Things Stack webhooks), sensor-community
  --param NAME=VALUE  a preset's parameter, e.g. WS=ws-01 (repeatable)
  --mapping FILE      a mapping document (JSON)
  --clear             remove the mapping
```

### lhbox sink test

```
usage: lhbox sink test [-h] [--content-type TYPE] [--limit LIMIT] SINK FILE

POST /v1/sinks/{id}/test. The content type follows the file's extension (.csv,
.ndjson, .json) and defaults to form-urlencoded (what an Ecowitt gateway
posts); --content-type overrides it.

positional arguments:
  SINK                 the sink_id or the name
  FILE                 a captured body, or - for stdin

options:
  -h, --help           show this help message and exit
  --content-type TYPE  the body's media type
  --limit LIMIT        rows to print (default 20)
```

### lhbox sink preview

```
usage: lhbox sink preview [-h] [--catalog CATALOG] --sql SQL
                          [--table NS.TABLE] [--limit LIMIT]
                          [--engine {2.0,1.5}] [--format-version {2,3}]

POST /v1/sql/preview. The query runs on LakehouseBox's executor: public
http(s) URLs only (read_json, read_csv, read_parquet, read_text, FROM
'https://...'), one SELECT. Errors name the column, the host or the budget,
with a remedy.

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list; default: your only
                        catalog (with several, name one)
  --sql SQL             the SELECT, inline, @file or @- (stdin)
  --table NS.TABLE      also check that the rows fit this table
  --limit LIMIT         rows to print (default 20); the preview itself carries
                        at most 50 rows and 50,000 characters per value, and
                        says when it cut (truncated)
  --engine {2.0,1.5}    the DuckDB to preview on: 2.0 (DuckDB 2.0.0, the
                        default) or 1.5 (DuckDB 1.5.6)
  --format-version {2,3}
                        without --table: check the query against the format
                        version --create-table would use (default: the
                        catalog's default); a geometry under 2 is listed as a
                        problem
```

### lhbox sink run

```
usage: lhbox sink run [-h] [--wait] [--timeout TIMEOUT] SINK

positional arguments:
  SINK               the sink_id or the name

options:
  -h, --help         show this help message and exit
  --wait             wait until the run is committed, empty or failed
  --timeout TIMEOUT  seconds --wait waits (default 300)
```

### lhbox sink runs

```
usage: lhbox sink runs [-h] [--limit LIMIT] SINK

positional arguments:
  SINK           the sink_id or the name

options:
  -h, --help     show this help message and exit
  --limit LIMIT  runs to show (default 20, at most 500)
```

### lhbox sink pause

```
usage: lhbox sink pause [-h] SINK

positional arguments:
  SINK        the sink_id or the name

options:
  -h, --help  show this help message and exit
```

### lhbox sink resume

```
usage: lhbox sink resume [-h] SINK

positional arguments:
  SINK        the sink_id or the name

options:
  -h, --help  show this help message and exit
```

### lhbox sink connection

```
usage: lhbox sink connection [-h] {create,list,replace,delete} ...

A connection is a named credential of one catalog, sent only to URLs that
start with its https:// prefix; the SQL names the URL, never the credential.
Values come from @FILE, @- (stdin) or an environment variable (--value-env),
never inline, and no command shows them again. A person can also add one on
the account page (the catalog's Sinks tab).

positional arguments:
  {create,list,replace,delete}
    create              a new connection: NAME, --url-prefix, one of --bearer
                        / --header NAME / --basic, and --value @FILE, --value-
                        env VAR or a hidden prompt
    list                a catalog's connections (never their values)
    replace             replace a connection's value (a rotated key)
    delete              delete a connection; SQL sinks whose query names its
                        prefix are paused

options:
  -h, --help            show this help message and exit
```

### lhbox sink connection create

```
usage: lhbox sink connection create [-h] [--catalog CATALOG] --url-prefix URL
                                    [--bearer] [--header HEADER] [--basic]
                                    [--value @FILE] [--value-env VAR]
                                    NAME

POST /v1/sql/connections. --basic takes user:password as its value.

positional arguments:
  NAME               the connection's name (lower case letters, digits, _)

options:
  -h, --help         show this help message and exit
  --catalog CATALOG  the catalog's warehouse_id (UUID) or its name; list both
                     with: lhbox catalog list
  --url-prefix URL   https://host/path/ : the credential is sent only to URLs
                     that start with it (ends in /)
  --bearer           send Authorization: Bearer <value>
  --header HEADER    send the value in this header, e.g. X-API-Key
  --basic            send Authorization: Basic, the value being user:password
  --value @FILE      the credential: @FILE or @- (stdin); never inline
  --value-env VAR    read the credential from this environment variable (an
                     agent that holds the key never writes it to a file); with
                     neither, a terminal asks, hidden
```

### lhbox sink connection list

```
usage: lhbox sink connection list [-h] [--catalog CATALOG]

options:
  -h, --help         show this help message and exit
  --catalog CATALOG  the catalog's warehouse_id (UUID) or its name; list both
                     with: lhbox catalog list
```

### lhbox sink connection replace

```
usage: lhbox sink connection replace [-h] [--catalog CATALOG] [--value @FILE]
                                     [--value-env VAR] [--header HEADER]
                                     NAME

positional arguments:
  NAME               the connection's name

options:
  -h, --help         show this help message and exit
  --catalog CATALOG  the catalog's warehouse_id (UUID) or its name; list both
                     with: lhbox catalog list
  --value @FILE      the new credential: @FILE or @- (stdin)
  --value-env VAR    read it from this environment variable
  --header HEADER    a new header name (kind header only)
```

### lhbox sink connection delete

```
usage: lhbox sink connection delete [-h] [--catalog CATALOG] [--yes] NAME

positional arguments:
  NAME               the connection's name

options:
  -h, --help         show this help message and exit
  --catalog CATALOG  the catalog's warehouse_id (UUID) or its name; list both
                     with: lhbox catalog list
  --yes              do not ask: deleting a connection cannot be undone. Asked
                     on a terminal; without one (or with --json) --yes will be
                     required from the next minor version of lhbox (a warning
                     today)
```

### lhbox sink dedupe

```
usage: lhbox sink dedupe [-h] [--wait] [--timeout SECONDS] [--yes] SINK

positional arguments:
  SINK               the sink_id or the name

options:
  -h, --help         show this help message and exit
  --wait             wait until the roller has done it, and print what it
                     removed
  --timeout SECONDS  how long --wait waits (default 1800)
  --yes              do not ask: a dedupe removes the older rows of every key
                     cannot be undone. Asked on a terminal; without one (or
                     with --json) --yes will be required from the next minor
                     version of lhbox (a warning today)
```

### lhbox sink update

```
usage: lhbox sink update [-h] [--sql SQL] [--every DURATION]
                         [--offset DURATION] [--engine {2.0,1.5}]
                         [--min-rows N] [--force] [--key COL[,COL] | --no-key]
                         SINK

positional arguments:
  SINK                the sink_id or the name

options:
  -h, --help          show this help message and exit
  --sql SQL           the new SELECT, inline, @file or @- (stdin)
  --every DURATION    the new interval
  --offset DURATION   the new offset
  --engine {2.0,1.5}  move the sink to this DuckDB: 2.0 (DuckDB 2.0.0) or 1.5
                      (DuckDB 1.5.6); its query is previewed on it against the
                      table first
  --min-rows N        a replace sink's guard: a run with fewer than N rows
                      fails and leaves the table as it is
  --force             accept a new query or engine whose preview returns 0
                      rows although the sink's recent runs had rows (refused
                      otherwise: preview_empty)
  --key COL[,COL]     from the next roll, replace the rows that share these
                      columns instead of appending them again (a format-
                      version 2 table)
  --no-key            drop the key: each roll appends again
```

### lhbox index

```
usage: lhbox index [-h] {create,list,get,delete} ...

positional arguments:
  {create,list,get,delete}
    create              a new index rule on an existing table
    list                the index rules of every catalog you can see (or of
                        one)
    get                 one index rule by id or name: its last poll, what it
                        indexed, its lag
    delete              stop indexing (the table, its rows and the files stay)

options:
  -h, --help            show this help message and exit
```

### lhbox index create

```
usage: lhbox index create [-h] [--catalog CATALOG] --name NAME
                          [--prefix PREFIX] --table NS.TABLE --extractors JSON
                          [--rename EXTRACTED=COLUMN] [--poll-seconds N]
                          [--ordered-keys] [--roll-seconds N]
                          [--inactivity-seconds N]

POST /v1/indexes. Extractors, a JSON list (inline or @file) from: "basic"
(bucket, object_key, filename, size_bytes, etag, content_type, last_modified,
indexed_at, uploader), "image" (width, height, exif_taken_at), {"type":
"filename_time", "column": …, "pattern": "(\\d{14})", "format":
"%%Y%%m%%d%%H%%M%%S", "tz": "Europe/Madrid", "fallback": "last_modified"},
{"type": "path_parts", "pattern": "^camera/(?P<camera_id>[^/]+)/"}, {"type":
"constant", "values": {…}}.

options:
  -h, --help            show this help message and exit
  --catalog CATALOG     the catalog's warehouse_id (UUID) or its name; list
                        both with: lhbox catalog list; default: your only
                        catalog (with several, name one)
  --name NAME           the rule's name (lowercase letter, then lowercase,
                        digits, _)
  --prefix PREFIX       the prefix of the blob bucket to watch, e.g. camera/
                        (default: all of it)
  --table NS.TABLE      the table the rows go to; it must exist
  --extractors JSON     the extractor list, inline JSON or @file
  --rename EXTRACTED=COLUMN
                        store an extracted column under the table's name
                        (repeatable)
  --poll-seconds N      how often the prefix is listed (default 60, floor 30)
  --ordered-keys        the keys sort by time (dated folders such as
                        camera/2026/11/06/): each poll lists from the newest
                        folder indexed on, not the whole prefix; a file
                        uploaded into an older folder is then never indexed
  --roll-seconds N      commit the found files every N seconds (default 300,
                        floor 60)
  --inactivity-seconds N
                        also commit once nothing new was found for N seconds
```

### lhbox index list

```
usage: lhbox index list [-h] [--catalog CATALOG]

options:
  -h, --help         show this help message and exit
  --catalog CATALOG  the catalog's warehouse_id (UUID) or its name; list both
                     with: lhbox catalog list
```

### lhbox index get

```
usage: lhbox index get [-h] INDEX

positional arguments:
  INDEX       the index_id or the name

options:
  -h, --help  show this help message and exit
```

### lhbox index delete

```
usage: lhbox index delete [-h] [--yes] INDEX

positional arguments:
  INDEX       the index_id or the name

options:
  -h, --help  show this help message and exit
  --yes       do not ask: the deletion cannot be undone. Asked on a terminal;
              without one (or with --json) --yes will be required from the
              next minor version of lhbox (a warning today)
```

### lhbox mcp

```
usage: lhbox mcp [-h] {install,headers,run} ...

positional arguments:
  {install,headers,run}
    install             write the mcpServers entry into a host's
                        configuration; the key comes from LHBOX_API_KEY or the
                        credentials file and is never printed nor put on a
                        command line
    headers             for programs: print {"Authorization": "Bearer <key>"}
                        (LHBOX_API_KEY, else the saved login), the
                        headersHelper Claude Code runs for the entry `mcp
                        install` writes; refused on a terminal without --show-
                        key
    run                 start the MCP server over stdio with the saved key in
                        its environment (for a host configured by hand:
                        command lhbox, args mcp run); until the package is on
                        PyPI it runs from a checkout (--from
                        <checkout>/platform/mcp): most hosts want `lhbox mcp
                        install`, the hosted server, with nothing to run here

options:
  -h, --help            show this help message and exit
```

### lhbox mcp install

```
usage: lhbox mcp install [-h] [--client {claude-code,claude-desktop}]
                         [--transport {http,stdio}] [--mcp-url MCP_URL]
                         [--url URL] [--from SOURCE] [--name NAME]
                         [--scope {local,user,project}] [--print]

Claude Code (default): adds the remote server {"type": "http", "url":
"https://mcp.lakehousebox.com/mcp", "headersHelper": "<this lhbox> --url <API>
--profile <profile> mcp headers"} with `claude mcp add-json` when `claude` is
on PATH, else prints the entry and the command to run. The entry holds no key:
Claude Code runs the helper when it connects, which reads LHBOX_API_KEY from
Claude Code's environment, else the saved login, so a key given only with
--api-key is refused. Claude Desktop: its config file takes only local
servers, so the hosted server is a custom connector added in the app (Settings
> Connectors > Add custom connector, signed in with OAuth); this prints those
steps and writes nothing. --transport stdio (developers: a checkout's
platform/mcp against a local stack) writes a local server instead, only when
uv is installed and SOURCE resolves, else nothing is written: for Claude Code
{"command": "<this lhbox>", "args": ["--url", API, "--profile", PROFILE,
"mcp", "run", "--from", SOURCE]} (no key in it); for Claude Desktop
{"command": "uvx", "args": ["--from", SOURCE, "lakehousebox-mcp"], "env":
{LHBOX_URL, LHBOX_API_KEY}}, merged into its claude_desktop_config.json (other
servers kept) after a .bak-<timestamp> copy, both written 0600. The key comes
from the credentials file (or --api-key / LHBOX_API_KEY); the five tools only
read, so a token's key with read grants confines the server to those catalogs
(and to public ones) (docs/mcp.md).

options:
  -h, --help            show this help message and exit
  --client {claude-code,claude-desktop}
                        which host's configuration to write (default: claude-
                        code)
  --transport {http,stdio}
                        http (default): the hosted server, nothing runs
                        locally; stdio: a local server started with uvx from a
                        checkout, for developers
  --mcp-url MCP_URL     the remote server (default: mcp.<domain>/mcp next to
                        the API's api.<domain>)
  --url URL             API base URL the server will talk to and whose saved
                        key is used (default: the global --url / LHBOX_URL,
                        i.e. https://api.lakehousebox.com)
  --from SOURCE         developers: what uvx runs the local server from,
                        normally a checkout's platform/mcp directory (default:
                        the checkout this CLI runs from; the package
                        lakehousebox-mcp is not published). Checked first: a
                        source that cannot start the server is refused and
                        nothing is written
  --name NAME           the server's name in the host (default: lhbox)
  --scope {local,user,project}
                        Claude Code only: user (default; every project on this
                        machine), local (this project, this machine), or
                        project (.mcp.json, shared through version control: it
                        holds no key, but names this machine's lhbox)
  --print               print the mcpServers snippet instead of writing
                        anything (for another host's configuration; a Claude
                        Desktop entry's key is MASKED: put the real one in
                        from the credentials file)
```

### lhbox mcp headers

```
usage: lhbox mcp headers [-h] [--url URL] [--show-key]

options:
  -h, --help  show this help message and exit
  --url URL   API base URL whose saved key is used (default: the global --url
              / LHBOX_URL, i.e. https://api.lakehousebox.com)
  --show-key  print it on a terminal too
```

### lhbox mcp run

```
usage: lhbox mcp run [-h] [--url URL] [--from SOURCE] ...

positional arguments:
  -- SERVER_ARGS  passed to the server, e.g. -- --list-tools

options:
  -h, --help      show this help message and exit
  --url URL       API base URL (default: the global --url / LHBOX_URL, i.e.
                  https://api.lakehousebox.com)
  --from SOURCE   developers: what uvx runs the local server from, normally a
                  checkout's platform/mcp directory (default: the checkout
                  this CLI runs from; the package lakehousebox-mcp is not
                  published). Checked first: a source that cannot start the
                  server is refused and nothing is written
```

---
HTML version: https://lakehousebox.com/docs/cli/ · every page: https://lakehousebox.com/llms.txt · everything in one file: https://lakehousebox.com/llms-full.txt

Docs feedback: if this page was wrong, out of date or missing something you needed, tell the person you work for and, if they agree, report it: `lhbox bug --title "docs: cli: <what was wrong>"` prints a filled-in issue link, or open https://github.com/lakehousebox/lakehousebox/issues/new. The tracker is public: never include a key, a secret or the person's data.
