Reading works in the Free edition and needs no licence. Changing data (set, update, delete, export, import and changing users) needs Pro on that computer, as in the app.
Set it up
The command comes with Firetool from the download page; there's nothing else to install. In Firetool, choose Help → Use Firetool from the terminal…: it shows where the command is on your computer and the one command that puts it on your PATH, with a button to copy it.
| System | Where it is | To use it anywhere |
|---|---|---|
| Windows | bin\firetool.cmd in Firetool's program folder (usually %LOCALAPPDATA%\Programs\Firetool) | Run the PowerShell line Help → Use Firetool from the terminal… gives you once, then open a new terminal. |
| macOS | Inside the app: Firetool.app/Contents/Resources/bin/firetool | sudo ln -sf "/Applications/Firetool.app/Contents/Resources/bin/firetool" /usr/local/bin/firetool |
| Linux | firetool (the .deb and the .tar.gz install it) | Nothing to do. |
Check it works:
firetool version
firetool help
The command uses the accounts you added in Firetool, and it works while Firetool is open.
Read data
# The accounts added in Firetool, and the projects of one
firetool accounts
firetool projects
# A project's databases, its top-level collections, and those under a document
firetool databases shop-production
firetool collections shop-production
firetool collections shop-production users/alice
# One document
firetool get shop-production users/alice
# Documents that match, sorted, at most 20
firetool query shop-production orders --where "status == paid" --where "total >= 100" --order "total desc" --limit 20
# Every collection called orders (a collection group)
firetool query shop-production orders --group --where "status == refunded"
# How many documents match (one read per 1,000 counted)
firetool count shop-production orders --where "status == paid"
# Authentication users
firetool users list shop-production --limit 50
firetool users get shop-production ada@example.com
--where takes "<field> <op> <value>", where op is one of == != < <= > >= in not-in array-contains array-contains-any starts-with. Numbers, true / false and null are read as such; put text that looks like a number in quotes ("pin == '007'"). Lists are a,b,c or ["a","b"]. query reads 50 documents unless you give --limit (up to 10,000).
Output for scripts
The command prints tables for people. Add --json for scripts; query also takes --format jsonl (one document per line) or --format csv.
firetool query shop-production orders --where "status == paid" --json
firetool query shop-production orders --format csv > orders.csv
JSON keeps exact values in Firetool's form, such as {"$timestamp": "2026-01-01T00:00:00Z"}, so nothing loses its type.
Change data (Pro)
# Write a whole document, or change only some fields of one that exists
firetool set shop-staging settings/app --data '{"maintenance": false, "version": 3}'
firetool update shop-staging users/alice --data '{"plan": "pro"}'
firetool update shop-staging users/alice --file alice.json
# Delete one document
firetool delete shop-staging users/test-user --yes
# Export what matches to a file (.json, .jsonl or .csv), and import it again
firetool export shop-production orders --where "status == paid" --out paid-orders.jsonl
firetool import shop-staging orders --file paid-orders.jsonl --skip-existing
# Users
firetool users disable shop-production uid1 uid2
firetool users enable shop-production uid1
firetool users delete shop-staging uid3 --yes
--data and --file use Firetool's JSON form, so types are kept ({"at": {"$timestamp": "2026-01-01T00:00:00Z"}}). import reads what export writes; --skip-existing keeps documents that are already there. Add --dry-run to set, update, delete, import or a user change to see what would be written, without writing anything.
Every change goes through the same checks as the window: your role, read-only and production projects, protected collections, and the audit log, where it's marked as coming from the terminal.
Confirmations
Where the window would ask you something, the terminal can't, so the command stops with exit code 4 and tells you which option answers it. Run it again with that option:
--confirm <project-id>: the project is marked production and the change needs its project ID typed.--reason "…": your policy asks for a reason. It goes in the audit log.--yes: deleting, or reading more than 10,000 documents.
firetool delete shop-production users/old-test --yes --confirm shop-production --reason "Removing a test account"
Options
| Option | What it does |
|---|---|
--account <id or name> | Which account to use, when several can open the project. |
--db <database> | A database other than (default). |
--json | Machine-readable output. |
--format table|json|jsonl|csv | For query, how to print; for export, the file's format (otherwise from the file name). |
--where, --order | Filters and sorting; both can be repeated. --order "total desc". |
--limit <n> | At most n documents (query: 50 by default) or users (100 by default). |
--group | Every collection with that name (query, count, export). |
--data, --file | The fields for set and update; the documents for import. |
--out <path> | The file export writes. |
--skip-existing | Import: keep documents that already exist. |
--confirm, --reason, --yes | Answers to the window's questions (above). |
--dry-run | Show what would be written, and write nothing. |
--key-file <path> | A service account key for this run only (below). |
Exit codes
| Code | Meaning |
|---|---|
| 0 | Done. |
| 1 | Failed (the message says why). |
| 2 | Wrong usage: a missing argument or an option it doesn't know. |
| 3 | Needs Firetool Pro. |
| 4 | Needs a confirmation: run it again with the option it names. |
| 5 | Refused by the rules: your role, a read-only project or a protected collection. |
CI and servers
On a build server there are no Firetool accounts, so give the command a service account key for the run:
--key-file key.json, or the file's path inFIRETOOL_KEY_FILE, or- the key's JSON itself in
FIRETOOL_KEY, which suits a CI secret.
The key is used for that run only: it's used by default, never saved and never printed. On Linux without a display, the command runs headless. Reading needs no licence; changing data needs Pro on that machine.
Firetool has to be installed on the runner from the Linux .deb or .tar.gz you download from firetool.in. For example, keep the installer where your runner can reach it, then:
name: Check orders
on:
schedule:
- cron: "0 6 * * *"
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# The Firetool installer, downloaded from firetool.in and kept in the repository
- name: Install Firetool
run: sudo apt-get install -y ./ci/Firetool-linux-x64.deb
- name: Count paid orders
env:
FIRETOOL_KEY: ${{ secrets.FIRETOOL_KEY }}
run: |
firetool count shop-staging orders --where "status == paid"
firetool query shop-staging orders --where "total < 0" --json > bad-orders.json
Give the service account only the roles it needs, such as Cloud Datastore Viewer for a job that only reads. The project rules you set in Firetool on that machine still apply.
Questions
Does the command need Firetool to be closed?
No. It works while Firetool is open, with the same accounts.
Do changes from the terminal go in the audit log?
Yes. Every change goes through the same checkpoint as the window and is recorded in the audit log, marked as coming from the terminal.
Is the key I give in CI kept anywhere?
No. A key from --key-file, FIRETOOL_KEY_FILE or FIRETOOL_KEY is used for that run only. It isn't saved with Firetool's accounts and isn't printed.
Related
- Firestore desktop client: how Firetool connects to Google.
- Roles and production safety: the rules the command follows.
- Export: exports from the window.
- Scheduled exports: exports that run by themselves.