mirror of
https://github.com/actualbudget/actual.git
synced 2026-11-09 10:45:04 -06:00
* [AI] Support reading any setting from a file with a _FILE environment variable Adds the `_FILE` environment-variable suffix convention used by Docker secrets and Kubernetes file-mounted secrets. Setting `<VAR>_FILE` to a path makes Actual read that setting's value from the file, so secrets can be mounted rather than passed through the environment. This covers both config systems: every env-backed setting in the sync server's convict schema, and every env-backed setting in the CLI. On the sync-server side the override is applied after `loadFile()` and before `validate()`, using `set()`. convict re-imports the environment at the end of both `load()` and `loadFile()`, so an override applied any earlier would be clobbered by the plain environment variable. A `_FILE` variable pointing at an unreadable file is a hard error rather than a silent fallback, so a secret that failed to mount stops startup instead of leaving the server running with an empty value. Refs #5233, #7686. Supersedes the approach in #5235, which applied the override before `loadFile()` and so had the precedence backwards. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * [AI] Address review feedback on _FILE support - Treat a `_FILE` variable that is set but empty as an unreadable path rather than as unset. Previously it fell back to the plain environment variable, which contradicted the documented behaviour and hid a failed mount. This applied to both the CLI and the sync server; regression tests added for both. - Read `_FILE` variables by their literal name in the CLI's resolution chain, so the `_FILE` variables are greppable in the config module and the precedence order is visible in the chain itself. This also keeps `utils.ts` free of filesystem access. - List the remaining supported variables in the CLI environment variable table. - Rename a test to describe what it asserts. - Narrow the release note, which overstated the scope: `_FILE` applies to settings that already have an environment variable binding. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * [AI] Limit _FILE support to secrets Following the convention set by Docker's official images, where only secrets get a _FILE variant, this narrows the scope to: - sync server: ACTUAL_OPENID_CLIENT_SECRET, ACTUAL_GITHUB_TOKEN - CLI: ACTUAL_PASSWORD, ACTUAL_SESSION_TOKEN, ACTUAL_ENCRYPTION_PASSWORD ACTUAL_HTTPS_KEY and ACTUAL_HTTPS_CERT already accept a path, so they get no _FILE variant. Both sides now name the `_FILE` variables literally at the point of use, so they are greppable and the precedence order is visible in the call itself. On the sync server this replaces the generic schema walk, which had to mirror convict's internal rule for telling a setting from a group; the schema no longer has to be hoisted to a named const, so load-config.js is reduced to an import and the override loop. Also documents ACTUAL_ENCRYPTION_PASSWORD, which the CLI supports but neither environment variable table listed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * [AI] Use a descriptive name for the temp directory in the config file env tests Addresses the remaining review nitpick: `dir` did not say what the directory held. Renamed in both test files for consistency. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * [AI] Trim comments and documentation per review - Shorten the readFileEnv doc comments in both packages, and drop the note in load-config.js about which variables are deliberately excluded, keeping the two lines explaining why the override runs between loadFile and validate. - Reduce the server documentation to a short note and move it to the top of the page. - Point at the runtime's own secret support (e.g. Docker secrets) in the CLI security notes rather than suggesting a hand-made file. - Drop the per-variable OpenID section, which repeated the note above it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * [AI] Simplify the wording of the _FILE precedence note Replaces the sentence in the server docs, the CLI docs and the CLI README with a shorter statement of precedence, and drops the note about stopping on an unreadable file, which can be inferred. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
217 lines
9.8 KiB
Markdown
217 lines
9.8 KiB
Markdown
# @actual-app/cli
|
|
|
|
Command-line interface for [Actual Budget](https://actualbudget.org). Query and modify your budget data from the terminal — accounts, transactions, categories, payees, rules, schedules, and more.
|
|
|
|
> **Note:** This CLI connects to a running [Actual sync server](https://actualbudget.org/docs/install/). It does not operate on local budget files directly.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
npm install -g @actual-app/cli
|
|
```
|
|
|
|
Requires Node.js >= 22.
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# Set connection details
|
|
export ACTUAL_SERVER_URL=http://localhost:5006
|
|
export ACTUAL_PASSWORD=your-password
|
|
export ACTUAL_SYNC_ID=your-sync-id # Found in Settings → Advanced → Sync ID
|
|
|
|
# List your accounts
|
|
actual accounts list
|
|
|
|
# Check a balance
|
|
actual accounts balance <account-id>
|
|
|
|
# View this month's budget
|
|
actual budgets month 2026-03
|
|
```
|
|
|
|
## Configuration
|
|
|
|
Configuration is resolved in this order (highest priority first):
|
|
|
|
1. **CLI flags** (`--server-url`, `--password`, etc.)
|
|
2. **Environment variables**
|
|
3. **Config file** (via [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig))
|
|
4. **Defaults** (`dataDir` defaults to `~/.actual-cli/data`)
|
|
|
|
### Environment Variables
|
|
|
|
| Variable | Description |
|
|
| ---------------------------- | ----------------------------------------------------- |
|
|
| `ACTUAL_SERVER_URL` | URL of the Actual sync server (required) |
|
|
| `ACTUAL_PASSWORD` | Server password (required unless using token) |
|
|
| `ACTUAL_SESSION_TOKEN` | Session token (alternative to password) |
|
|
| `ACTUAL_SYNC_ID` | Budget Sync ID (required for most commands) |
|
|
| `ACTUAL_DATA_DIR` | Local directory for cached budget data |
|
|
| `ACTUAL_CACHE_TTL` | Cache TTL in seconds (default: 60) |
|
|
| `ACTUAL_LOCK_TIMEOUT` | Budget-dir lock wait timeout in seconds (default: 10) |
|
|
| `ACTUAL_NO_LOCK` | Set to `1` to disable budget-dir locking |
|
|
| `ACTUAL_ENCRYPTION_PASSWORD` | Password for end-to-end encrypted budget files |
|
|
|
|
The three secrets — `ACTUAL_PASSWORD`, `ACTUAL_SESSION_TOKEN` and `ACTUAL_ENCRYPTION_PASSWORD` — can be read from a file instead, by adding `_FILE` to the variable name (for example `ACTUAL_PASSWORD_FILE=/run/secrets/actual-password`). `_FILE`-suffixed environment variables take priority over regular ones.
|
|
|
|
### Config File
|
|
|
|
The CLI uses [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig) for configuration. The config file can be anywhere between the current working directory and your home directory.
|
|
|
|
You can create a config file in any of these formats:
|
|
|
|
- `.actualrc` (JSON or YAML)
|
|
- `.actualrc.json`, `.actualrc.yaml`, `.actualrc.yml`
|
|
- `actual.config.json`, `actual.config.yaml`, `actual.config.yml`
|
|
- An `"actual"` key in your `package.json`
|
|
|
|
You can instead store your configuration in the `actual` subdirectory of the global configuration directory (e.g. `~/.config/actual/` on Linux) in any of these formats:
|
|
|
|
- `config` (JSON or YAML)
|
|
- `config.json`
|
|
- `config.yaml`
|
|
- `config.yml`
|
|
|
|
Example `.actualrc.json`:
|
|
|
|
```json
|
|
{
|
|
"serverUrl": "http://localhost:5006",
|
|
"password": "your-password",
|
|
"syncId": "1cfdbb80-6274-49bf-b0c2-737235a4c81f",
|
|
"cacheTtl": 60,
|
|
"lockTimeout": 10,
|
|
"noLock": false
|
|
}
|
|
```
|
|
|
|
**Security:** Avoid storing plaintext passwords in config files (including the `password` key above). If these files do contain passwords, set restrictive permissions (e.g. 600 on Linux), and, if they are in a git repo, add them to `.gitignore`. Prefer environment variables such as `ACTUAL_PASSWORD` or `ACTUAL_SESSION_TOKEN`, or use a session token in config instead of a password. Better still, use your runtime's built-in support for secrets (e.g. Docker secrets) and point `ACTUAL_PASSWORD_FILE` or `ACTUAL_SESSION_TOKEN_FILE` at the resulting file. See [Environment Variables](#environment-variables) for details.
|
|
|
|
### Global Flags
|
|
|
|
| Flag | Description |
|
|
| ------------------------- | ----------------------------------------------- |
|
|
| `--server-url <url>` | Server URL |
|
|
| `--password <pw>` | Server password |
|
|
| `--session-token <token>` | Session token |
|
|
| `--sync-id <id>` | Budget Sync ID |
|
|
| `--data-dir <path>` | Data directory |
|
|
| `--cache-ttl <seconds>` | Cache TTL; `0` disables caching (default: 60) |
|
|
| `--refresh` | Force a sync on this call, ignoring the cache |
|
|
| `--no-cache` | Alias for `--refresh` |
|
|
| `--lock-timeout <secs>` | Lock wait timeout (default: 10) |
|
|
| `--no-lock` | Disable budget-dir locking (use with care) |
|
|
| `--format <format>` | Output format: `json` (default), `table`, `csv` |
|
|
| `--verbose` | Show informational messages |
|
|
|
|
## Commands
|
|
|
|
| Command | Description |
|
|
| ----------------- | ------------------------------ |
|
|
| `accounts` | Manage accounts |
|
|
| `budgets` | Manage budgets and allocations |
|
|
| `categories` | Manage categories |
|
|
| `category-groups` | Manage category groups |
|
|
| `transactions` | Manage transactions |
|
|
| `payees` | Manage payees |
|
|
| `tags` | Manage tags |
|
|
| `rules` | Manage transaction rules |
|
|
| `schedules` | Manage scheduled transactions |
|
|
| `query` | Run an ActualQL query |
|
|
| `server` | Server utilities and lookups |
|
|
| `sync` | Refresh or inspect local cache |
|
|
|
|
Run `actual <command> --help` for subcommands and options.
|
|
|
|
### Examples
|
|
|
|
```bash
|
|
# List all accounts (as a table; excludes closed by default)
|
|
actual accounts list [--include-closed] --format table
|
|
|
|
# Find an entity ID by name
|
|
actual server get-id --type accounts --name "Checking"
|
|
|
|
# Add a transaction (amount in integer cents: -2500 = -$25.00)
|
|
actual transactions add --account <id> \
|
|
--data '[{"date":"2026-03-14","amount":-2500,"payee_name":"Coffee Shop"}]'
|
|
|
|
# Export transactions to CSV
|
|
actual transactions list --account <id> \
|
|
--start 2026-01-01 --end 2026-12-31 --format csv > transactions.csv
|
|
|
|
# Set budget amount ($500 = 50000 cents)
|
|
actual budgets set-amount --month 2026-03 --category <id> --amount 50000
|
|
|
|
# Run an ActualQL query
|
|
actual query run --table transactions \
|
|
--select "date,amount,payee" --filter '{"amount":{"$lt":0}}' --limit 10
|
|
```
|
|
|
|
### Amount Convention
|
|
|
|
All monetary amounts are **integer cents** when passed as input (flags, JSON):
|
|
|
|
| CLI Value | Dollar Amount |
|
|
| --------- | ------------- |
|
|
| `5000` | $50.00 |
|
|
| `-12350` | -$123.50 |
|
|
|
|
**Output formatting:** Table (`--format table`) and CSV (`--format csv`) output automatically converts cent values to decimal (e.g. `1665.00` instead of `166500`). JSON output always returns raw cents for programmatic use.
|
|
|
|
### Tips & Common Pitfalls
|
|
|
|
- **Split transactions:** When summing or counting transactions, filter `"is_parent": false` to avoid double-counting. A split parent holds the total amount, and its children hold the individual parts — including both would count the total twice.
|
|
|
|
- **Rapid sequential requests:** The CLI caches the budget locally (see [Caching](#caching)), so read-heavy scripts no longer need a single-query workaround by default. For very chatty scripts, run `actual sync` once and then use a long `--cache-ttl` for reads:
|
|
|
|
```bash
|
|
actual sync
|
|
actual --cache-ttl 3600 query run ...
|
|
actual --cache-ttl 3600 accounts list
|
|
```
|
|
|
|
- **Uncategorized transactions:** `category.name` is `null` for transactions without a category. Account for this when filtering or grouping by category.
|
|
|
|
- **No date sub-fields in AQL:** `date.month`, `date.year`, etc. are not supported as query fields. To group by month, fetch raw transactions with a date range filter and aggregate locally in a script.
|
|
|
|
## Caching
|
|
|
|
The CLI keeps a local copy of your budget so repeated commands don't hit the sync server on every call. Within the TTL (default `60` seconds), read commands (`list`, `balance`, `query run`, …) reuse the cached budget without a network round-trip. Write commands (`add`, `update`, `set-amount`, …) always sync with the server before and after the write.
|
|
|
|
- `actual sync` — refresh the cache now.
|
|
- `actual sync --status` — show how stale the local cache is.
|
|
- `actual sync --clear` — delete the local cache; the next command re-downloads.
|
|
- `--refresh` (or `--no-cache`) — force a sync on a single call.
|
|
- `--cache-ttl <seconds>` — override the TTL for a single call (use `0` to disable caching).
|
|
|
|
### Concurrency
|
|
|
|
The CLI takes a shared lock for reads and an exclusive lock for writes on the per-budget cache directory. Many parallel reads are safe; writes serialize. If another CLI process is holding the lock, subsequent invocations wait up to `--lock-timeout` seconds (default `10`) before failing with an error. Pass `--no-lock` to opt out in trusted single-process setups.
|
|
|
|
## Running Locally (Development)
|
|
|
|
If you're working on the CLI within the monorepo:
|
|
|
|
```bash
|
|
# 1. Build the CLI
|
|
yarn build:cli
|
|
|
|
# 2. Start a local sync server (in a separate terminal)
|
|
yarn start:server-dev
|
|
|
|
# 3. Open http://localhost:5006 in your browser, create a budget,
|
|
# then find the Sync ID in Settings → Advanced → Sync ID
|
|
|
|
# 4. Run the CLI directly from the build output
|
|
ACTUAL_SERVER_URL=http://localhost:5006 \
|
|
ACTUAL_PASSWORD=your-password \
|
|
ACTUAL_SYNC_ID=your-sync-id \
|
|
node packages/cli/dist/cli.js accounts list
|
|
|
|
# Or use a shorthand alias for convenience
|
|
alias actual-dev="node $(pwd)/packages/cli/dist/cli.js"
|
|
actual-dev budgets list
|
|
```
|