The command-line tool retrieves from a folder of your files and answers from what it
found, naming each file it read. Every command and every number below was run against
armore 1.0.10 before it was written here.
This page uses the CLI, which is a separate download from the installer — see install and first run if you only have the app.
Inference is compiled in or it is not, and this tells you which — before you spend time on anything else.
armore models capabilities --json
{
"backends": {
"llama_cpp": true
},
"cli_version": "1.0.10",
"command": "models.capabilities",
"commands_requiring_inference": [
"ask",
"extract",
"rag"
],
"inference": true,
"note": "on-device inference is compiled in — ask/extract/rag will run with a --model.",
"ok": true
}
"inference": true is the line that matters. The three commands listed
under commands_requiring_inference are the ones that will refuse to work
without a model file you supply yourself.
No model is bundled with the CLI. Point it at a directory of .gguf files
and it will tell you what it found, with the size and content address of each:
armore models list --dir ~/models --json
{
"command": "models.list",
"count": 2,
"dir": "~/models",
"models": [
{
"blake3": "b244ec2a2bbaf54c643239aa2894fd71f680252525969dfc81fa35a768e096a0",
"bytes": 36806944,
"name": "bge-small.gguf",
"path": "~/models/bge-small.gguf"
},
{
"blake3": "22a0829973f064026cacce3584596851a79048c7c847bbf05ceecc5093e6ce1d",
"bytes": 8540775840,
"name": "llama-3.1-8b-q8.gguf",
"path": "~/models/llama-3.1-8b-q8.gguf"
}
],
"ok": true
}
--model, and a separate embedding model for
--embed-model — armore ask --help states that
--rag “Needs --embed-model”. They are not
interchangeable: the embedding model here is 36.8 MB and the chat model is 8.5 GB.
Retrieval on its own needs only the small embedding model, so it is the cheapest way
to find out whether your folder even contains the answer. All four flags shown are
required by armore rag search.
armore rag search --json \
--query "when does the Hartley contract renew" \
--dir ~/Documents/contracts \
--model ~/models/bge-small.gguf
{
"command": "rag.search",
"ok": true,
"query": "when does the Hartley contract renew",
"results": [
{ "blake3": "0c2498735b51fe33…", "score": 0.9127750992774963, "source": "hartley-contract.txt" },
{ "blake3": "114f8548f8d62c92…", "score": 0.5476594567298889, "source": "office-move.txt" },
{ "blake3": "77738ff47cba4c3c…", "score": 0.46502354741096497, "source": "q3-numbers.txt" }
],
"top_k": 3
}
Three files, ranked, in 0.17 s once the model was in the page cache.
--top-k defaults to 3. Each result carries the BLAKE3 of the file it
scored, which is what lets you tie an answer back to a specific byte sequence on your
disk rather than to a filename someone could have swapped.
top.txt, report.md,
subdir/nested.txt and binary.bin, the returned sources were
exactly report.md and top.txt. Subfolders are not
walked and non-text files are skipped — so a folder whose documents live one
level down retrieves nothing, and the command still exits successfully. Any UTF-8
text file counts, not just .txt. Flatten the folder, or point
--dir at the subfolder itself.
Same folder, now with a chat model as well. --model and the prompt are
required; --rag and --embed-model are what make it read
your files instead of answering from the model's own training.
armore ask --json \
--model ~/models/llama-3.1-8b-q8.gguf \
--rag ~/Documents/contracts \
--embed-model ~/models/bge-small.gguf \
--top-k 2 \
"When does the Hartley contract renew, and how much notice does termination need?"
{
"command": "ask",
"grounded_on": [
{ "blake3": "0c2498735b51fe33…", "score": 0.9541630148887634, "source": "hartley-contract.txt" },
{ "blake3": "114f8548f8d62c92…", "score": 0.5768898129463196, "source": "office-move.txt" }
],
"model": "~/models/llama-3.1-8b-q8.gguf",
"ok": true,
"reply": "According to [hartley-contract.txt], the Hartley contract renews on 14 March 2027,
and termination requires a notice period of 90 days.…"
}
29.22 s wall clock end to end, including loading the 8.5 GB chat
model from disk. grounded_on is the part worth reading: it names which
files were actually put in front of the model, so a wrong answer is traceable to a
wrong source rather than a mystery.
model field in the JSON above, and
armore models list --dir … --json gives you its BLAKE3.
armore extract pulls structured fields out of one document and will sign
a receipt over the run. Two flags are required together, by design:
--receipt-key-file and --seal-store.
# a 32-byte Ed25519 key, 64 hex chars, in a file you control
openssl rand -hex 32 > receipt.key && chmod 600 receipt.key
armore extract --json \
--model ~/models/llama-3.1-8b-q8.gguf \
--template contract \
--receipt-key-file receipt.key \
--seal-store ./sealed \
~/Documents/contracts/hartley-contract.txt
{
"command": "extract",
"input_blake3": "0c2498735b51fe335734ccb1fe2d8ddf14b2379f055dee382cd27165a14b9908",
"ok": true,
"output": {
"effective_date": "2027-03-14",
"fees": "none",
"jurisdiction": null,
"parties": [ "Hartley Inc." ],
"term": "90 days"
},
"receipt": { "…": "author, model_blake3, input_blake3, ran_offline, signature" },
"sealed_output": { "blake3": "cd04cf5dba148854…", "file_id": "extract-output-cd04cf5dba148854" },
"template": "contract"
}
--receipt-key <hex> exits with: “a literal key on the command line
… puts it in ps output and your shell history”. Use
--receipt-key-file <path>, --receipt-key-file - for
stdin, or the ARMORE_RECEIPT_KEY environment variable.
--seal-store is required alongside it for the same reason — so the
directory that ends up holding key material is one you named, not a default nobody
showed you.
Now verify it, with no network and nothing of ours involved:
# re-run the command above with `> extract.json` to keep the output, then: jq '.receipt' extract.json > receipt.json armore audit receipt --json receipt.json { "author": "da04f0a8303f1110c4c63d9ad6cacd5560267ff977456e723cb2439310feb238", "command": "audit.receipt", "input_blake3": "0c2498735b51fe335734ccb1fe2d8ddf14b2379f055dee382cd27165a14b9908", "model_blake3": "22a0829973f064026cacce3584596851a79048c7c847bbf05ceecc5093e6ce1d", "ran_offline": true, "valid": true }
input_blake3 in the receipt and re-running the same command flips
"valid" to false. Worth doing once on your own machine —
a verifier that cannot say no is decoration.
input_blake3 is only meaningful if you can show it is your
document. Hash it yourself:
armore files hash --json ~/Documents/contracts/hartley-contract.txt
{
"blake3": "0c2498735b51fe335734ccb1fe2d8ddf14b2379f055dee382cd27165a14b9908",
"command": "files.hash",
"ok": true
}
Same value as input_blake3 in the receipt, and as the
blake3 of hartley-contract.txt in Steps 3 and 4. Those four
figures agreeing is the whole point: the receipt is about this file, this model, and
nothing else.
Nothing above stores your documents, but the CLI does keep state of its own. Read it rather than take our word for it:
armore privacy list --json
{
"command": "privacy.list",
"present": [
{ "key": "file-store", "irreversible": true, "size_bytes": 1252,
"what": "Encrypted file store, INCLUDING epoch.key — the 32-byte symmetric key that seals every file in it." },
{ "key": "identity", "irreversible": true, "size_bytes": 3878,
"what": "This device's identity: member-root seed, enrollment record, device certificate." },
{ "key": "serve", "irreversible": false, "size_bytes": 219 },
{ "key": "mcp", "irreversible": false, "size_bytes": 0 }
],
"absent": [ { "key": "memory" }, { "key": "msg" } ],
"roots": [ "~/.config/armore" ],
"total_bytes": 5349
}
armore privacy export copies all of it into a directory you choose;
armore privacy purge deletes it.
privacy list reports the CLI's own stores
under ~/.config/armore only. Clearing one leaves the other untouched, in
both directions.