@zilfu/cli is a thin wrapper around the Zilfu REST API — it calls the same endpoints through @zilfu/sdk and authenticates with the same personal access token as the API and the MCP server. One token covers all three.
The CLI is read-only today. It lists spaces, connected accounts, and posts; it does not create, update, or delete anything. For writes, drive the REST API directly or point an assistant at the MCP server.
Install
npm install -g @zilfu/cli
That puts a zilfu binary on your PATH. Node.js 18.17 or newer is required.
For a one-off run, skip the install:
npx @zilfu/cli health
health needs no credentials, so it's the fastest way to confirm the binary works before you log in.
Authenticate
There are three ways in. All of them end with a token written to the same config file, and all of them validate that token against /session before writing it — a bad token is never stored.
Browser consent screen
zilfu login --web
This opens a Zilfu consent screen in your default browser and waits for you to approve. Approval hands a one-time code back to a loopback callback on 127.0.0.1 (PKCE-protected), which the CLI exchanges for the token. The authorize URL is printed before the browser opens, so on a headless machine you can copy it to a browser that can reach the printed port. The CLI gives up after five minutes.
You must already be signed in to Zilfu in that browser. If you aren't, the consent screen bounces you to the sign-in page and signing in lands you on /posts — it does not return you to the consent screen. Sign in first, or re-open the printed authorize URL afterwards.
Paste a token
zilfu login
Create a token at Settings → API tokens first, then paste it at the Zilfu API token: prompt. The input is hidden and there are no mask characters at all — not even asterisks — so the terminal looks frozen while you type or paste. It isn't.
Non-interactive
zilfu login --token "$TOKEN"
Use this in CI or any non-TTY shell, where plain zilfu login fails with No token provided. A token passed on the command line ends up in shell history and in process listings — see Scripting for the safer unattended pattern.
On success, all three print:
Logged in as Ada Lovelace <ada@example.com>.
Credentials stored at /Users/you/.config/zilfu/config.json
What approving actually grants
The consent screen mints a personal access token with full access to your account — the exact same kind of token you'd create by hand at Settings → API tokens, listed alongside the others and revocable the same way. There is no scope picker: no read-only CLI token, no single-space CLI token. Tokens do not expire.
Where credentials are stored
| Platform | Path |
|---|---|
| macOS, Linux | $XDG_CONFIG_HOME/zilfu/config.json, defaulting to ~/.config/zilfu/config.json |
| Windows | %APPDATA%\zilfu\config.json |
The file holds a token and an optional baseUrl, and is written with mode 600. macOS uses the XDG path, not ~/Library/Application Support.
A config file that can't be read or isn't valid JSON is silently treated as empty. That surfaces later as No API token found. rather than a parse error, so if a logged-in machine suddenly claims it has no token, check the file itself.
Environment variables
| Variable | Effect |
|---|---|
ZILFU_TOKEN |
The API token to authenticate with |
ZILFU_BASE_URL |
The API base URL to call |
Both are read by every command that calls the API — health, whoami, and the three list commands — and both are overridden by the equivalent flag. login is a partial exception: it honours ZILFU_BASE_URL but ignores ZILFU_TOKEN (pass --token or paste at the prompt). logout reads neither.
Token precedence:
| Order | Source |
|---|---|
| 1 | --token <token> |
| 2 | ZILFU_TOKEN |
| 3 | Stored config file |
| — | No default. Without one of the above, every command that calls the API fails — only health, login, and logout run without a token |
Base-URL precedence:
| Order | Source |
|---|---|
| 1 | --base-url <url> |
| 2 | ZILFU_BASE_URL |
| 3 | Stored config file |
| 4 | https://zilfu.app/api |
loginis the exception: it reads the base URL from--base-urlorZILFU_BASE_URLonly, never from the stored config, and it rewrites the config file wholesale. So re-running plainzilfu loginafter logging into a non-default host silently drops the storedbaseUrland reverts you tohttps://zilfu.app/api. Pass--base-urlon every login to a non-default host.
Log out
zilfu logout
Prints Logged out., or No stored credentials found. if there was nothing to delete. Either way it exits 0.
That command deletes the local config file and nothing else. It does not revoke anything server-side — the token stays valid indefinitely, because Zilfu tokens don't expire — and it leaves ZILFU_TOKEN in your environment untouched. To actually kill a token, delete it at Settings → API tokens.
Commands
| Command | What it does |
|---|---|
zilfu login |
Authenticate and store a token |
zilfu logout |
Delete the stored credentials |
zilfu whoami |
Show the authenticated user (id, name, email) |
zilfu health |
Check the API is up — the only API call that works without a token |
zilfu spaces list |
List the spaces you can access, with their ids |
zilfu accounts list --space <id> |
List the connected accounts in a space |
zilfu posts list --space <id> |
List posts in a space |
That's the whole command surface, plus citty's --help and --version. Every value is passed as a flag — there are no positional arguments; list is a subcommand, not an argument.
A sensible first run after logging in:
zilfu whoami
zilfu spaces list
spaces list is the one that matters: --space has no default and no environment variable, so the id it prints is what you pass to every other list command.
Flags, output columns, and the caveats on each command are on Commands. Exit codes, --json shapes, and error handling are on Scripting.