CLI
polign is the command-line client for a running polign_db
server. It speaks the same HTTP/JSON API as everything else, so every command here is a curl
you didn't have to type. This page is the full reference; the
Get started guide is the walkthrough.
Getting the binary
polign ships in every release archive next to
polign-server, and Homebrew installs it alongside the other binaries.
One command talks to your bucket rather than the server.
collections claim writes the claim object to your s3:// or
gcs:// bucket with your own credentials. Everything else is pure HTTP.
Talking to a server
polign [-url http://localhost:23000] [-key plgn_…] <command> …
| Flag | Environment | What it does |
|---|---|---|
-url | $POLIGN_URL | Server base URL. Defaults to http://localhost:23000, the server's HTTP listener. |
-key | $POLIGN_API_KEY | API key, sent as Authorization: Bearer. The collection commands need one; the data commands never do. |
-version | — | Print the version and exit. |
Global flags go before the command, per-command flags after it:
export POLIGN_URL=https://db.example.com:23000
polign search articles -text "getting started" -k 5
# or per invocation
polign -url https://db.example.com:23000 search articles -text "getting started"
Data commands
These are the everyday operations. Batch put and batch get stay in the client libraries.
No API key is required, and the data plane
ignores one if you send it. Collections are auto-created on the first put,
inferring their dimension from that vector, so there is nothing to create first.
polign put <collection> <id> -values 0.1,0.2,… [-meta k=v]…
polign get <collection> <id>
polign delete <collection> <id>
polign list <collection> [-limit n] [-offset n]
polign search <collection> [-values …] [-text "…"] [-k 5] [-filter '{"k":"v"}']
put
An upsert: writing the same id twice replaces it. Prints the id on success.
polign put articles guide-1 \ -values @embedding.json \ -meta title="Getting started" -meta lang=en
-meta is repeatable and takes key=value, and it always sends
the value as a string — a CLI limitation, not a data-model rule: the API stores typed
scalars (string, number, boolean; see the
HTTP API reference), so to write {"score": 0.85}
use raw HTTP or the Python SDK. Metadata comes back with every hit (the CLI renders it in
string form) and drives filters.
get, delete, list
polign get articles guide-1 # pretty-printed JSON: id, values, metadata polign delete articles guide-1 # prints "deleted: true"; an unknown id is a 404, exit 1 polign list articles -limit 100 -offset 0
list prints an ID/METADATA table followed by the
collection's total, so it doubles as a "how much is in here?" check. Omit -limit
to take the server's page size (100; the server caps -limit at 1,000). Filtered
listing is HTTP-only for now — the CLI's list has no -filter flag.
On a cold-first fleet, listing a resource the node keeps no in-memory index for returns 501:
its records live in segments, so search or get by id instead. (Since v0.3.1, listings on a
node that does hold an index reflect acknowledged writes and deletes immediately.)
search
One command covers all three search modes, and what you pass decides which you get:
# by meaning — vector search polign search articles -values @query.json -k 5 # by keyword — BM25 polign search articles -text "getting started" -k 5 # both — hybrid, fused server-side polign search articles -values @query.json -text "getting started" -k 5 # narrowed by metadata polign search articles -values @query.json -k 5 \ -filter '{"lang":"en","score":{"$gte":0.5}}'
| Flag | What it does |
|---|---|
-values | The query vector. Omit it (and set -text) for a pure keyword search. |
-text | BM25 query. Alone = keyword search; with -values = hybrid. |
-k | How many hits to return. Default 5. |
-ef | How hard the search tries: higher finds slightly better matches, a bit slower. 0 = server default. |
-filter | Metadata predicate as JSON. Bare values are equality (AND across keys); per-key operators ($eq, $ne, $in, $gt/$gte/$lt/$lte, $exists) and $and/$or/$not express more. Filtering happens during the search, not after it, so asking for 5 results still returns the 5 best matching results. |
-cold | Serve straight from object-storage segments instead of the in-memory index. Needs a segment store. |
-nprobe | How many index regions a cold search checks; 0 = server default. Higher = more accurate, more object-store reads. |
-rescore | On compressed collections: how many candidates get re-checked exactly; bigger = more accurate, slower. 0 = default, a negative value skips the re-check entirely (fastest, least accurate). |
Results print as an ID/DISTANCE/SCORE/METADATA
table. distance is the vector metric (smaller = closer);
score is relevance (larger = better) and shows - on a pure vector
search, where there is no relevance score to report. Keyword and hybrid search read the
object-store segment index, so they need a server started with a store. See the
install guide.
Passing vectors
Anywhere -values appears, three input forms work:
| Form | Example |
|---|---|
| Inline list | -values 0.1,0.2,0.3 — commas or spaces |
| File | -values @embedding.json — a JSON array |
| Stdin | -values - — a JSON array on stdin |
Stdin is the one that makes the CLI composable: pipe your embedding model straight into a search.
./embed "how do I get started?" | polign search articles -values - -k 5
Collection commands
These manage the collection registry on a server started with -byo-store, where
each collection lives in a bucket you own. They need an API key, so pass
-key or set $POLIGN_API_KEY. The
operations guide walks the whole lifecycle; this is the command
surface.
polign setup -bucket s3://bkt/prefix # print the IAM recipe for that bucket polign collections create <name> -bucket s3://bkt/prefix [-role arn:…] polign collections claim <name> <token> # write the claim token, then verify polign collections get <name> # full JSON: status, backend, capabilities polign collections list # NAME / STATUS / BUCKET table polign collections verify <name> # re-probe the bucket, refresh status polign collections delete <name> # unregister; bucket contents untouched
create flag | What it does |
|---|---|
-bucket | Required. The store URI the collection lives in, e.g. s3://myco-prod-vectors/polign. |
-role | IAM role ARN the server assumes for this bucket, for cross-account backends. |
-region | Bucket region, when it differs from the fleet's. |
-gcs-sa | GCS service account to impersonate, for gcs:// backends. |
create prints a claim token once and the exact
claim command to run next. claim is the ownership proof: it writes the
token to .polign/claim in the bucket using your credentials, not the
server's, and then asks the server to verify. That's why this one command needs bucket access
from wherever you run it, and why a typo'd bucket name fails at claim time instead of silently
collecting your vectors.
polign collections create prod-docs -bucket s3://myco-prod-vectors/polign claim token (shown once): plgnclaim_8d1f0c2ab34e… polign collections claim prod-docs plgnclaim_8d1f0c2ab34e… prod-docs: active
Output & exit codes
| Command | Output |
|---|---|
list, search, collections list | Aligned columns on stdout, readable and fine for awk. |
get, collections get, setup | Indented JSON, exactly as the server sent it. Pipe to jq. |
put, delete, claim, verify | One short confirmation line. |
Errors go to stderr and exit 1; running with no command (or bad flags) prints
usage and exits 2. An unrecognized command or a missing required flag is a plain
error: stderr, exit 1. Server errors are reported with the HTTP status and the
server's own message, so a failing script says what the API said.
The CLI covers one vector at a time on the write path. For bulk loads, reach for the
Python SDK's put_many, which takes up to 5,000
vectors per request instead of one process per vector.
Same operations, different doors: the Python SDK, the gRPC API, and raw HTTP/JSON (curl examples in the HTTP API reference).