AppXen CLI
Command-line tool for managing your AppXen MCP Gateway and RAG Engine. Ingest documents, search your knowledge base, and manage sources — all from the terminal.
Install
curl -sSL https://appxen.ai/install.sh | sh
Or install from PyPI:
pip install appxen
For local development:
pip install -e products/appxen-cli/
Requires Python 3.10+.
Quick Start
1. Log in with your API key
Get an API key from your appliance's console (https://) under API Keys, then:
appxen login
By default the CLI talks to https://localhost — the appliance's own Caddy origin,
which fronts the console, gateway, and orchestrator behind a self-signed certificate.
Running the CLI on the box itself, add --insecure (or set APPXEN_INSECURE=1) to skip
TLS verification for that cert:
appxen login --insecure
Running the CLI from elsewhere? Point it at your appliance's own host:
appxen login --endpoint https://your-appliance-host
If the appliance has a domain name (set with sudo appxenctl set-domain <your-domain>),
always pass --endpoint https://<your-domain>, on the box itself too. Caddy then serves
only that domain, with its managed certificate, so the default https://localhost fails
the TLS handshake and --insecure does not help. The default works only on a box without
a domain, whose Caddy listens on :443 with the self-signed certificate.
appxen login --endpoint https://appliance.example.com
In a terminal you are prompted for your key (input is hidden). In a script, pipe it: when stdin is not a terminal, the key is the first line of stdin, and nothing is echoed:
printf '%s\n' "$APPXEN_KEY" | appxen login --endpoint https://your-appliance-host
The CLI checks the key's format (axgw_*),
then verifies it with the appliance on an authenticated route. Only a key the appliance
accepted is saved: a refused key, or one that could not be verified (the appliance is
unreachable, its certificate is refused), is not saved and the command exits 1. For an
appliance that cannot be reached yet, appxen login --no-verify saves the key unchecked.
Your key is stored in ~/.config/appxen/config.toml, mode 0600, in a directory of mode 0700.
Against the appliance's self-signed certificate (an appliance without a domain) the CLI refuses
the connection and says so. --insecure (or APPXEN_INSECURE=1) skips the verification: use it
for your own appliance, over a network path you trust, until it has a domain
(sudo appxenctl set-domain <name>); never against a host you do not control. Every command
that skips the verification says so, in one warning line on stderr.
2. Check connectivity
appxen status
CLI: 0.2.7
Endpoint: https://localhost
Service: mcp-gateway-pro
Gateway: 0.1.0
Status: Connected
RAG: 12 sources, 347 chunks
3. Ingest documents
Single file:
appxen ingest ./report.pdf
Entire directory (recursive by default):
appxen ingest ./docs/
The CLI uploads each file, then polls until ingestion completes (chunking, embedding, indexing). You'll see progress bars for both stages:
Found 14 file(s) to ingest.
Uploading architecture.pdf ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14/14
Waiting for 14 file(s) to process...
Processing architecture.pdf ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14/14
OK 14 file(s) ingested successfully.
4. Search
appxen search "how does authentication work"
╭──────────── #1 auth-design.md (score: 0.847) ────────────╮
│ Authentication uses passkeys (WebAuthn) as the primary │
│ method with magic link email as a fallback... │
╰──────────────────────── chunk: a1b2c3d4-e5f ─────────────╯
╭──────────── #2 api-keys.md (score: 0.723) ────────────────╮
│ API keys use the format axgw_{mode}_{version}_{hex} and │
│ are stored as SHA-256 hashes in DynamoDB... │
╰──────────────────────── chunk: f6g7h8i9-j0k ─────────────╯
Commands
appxen login
Save your API key to the local config.
appxen login --insecure # default profile, on a box without a domain
appxen login --profile staging # named profile
appxen login --endpoint https://your-appliance-host # remote appliance
appxen login --endpoint https://appliance.example.com # appliance with a domain (on the box too)
| Option | Short | Default | Description |
|---|---|---|---|
--profile |
-p |
the global appxen --profile, then APPXEN_PROFILE, then default |
Profile to save to |
--endpoint |
-e |
https://localhost |
Appliance origin — gateway and orchestrator paths are derived from it |
--insecure |
false |
Skip TLS verification, for the appliance's self-signed certificate (also APPXEN_INSECURE=1) |
|
--verify / --no-verify |
--verify |
Verify the key with the appliance before saving it; --no-verify saves it unchecked |
Exit status of appxen login: 0 the key was saved (verified first, unless --no-verify);
1 the key was refused, could not be verified or was not given, and nothing was saved;
2 usage: a malformed endpoint, or one that carries a user name or password (checked before
the key is read), an unknown option.
appxen status
Check connectivity and the API key, show gateway info and RAG stats. The key is verified on an
authenticated route (/health answers anyone).
appxen status
appxen status --json # machine-readable output
Exit status of appxen status: 0 connected, with a key the appliance accepted;
1 the gateway is unreachable, or the key was refused (401, 403) or could not be verified (the
authenticated route failed another way);
2 usage: a malformed endpoint or API key, an unknown option.
On 1 nothing is printed on stdout.
appxen ingest <path>
Upload files to the RAG knowledge base. Accepts a single file or a directory.
appxen ingest ./report.pdf # single file
appxen ingest ./docs/ # directory (recursive)
appxen ingest ./docs/ --glob "*.md" # only markdown files
appxen ingest ./docs/ --no-recursive # top-level only
appxen ingest ./docs/ --no-wait # upload and exit (don't wait)
appxen ingest ./docs/ --json # JSON output: waits, then prints the final state
appxen ingest ./docs/ --json --no-wait # JSON output of the accepted state (status: queued)
appxen ingest ./scans/ --timeout 1800 # wait up to 30 minutes for each file
| Option | Short | Default | Description |
|---|---|---|---|
--glob |
-g |
* |
Glob pattern for filtering files in a directory |
--recursive / --no-recursive |
--recursive |
Recurse into subdirectories | |
--wait / --no-wait |
--wait |
Wait until every file is ingested, with --json too; --no-wait returns once the uploads are accepted (--poll / --no-poll are the same option) |
|
--timeout |
300 |
Seconds to wait for EACH file before reporting it as not ready (the appliance goes on ingesting it) | |
--json |
false |
Output raw JSON: one array, an entry per file |
With --json, stdout is the array and nothing else; the progress goes to stderr. An entry is
{"file", "source_id", "filename", "status", "chunk_count"}; a file that failed has "error"
with the reason. A file that is not ready when its --timeout passes is a failure, and so is one
whose source disappears during the wait (someone deleted it): the command goes on waiting for the
other files and its document, or its summary, holds every file with its source id. Only a refused
API key ends the command at once: no other file can succeed.
Supported file types:
| Category | Extensions |
|---|---|
| Documents | .pdf .docx .html .htm .md .txt .csv .json .xml .yaml .yml |
| Code | .py .js .ts .jsx .tsx .css .sql .sh .rs .go .java .c .cpp .h .rb .php |
| Images (OCR) | .png .jpg .jpeg .tiff .bmp |
Unsupported file types are silently skipped when scanning directories. The maximum file size is 100 MB.
Exit status of appxen ingest: 0 every file was ingested (with --no-wait: accepted);
1 nothing could be ingested at all: no supported file, not logged in, the API key was refused;
2 some or all files failed (upload refused, ingestion failed, not ready within --timeout, the
source disappeared during the wait): the other files are still waited for, and with --json the
document says which failed. Also a usage error (an unknown option, a --timeout that is not a
positive number, a malformed endpoint or API key), which prints no document;
130 interrupted (Ctrl-C): stderr says how many files were uploaded and each one's last status,
and the appliance goes on ingesting what it accepted.
2 for files that failed is the meaning it has had since 0.2.4 ("partial failure"). A script
tells it from a usage error by the output: with --json, files that failed come with the
document, each failed entry carrying "error"; a usage error prints nothing on stdout.
appxen search <query>
Semantic search over your knowledge base.
appxen search "database schema design"
appxen search "error handling" --top-k 10
appxen search "deployment steps" --json
| Option | Short | Default | Description |
|---|---|---|---|
--top-k |
-k |
5 |
Number of results to return |
--json |
false |
Output raw JSON |
appxen sources
List and manage knowledge base sources.
appxen sources # list all sources
appxen sources --status ready # filter by status
appxen sources --search "report" # filter by filename
appxen sources --limit 50 # more results
appxen sources --json # JSON output
| Option | Short | Default | Description |
|---|---|---|---|
--status |
-s |
Filter by status (ready, processing, queued, failed) |
|
--search |
-q |
Filter by filename | |
--limit |
-n |
20 |
Results per page |
--offset |
0 |
Pagination offset | |
--json |
false |
Output raw JSON |
Sources (47 total)
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓
┃ ID ┃ Filename ┃ Status ┃ Chunks ┃ Created ┃
┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━┩
│ a1b2c3d4-e5f │ architecture.pdf │ ready │ 23 │ 2026-02-08T14:30:00 │
│ f6g7h8i9-j0k │ api-reference.md │ ready │ 8 │ 2026-02-08T14:28:00 │
│ ... │ ... │ ... │ ... │ ... │
└──────────────┴───────────────────────┴────────┴────────┴─────────────────────┘
appxen sources delete <source_id>
Delete a source and all its chunks.
appxen sources delete a1b2c3d4-e5f6-7890-abcd-1234567890ab
appxen sources delete a1b2c3d4-e5f6-7890-abcd-1234567890ab --yes # skip confirmation
| Option | Short | Description |
|---|---|---|
--yes |
-y |
Skip the confirmation prompt |
appxen stats
Show knowledge base statistics.
appxen stats
appxen stats --json
╭──────────────────────── RAG Engine Stats ─────────────────────────╮
│ Sources: 47 │
│ Chunks: 1,203 │
│ Storage: 24.7 MB │
╰───────────────────────────────────────────────────────────────────╯
appxen workflow
Workflows of the Agent Orchestrator: list, compile <file>, create <file>, get <id>,
delete <id>, run <id>, status <execution>, output <execution>, stop <execution>.
appxen workflow create ./digest.md --name digest
appxen workflow run wf_123 -i topic=security # starts it and waits for the end
appxen workflow run wf_123 --no-poll --json # starts it and returns the execution id
appxen workflow status exec_456 --poll --timeout 1800
appxen workflow output exec_456 --save result.md
appxen workflow run wf_123 --json --save result.md # the document on stdout AND the output in the file
appxen workflow stop exec_456
workflow run (unless --no-poll) and workflow status --poll wait until the execution
ends: completed, failed, rejected, timed_out or aborted.
| Option | Default | Description |
|---|---|---|
--timeout |
600 |
Seconds to wait before giving up; the execution keeps running on the appliance |
Exit status of appxen workflow run: 0 the execution completed (with --no-poll: it was
started);
1 it could not be started, it ended failed, rejected, timed_out or aborted (its error
is printed), or it was still running when --timeout passed;
2 usage: --save together with --no-poll, an unknown option, a malformed endpoint or API key;
130 interrupted (Ctrl-C) during the wait: stderr says what status the execution was last seen
in, and it keeps running on the appliance.
Exit status of appxen workflow status: 0 the status was read (with --poll: the execution
completed);
1 the execution could not be read, or with --poll it ended failed, rejected, timed_out
or aborted, or it was still running when --timeout passed;
2 usage: an unknown option, a --timeout that is not a positive number, a malformed endpoint
or API key;
130 interrupted (Ctrl-C) during --poll: the execution keeps running on the appliance.
An execution held at an approval gate (awaiting_approval) is approved or rejected in the
console. With --json, stdout is one document, {"execution": ..., "steps": [...]}: the final
state, or at the deadline the state as last seen; nothing after an interrupt.
--save FILE (workflow run, workflow output) writes the output to a file, with --json
too (the "saved" line is then on stderr). It is refused with --no-poll (exit 2: there is no
output to save yet), and with no output to write it is an error, never an empty file.
workflow stop on an execution that had already ended changes nothing and says so
(had already ended (status: completed); nothing was stopped), exit 0.
Configuration
Config file
Stored at ~/.config/appxen/config.toml:
[default]
api_key = "axgw_live_k1_..."
endpoint = "https://localhost"
insecure = true
[staging]
api_key = "axgw_test_k1_..."
endpoint = "https://staging-appliance.example.com"
insecure = false
Switch profiles with --profile or APPXEN_PROFILE:
appxen --profile staging sources
appxen -p staging search "test query"
APPXEN_PROFILE=staging appxen stats
appxen --profile staging login # saves to [staging]
Which profile is used, nearest first: the command's own --profile (only login has one),
the global appxen --profile, APPXEN_PROFILE, then default. insecure skips TLS
verification only as the boolean true. The file is replaced atomically on every save.
Environment variables
Environment variables override the config file:
| Variable | Description |
|---|---|
APPXEN_API_KEY |
API key. When set, NO profile is read: the endpoint is APPXEN_ENDPOINT (default https://localhost) |
APPXEN_ENDPOINT |
Appliance origin, used with APPXEN_API_KEY |
APPXEN_INSECURE |
1, true, yes or on skips TLS verification, for the appliance's self-signed certificate; any other value (0, false, empty) keeps it on. Overrides the profile's insecure |
APPXEN_PROFILE |
Profile to use when --profile is not given (default: default) |
The endpoint is the appliance's origin (https://<host>[:port]). One that carries a user name
or password (https://user:pass@host) is refused. Plain http:// to a host other than this
machine works, with a warning on stderr that the API key travels unencrypted.
# One-off command against the appliance box itself
APPXEN_API_KEY=axgw_test_k1_abc123 APPXEN_INSECURE=1 appxen status
JSON Output
Commands that read from the appliance take --json for scripting and piping. Stdout is then
exactly one JSON document, written as it is (never wrapped to the terminal width, text outside
ASCII escaped); progress, warnings and errors go to stderr; a command that fails prints no
document, except a wait that ended (see appxen workflow) and ingest (the array says which
file failed).
# Get source IDs for all ready sources
appxen sources --json | jq '.sources[] | select(.status == "ready") | .source_id'
# Count chunks across all sources
appxen stats --json | jq '.chunk_count'
# Ingest and capture results
appxen ingest ./docs/ --json | jq '.[] | {file, status, source_id}'
Exit status
| Code | Meaning |
|---|---|
0 |
Success |
1 |
The command failed: not logged in, the appliance is unreachable or refused the API key, an execution did not complete |
2 |
Usage: an unknown option, a malformed endpoint (https://host:abc, https://user:pass@host), an API key that cannot be sent, --save with --no-poll. For ingest also: some or all files failed (see appxen ingest) |
130 |
Ctrl-C during a wait (ingest, workflow run, workflow status --poll) |
Errors and warnings go to stderr, each message on one unwrapped line, and never contain the API key. A certificate that cannot be verified, a refused key and a malformed endpoint each say what to do next.
appxen status (without --json) and appxen login also look for a newer CLI at
https://appxen.ai/releases/latest.json (3 seconds at most; a failure is silent, a success is
remembered for a day).
Examples
Ingest a project's documentation
appxen ingest ./docs/ --glob "*.md" --recursive
Ingest only PDFs from a folder
appxen ingest ./reports/ --glob "*.pdf"
Search and get raw JSON for processing
appxen search "error handling patterns" --top-k 20 --json
Delete all failed sources
appxen sources --status failed --json \
| jq -r '.sources[].source_id' \
| xargs -I{} appxen sources delete {} --yes
Use in CI/CD
export APPXEN_API_KEY=${{ secrets.APPXEN_API_KEY }}
export APPXEN_ENDPOINT=https://your-appliance-host # your appliance's origin
# Sync docs on every deploy
appxen ingest ./docs/ --glob "*.md"
Metadata
Release files for appxen 0.2.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| appxen-0.2.7.tar.gz | 84.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| appxen-0.2.7-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 125.7 kB
Release files / appxen-0.2.7.tar.gz
| Download URL | appxen-0.2.7.tar.gz |
|---|---|
| Size | 84.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d10729873538b062b32b3fd667e81532192962cdb2c597e5ea8075a5dd0d71ff
|
|
BLAKE2b-256 checksum How to use checksums |
dd9c4a9758cf87584084630aa96c3ec21abda80fd2cef13ca3df1f60ece8d701
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.12
|
Release files / appxen-0.2.7-py3-none-any.whl
| Download URL | appxen-0.2.7-py3-none-any.whl |
|---|---|
| Size | 41.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
470dd99f0fa71044458a93bd2bf6a501a168947fbfed059ccacb2219c0fdbde1
|
|
BLAKE2b-256 checksum How to use checksums |
589d58f20988956b8649de51daf9877c74c83880066d953d8198ba2c189c7649
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.12
|