16 · Configuration
alix works out of the box; the config file is for when you want to change key
bindings, point at a different decks directory, or tune the AI features. It
lives in the platform’s config directory (on Linux
~/.config/alix/config.toml); create it with alix config --init, and
inspect the active key bindings with alix config.
Key bindings
All keybindings live under [keys], one subtable per surface: [keys.review]
(the review screen), [keys.picker] (the deck picker), and [keys.browse]
(the browse overlay). Every action takes a list of keys (the first is shown in the
footer). To grade self-graded cards with j/k/l:
[keys.review]
failed = ["j"]
partly = ["k"]
passed = ["l"]
Keys are a single character ("j"), a special name ("space", "enter", "tab",
"esc", "backspace"), or either with a ctrl- prefix ("ctrl-s"). The
rebindable [keys.review] actions are failed, partly, passed, reveal, submit, skip,
remove (default ctrl-x), ask (default ?), context (default c, open
the card’s section drawer),
continue, restart (default r), quit, up/down
(defaults k/j) to move within a multiple-choice or key-point list (the arrow keys always work too),
and the tutor’s distill actions make_note (default ctrl-n) and make_card (default ctrl-d). While
you’re typing an answer (a reconstruct check), plain-character bindings are ignored so
they can’t shadow your input: use ctrl-/special keys for skip and
quit there. Pass a different file with --config <path>.
The picker’s navigation is [keys.picker] (up, down, open, back,
filter, mastered, plus depth to open the depth menu,
recognize/recall/reconstruct to pick within it, and cram to toggle its
tick-box, defaults v, 1/2/3, and c), the browse overlay has its own [keys.browse] bindings, and
the web server reads its default port from [serve]:
[keys.browse]
next = ["l", "n", "space"]
prev = ["h", "p"]
remove = ["x"]
quit = ["q", "esc", "ctrl-c"]
[serve]
port = 7777
# token = "..." # pairing token required on /api/*; --lan auto-generates one (printed, with a QR); a pinned one needs 16+ characters
audience = "adult" # or "kids", which frontend `/` serves, and the tutor's voice (see 15 · The web app)
[log]
max_bytes = 5242880 # cap for each of the current and rolled files
verbose = false # also record verbose targets such as HTTP timings
(Jump-to-first/last stays fixed at g/G, and the arrow keys always move.)
The server log is always on. [log] max_bytes bounds each of its two files and
must be positive. Card selection and content-free panic, AI, parser, and HTTP
failure records are written by default. [log] verbose = true adds HTTP
timings. For live debugging, --log http,select,error also
mirrors the named targets to stderr and enables verbose file records for that
run. It does not add card content, deck names, or request paths.
Review pacing
The [review] section tunes the FSRS scheduler shared by all three review
depths:
[review]
retention = 0.9 # FSRS target recall probability (0.70–0.99); higher = shorter intervals
recognize_retention = 0.85 # same, for the Recognize depth alone; recognition decays slower
retire_after = "1y" # a card rests once its Recall interval reaches this ("2w", "6m", "30d", or "never")
introduction_cooldown = "5m" # settle gap before a new card's first quiz ("90s", "10m", "1h"; "0" = none)
max_session = 10 # cards a single sitting serves (default 10)
new_cards_percent = 30 # new-card share of max_session; the rest are due cards (default 30)
retention is the recall probability FSRS schedules for; recognize_retention
is the same knob for the Recognize depth alone, laxer by default (0.85) because
recognition holds far longer than production. retire_after is when
a card retires (rests until alix reset); "never" keeps it in rotation forever.
introduction_cooldown is the settle gap between seeing a new card and its first
graded quiz, and the same floor keeps any just-seen card (a miss, a wrong
pick) from returning immediately, so one knob paces both. A bare number is
minutes; "0" disables the gap.
max_session is how many cards one sitting serves; new_cards_percent is the
new-card slice of that cap (so at the defaults, three new and seven due out of
ten). Whichever pool comes up short, the other fills the cap, so a fresh deck
serves ten new and a deck with nothing new serves ten due; a big backlog just
slows introductions proportionally. A workspace can override any of these keys
for its own decks in an alix.local.toml (see Workspaces).
The precedence for the cap is --session N on the launch > max_session > the
built-in 10; new_cards_percent has no launch flag.
Ready by a deadline
Two more [review] keys exist only in a workspace’s alix.local.toml, never
in the global config (which rejects both outright):
[review]
deadline = "2026-09-01" # a personal "ready by" date; the day itself counts
deadline_ramp = "14d" # how early the pre-deadline ramp starts ("2w"; "0" = cap only)
deadline is an ISO date (YYYY-MM-DD). deadline_ramp takes a bare number
of days, "<n>d", or "<n>w"; "0" caps intervals at the days left without
ramping retention early. Inside the window the target retention climbs
linearly toward a fixed 0.95 by the deadline day (deliberately not a
config key); see Scheduling for the full mechanics.
These keys are workspace-only: they take effect only in a directory with
an alix.toml. In a plain decks folder, or on a loose deck, they parse but do
nothing (no ramp, no picker readout, no doctor warning). alix workspace deadline refuses a non-workspace directory and points at alix workspace init.
The picker’s ready percent counts a deadline’s member decks as ready once mastered, or finished when they have no exam grounding. Mastery itself rests on the AI exam’s sampled questions, not a check of every card. Treat ready% as evidence toward readiness, not proof of it.
How deeply you drill is never configuration: it’s the session depth you
pick per review (the picker’s Depth… menu). See
Reveal & session depths. The old [review] depth config
key (and the per-deck [review.deck."<file>"] override), a dial that fixed
the drilling depth from config, is gone, not renamed; a config that still
sets either now fails to load.
Backends
By default all AI calls go through the Claude Code
CLI. You can switch to one of the other supported CLIs with backend in [ask]:
[ask]
backend = "claude" # default, Claude Code CLI
# backend = "gemini" # Google Gemini CLI
# backend = "codex" # OpenAI Codex CLI
# backend = "copilot" # GitHub Copilot CLI
Auth is each CLI’s own login: alix stores no API keys. Install whichever CLI you want to use and run its login command once.
Each backend is granted read-only tools only (file reading; web fetch where
the backend supports it). Codex runs under a network-blocking sandbox rather
than a tool allowlist, so it can read local source files but can’t fetch URLs:
a URL-based exam or a generate subcommand will refuse and tell you to use a local
file instead, or switch backends.
Run alix doctor --backends to send a quick test request to the configured
backend and confirm it’s installed, signed in, and responding. --all-backends
probes all four.
The multi-turn tutor works on every backend: Claude uses its native session
flags (--session-id / --resume) for efficient continuation; other backends
re-inline the accumulated Q&A transcript into each follow-up so the context
carries over (the prompt grows with the conversation rather than being resumed
efficiently).
The AI sections
Each AI feature has its own section, all reusing the [ask] command and permission
settings:
[ask]: the tutor:command(how to invoke the CLI),backend,permission_mode, the tool allowlist, amodeloverride,timeout_secs, aneffort,source_access(local source grounding; a workspace manifest may override it, see the tutor), andpreflight_threshold(warn and confirm before spending a large model call on a local source tree bigger than this many bytes;0proceeds silently).[generate]:alix deck generate’s drafting:model, the absolutetimeout_secs(3600), andidle_timeout_secs(300, or0to disable). The latter is a resetting inactivity limit for structured-event backends and a nonrenewing absolute fallback for unstructured backends. Other controls aremax_cards(100, a soft ceiling: exceeding warns, never truncates), defaultlanguageandaudience,card_style(mixed,plain,cloze, orauthored-choices),extra, apromptoverride, andreview. Per-run flags override the language, audience, and style defaults. The terminal shows calm live progress, but partial generated cards remain hidden until the result passes validation.[exam]: the AI exam:model,timeout_secs(300),num_questions(5),pass_threshold(1.0),strictness(balanced),extra,retry_cooldown_secs(3600;0disables the wait before re-sitting a failed trace exam; fact-deck exams never wait).[trace]: thegenerate deck --traceandworkspace generateplanning passes:modeldefaults to unset, which resolves to the backend’s strong model where it defines one (Claude:opus), andeffortdefaults to"high"(the build is correctness-critical and amortized); alsotimeout_secsandextra(extra guidance appended to the build prompt).[ai]:alix deck augment’s generation targets: amodeloverride,distractor_count(3),variant_count(4),keypoint_count(5), andtimeout_secs(300, sized for a whole-deck batch).
Decks directory and storage
By default alix looks for decks in ~/decks; set decks_dir to change it.
Shareable material and private user files default to that folder, one document
per initialized deck:
<decks_dir>/
├── augment/deck-<token>.json
└── .alix/
├── progress/deck-<token>.json
├── recent.json
└── sync.toml
.alix/progress/ is private, indispensable learning state: schedules, review
history, exam state, and the last writer. augment/ is
regenerable, shareable material: generated choices, notes, key points,
variants, and topologies. It stays beside the deck so sharing the deck can
carry its generated study material without carrying personal history. The
stable deck id (deck-<token>), not the Markdown filename, selects both
documents, so renaming a deck keeps their ownership stable.
.alix/sync.toml holds the served folder’s paired-sync identity. Alix mints
its root_id the first time that folder is served and preserves it on later
starts and moves. Copying the whole folder deliberately copies that identity.
If the copy is meant to become an independent library, delete only the copy’s
.alix/sync.toml before serving it; the first serve then mints a new identity.
Do not delete the file from an existing paired library merely to fix a phone:
the root check exists to stop progress from landing in a different library.
decks_dir chooses what bare alix serves; it does not relocate state for a
deck named elsewhere. Every deck uses its content root: the workspace root for
a workspace member, or the containing folder for a loose deck. Bare alix,
alix <dir>, stats, list, and reset therefore resolve the same colocated
.alix/ documents. Shareable augment/ and assets/ stay beside the decks.
Each document carries its owner ID, format version, and revision. Saves write a
sibling .json.tmp and atomically rename it into place. A process that can see
that its loaded revision is stale refuses to overwrite the newer document.
If the replacement commits but the final directory flush fails, Alix reports
the failure while retaining the committed revision in memory, so a later save
can retry instead of remaining stale forever.
This protects local overlapping writers; it cannot turn disconnected folder
synchronization into a transaction.
Alix is pre-1.0 and reads only the current version-1 per-deck documents. A
persisted-state format break is handled before installing that build: back up
the affected files, perform any one-time conversion outside production Alix, and
verify the result with alix doctor <folder>. Production does not contain
runtime compatibility branches or converters for superseded pre-1.0 layouts.
Backing up
Everything Alix stores is plain files in one folder, so a backup is a copy of
that folder. Use whatever you already use for folders: a cloud drive, git,
rsync, Time Machine, or cp -r. Alix manages no backup archives or
generations of its own: a general backup could only reproduce what those tools
already do, losslessly, over the same plain files. What it does keep is one
.bak safety net per overwrite: alix deck restore swaps a deck (file, review history,
augmentations) with the backups the last overwrite left behind. Keep an
independent copy of any study history you care about.
What Alix does guarantee is that its own writes cannot corrupt your files. Every state, deck, and manifest write goes to a sibling temporary file that is flushed to disk, atomically renamed over the target, and (on Linux and macOS) has its directory entry flushed. An interrupted save leaves the previous file intact, never a half-written one; a kill-point fault-injection suite checks that at every filesystem operation, including partial multi-document saves and the deck/progress promotion boundary. Surviving a hard power loss additionally relies on flushing before the rename, which the code does but a test cannot simulate. That protects against Alix; your own folder backup protects against disk failure and accidental deletion.
Multi-device via your cloud drive
With the defaults, your decks, augmentation, assets, and progress live in one folder. Put that folder in a cloud drive you already use (Dropbox, iCloud, OneDrive, Syncthing) and it follows you across devices. Alix stays unaware that the folder is synced and uploads nothing itself.
To share only the workspace’s authored and regenerable material while keeping
private files on each person’s device, add this .stignore to the shared
folder:
.alix/
*.local.*
For a free, no-account option that fits alix’s local-first grain,
Syncthing works well: install it on each machine, pair
the devices, and share your decks folder between them. It syncs the folder
peer-to-peer over your own network, with no cloud company in the middle. Add
*.json.tmp to the folder’s .stignore too, since alix writes documents
through a temporary file before the atomic rename above.
The writer boundary is now one deck, not the whole workspace. Different devices may review different decks in the same synchronized folder: their progress documents do not compete. For the same deck, use one active writer and let synchronization settle before switching devices. Alix does not merge concurrent same-deck reviews or decide which schedule is semantically correct. A disconnected collision therefore remains a Syncthing conflict copy. If a running web session’s own deck is replaced under it, that session can no longer save; the review screen shows a persistent banner and the fix is to reopen the deck (grades made after the collision are not kept).
Run alix doctor <folder> before recovery. For a progress conflict, stop both
writers and synchronization, back up the folder, compare the canonical
.alix/progress/deck-<token>.json with its
deck-<token>.sync-conflict-….json copies, and deliberately keep the complete
history you trust at the canonical path. Do not combine schedules by hand.
Augmentation conflicts are regenerable: keep one complete
augment/deck-<token>.json or move all conflicting copies aside and regenerate
that deck’s augmentation. Resume synchronization and rerun doctor only after
the canonical files are settled.
A card’s identity is a minted token alix writes into the file as an
<!-- id: ... --> line, not a hash over its content. Editing any text,
including the answer, preserves a card’s history; only deliberately replacing
a card starts it over. (That’s the “editing is safe” rule from
chapter 3, stated precisely.)
alix reset <target> clears progress so cards go “new” again: a whole deck, a
folder or workspace (every member deck, plus a workspace’s mastered flags and
personal-card schedules), a single card (--card <id-or-front>), or the entire store
(--all); it confirms once unless you pass -y.