The manual

Fanad

Get it out of your head.

Everything you can say to Fanad, in the order you'll need it: start here, learn the everyday moves, then reach for the advanced pieces and the admin setup only when you want them. The whole tool fits in three rules — and a leading slash is always optional.

Start with the quickstart What is Fanad?

Part one

Quickstart

The handful of moves that are 90% of Fanad. If you only read one part, read this one.

The Rules of Three
  • A statement becomes a task on your list.
  • A question runs a command.
  • Answering Fanad's own open question is taken as that answer.

That's the whole grammar — and the leading slash is always optional, so /mood 🙂 and mood 🙂 do the same thing.

The Rules of Three

The rules live in chat too — ask for them any time.

CommandWhat it does
/rulesShow the Rules of Fanad — the rules of three.
/howtoThe getting-started walkthrough on how to fill your Fanad.

Just talk to capture

Say what's on your mind in your own words and Fanad files it as a task, quietly picking a category and effort. You unload; Fanad organizes.

Do thisWhat happens
Capture a task by talkingMake a plain statement and Fanad files it as a task in your own words.
Send a photoA photo with a caption files a task with the photo attached.
Don't sort, just unload

Organizing is Fanad's job, not yours. Fanad keeps your own words as the task title and silently picks the category and effort — you never have to file, tag, or tidy anything by hand.

See and clear your list

Your open tasks, always numbered 1..N for whatever's on screen. Finish what's done, clear what isn't yours anymore.

CommandWhat it does
/tasksShow your open tasks — grouped by category, or counts to drill into when there are many.
/startStart task N, showing your original words plus its steps.
/doneFinish task N (or a few: done 1 2 3), or a bare done finishes what you just started.
/dropClear task N off your list, archiving it as dropped.
List numbers are per-listing

Positions restart at 1 on every listing and every page. done 3 always means the third row you last saw — never a hidden database id.

One thing to do now

When the list feels like a lot, ask for a single next step, sized to your time, energy, and mood — never a wall.

CommandWhat it does
/whatdoFanad suggests one thing to do right now, sized to you.
yes / no / smaller / done / not todaySteer it: yes to start, done if finished, smaller for lighter, no/not today to pass.
A "no" is never the end

Refusing a suggestion never fails. Fanad simply offers something smaller, or nothing at all — and after a few passes it gently offers to reword or break the task down rather than push.

Tell Fanad how you feel

Drop a mood anytime; it gently softens and sizes what Fanad suggests for a while.

CommandWhat it does
/moodSet how you feel with an emoji (mood 🙂) or a word (mood overwhelmed); it steers suggestion size.

Where to get help

Friendly, tappable references whenever you need them.

CommandWhat it does
/guideOpen the topic-guide hub, or guide <topic> for a deep dive on one feature.
/commandsThe tappable command reference, split into sections that expand in place.
c  (/menu)Pop the tappable command menu; works mid-question to back out.

Part two

Core

The everyday depth: filing precisely, breaking tasks into steps, jotting notes, dates, rewards, and letting stale tasks rest.

Filing tasks your way

Capture happens by just talking, but you can be explicit when you want a category, a due date, or a quick "today".

CommandWhat it does
/task  (/task:category)Explicitly file a task, optionally with a category, deadline, or inline priority.
/today  (x)File a task due by the end of today (e.g. x call the pharmacy).
/tasks allShow every open task as a ranked, paginated flat list.
/tasks todayShow only the tasks due by the end of today.
/whatdo todayThe same one-thing suggestion, scoped to tasks due today.

Steps: breaking a task down

Add steps under a task and Fanad walks you through them one at a time with tappable checkboxes.

CommandWhat it does
step (s) / substep / subtaskAdd a step under your last task, or under a listed task with step N …
unstep / remove stepRemove step N (or unstep all); the list renumbers.
start (stepping)Starting a stepped task walks you through its steps one at a time.
done / done N / done allTick off steps: done for the next, done 2 3 for a few, done all finishes the task.
stop / pauseLeave a stepping session without finishing; steps stay saved.

Notes: a place for the rest

Jot things you want to recall later, kept separate from your task list. Find them by meaning, not exact words. (Opt-in.)

CommandWhat it does
/note  (n)Jot something to recall later, kept separate from tasks.
/notesShow your note inbox.
/recall  (r)Find a note by meaning rather than exact words.
/promoteTurn note N into a real task (carrying any attached photo over).
/forget  (/delete)Delete note N from your inbox.

Deadlines, reminders & calendar

Dates gently lift a task's ranking. Reminders fire once. For anything recurring, hand it to your own calendar — Fanad won't nag on a schedule.

CommandWhat it does
by <when>End a task with by friday to set a due date that lifts ranking and retires after it passes.
on <when>on sunday 6pm sets both a deadline and a one-time reminder at that moment.
remind me … at <time>A one-time nudge that pings once and leaves the task on your list.
/timer <how long>Timer module (opt-in): a one-shot ding — timer 12 min pasta; bare timer lists, timer off 1 cancels. Nothing lands on your list.
/calAdd dated task N to your own calendar via a downloadable .ics file.
No recurring tasks — use /cal

Recurrence is nagging, and nagging is stress, so Fanad has none. A dated task instead gives you an "add to calendar" .ics so you make it recur in your own calendar, on your terms. Reminders fire exactly once.

Rewards & knowing yourself

Let finishing feel good, and see what Fanad has quietly learned about your patterns.

CommandWhat it does
/rewardAsk what reward fits, so finishing feels good (prompts you to save one if you have none).
/me  (/dossier)Show what Fanad has learned — completion rate, favored categories, usual mood.
/summaryA narrative recap of your activity, optionally scoped (today, this week, last week).

Locking categories

Doing a batch on one theme? Pin a category or effort so Fanad stops guessing each one.

CommandWhat it does
/lockPin a category and/or difficulty for the next tasks you add; a new word mints that category.
/unlockClear the lock so Fanad sorts each task on its own again.

Sleeping & reviving

Tasks untouched for about three weeks quietly go to sleep so the list stays scannable. Nothing is lost — bring them back anytime.

CommandWhat it does
/sleepingShow tasks that drifted off to sleep after ~3 weeks untouched.
/reviveBring a sleeping task back onto your list (e.g. /revive 1, or a few: /revive 1 2 3).
Auto-sleep after ~3 weeks

Once a day, anything left untouched for about three weeks quietly goes to sleep — kept out of both your listings and your suggestions, so the pad never rots into a guilt archive. Revive any of it whenever you like.

Photos & shortcuts

Little conveniences: resend an attached photo, and lead a message with a single letter to skip the command.

CommandWhat it does
/picResend the photo attached to task N (rows with a photo show a tappable 📷 /pic_N link).
n t d k s r g x wLead with one letter: n=note, t=task, d=done, k=drop, s=step, r=recall, g=guide, x=today, w=whatdo.

Turning modules on & off

A new account sees only Tasks. Turn on the extras you want, whenever you want; turning one off hides but never deletes your data.

CommandWhat it does
modulesShow your optional modules and tap to turn each on or off.
optin / optout <module>Turn a module (lists/notes/metrics/vouch/notebook/timer) on or off; opt-out keeps your data.
Per-user modules are opt-in

Notes, Lists, Metrics, Vouch, Notebooks, and Timer are all off by default so a fresh account sees only Tasks. Turn on exactly what you want; opting a module back out only hides its data, it never deletes it.

Part three

Advanced

For when you want more control: nestable lists, templates instead of recurrence, metrics, notebooks, and a look under the hood at how suggestions are chosen.

Lists & nesting

A separate outliner for anything that isn't a task — checklists, packing lists, anything nestable. (Opt-in.)

CommandWhat it does
/listsOpen your nestable lists; at the top, type a name or /list <name> to start one.
/list <name>Create a new top-level list, or add an item when a list is open.
/sub_NDescend into list item N as its own sub-list; /sub_N text quick-adds a child.
out / top / next / prev / del / rename / exitMove around inside lists: up a level, to all lists, page, delete, rename, or leave.

Notes recall, grounding & photos

Fanad finds your real notes by meaning — and only ever surfaces notes and tasks that actually exist. It never invents a row.

FeatureWhat it does
Semantic note recallFinds your real notes by embedding-cosine meaning plus keyword match — only actual stored notes.
Notes inbox + photosA caption-less photo waits in your notes for recall; a captioned photo becomes a task.
Closed-world id allow-listRejects any suggested task that isn't in your real tasks, so Fanad can never surface an invented one.
Grounded — never invents

Retrieval decides what exists; the model may only order and rephrase a closed set of your own real rows. An id allow-list backstop rejects any invented task, so Fanad can't hand you homework you never wrote down.

Guessing steps

The one place Fanad draws on general know-how: once a task is started, it can guess a first-draft checklist — always labeled a guess, always yours to edit.

CommandWhat it does
/guessOnce a task is started, Fanad guesses an editable step checklist from general know-how (add via step, remove via unstep).
/guess is the one labeled exception

Everywhere else Fanad never invents. /guess is the single sanctioned place it draws on the model's general know-how — and the result is always surfaced as an explicit, disposable, fully-editable guess, never as fact.

Templates

The calm alternative to recurring tasks: save a task's shape and steps, then drop a fresh copy whenever you need it.

CommandWhat it does
/template N <name>Save listed task N as a reusable blueprint (shape + steps), never a deadline or priority.
/template <name>Drop a fresh copy of a saved template onto your list, reset to unchecked.
/templatesList your saved templates.
/template retire <name>Delete a saved template.

Metrics & diet

Track any number you like, and log meals with an LLM calorie estimate you confirm. (Opt-in.)

CommandWhat it does
/track /measure /tally /chartDefine metrics, log summed or one-off readings, and see a daily tally against optional targets.
/eatEstimates a food's calories/protein/carbs/fat, confirms with you, then logs it so undo removes them together.

Notebooks

Step into an isolated sub-space with its own tasks, notes, and lists — separate from your main one. Like a fresh account you can walk into and back out of. (Opt-in.)

CommandWhat it does
optin notebookTurn Notebooks on (off by default). Opting back out returns you to your main space; nothing is deleted.
notebookWhere am I? Lists your notebooks with tappable switch buttons — plus a “back to main” chip when you're inside one.
notebook <name>Switch into that notebook — a new name creates it on the spot (notebook work). Keep names under 40 characters.
notebook mainBack to your default space (home, exit, and out work too). Notebooks never nest.
notebook rename <old> <new>Rename one of your notebooks.
One person, separate rooms

Everything you say lands in whichever space you're standing in — say notebook if you're unsure where you are. A notebook is yours alone (nobody else can reach it), your module choices carry across all your spaces, and reminders still find you in the same chat.

Vouching people in

Fanad is invite-only by endorsement: anyone already in can vouch a friend in, and the record remembers who let in whom. (Opt-in — already on for the owner.)

CommandWhat it does
optin vouchTurn vouching on for yourself (off by default; the owner has it on already).
vouch @usernameLet someone you trust message this bot — their Telegram @username, or on Slack just @-mention them. You're on record as who let them in.
vouchWho you've vouched in so far.
Grows by trust, revokes from the root

There's no un-vouch in chat: the owner revokes from web Settings, and revoking someone also revokes everyone they vouched in. Vouches are per-platform — letting someone into Telegram doesn't open Slack.

Check-ins

Ask for a gentle, once-a-day nudge at a time that suits you — a check-in, never a stream of pings.

CommandWhat it does
/wakeSet a gentle check-in at a time of day (e.g. /wake 8:30).
/wakelistList your scheduled check-ins.
/wake off <id>Remove a check-in by id.

How the suggestions think

/whatdo isn't random. Fanad retrieves your own tasks, scores them on real signals, then lets the model pick one with an honest reason — and a "no" just reshapes the offer.

PieceWhat it does
RAG suggestion engineRetrieves your open tasks, prefilters a shortlist, then the model chooses the single best next one with an honest reason.
Learned affinity scoringNudges a task from your real outcomes (done/refused/dropped and 👍/🙁) per category and time of day.
Context-fit scoringNudges a task by whether now matches the day-part, hour, and weather it was noted in.
Deadline urgency boostLifts a live-dated task as its due date nears, without ever being an absolute override.
Anti-repetition + refusal penaltiesDown-weights recently-shown tasks and ones you tend to refuse now; never re-offers the just-declined pick.
Grooming reshapersAfter repeated refusals, offers to reword a task or break it into first steps — inventing nothing.

Mood, brain-state & sizing

Your mood steers how big a suggestion is, so a hard day gets an easier ask.

PieceWhat it does
Mood-to-energy sizingInfers energy from your last mood (low/sad/sick/hungry → lighter offers) to soften suggestions for ~6h.
Brain-state awarenessA designed nervous-system gate to down-shift to low-demand offers when dysregulated; not yet in code — the shipped proxy is mood sizing.

Reactions & the honest-by-design behavior

Fanad acks your message with a quick two-step reaction, and holds two promises: it never invents, and it lets stale tasks rest.

BehaviorWhat it does
Reactions on your messageA two-step reaction: 👀 on arrival, then a decision emoji (mood, ✍ for a note, else 🫡; 🤬 on error).
Auto-sleep of stale tasksOnce a day, long-untouched tasks go to sleep and are also excluded from suggestions.
Honest recommendation reasonsEvery suggestion's reason is the model's own or a deterministic phrase grounded in real signals — never fabricated.

Part four · host only

Admin & setup

For whoever runs the box: installing, wiring up surfaces, bringing your own local model, and the flags that guard privacy and persistence.

How private is it? The four tiers

Privacy isn't one switch — it's three choices: where Fanad is hosted, how you talk to it, and which model does the thinking. The setup below can land you anywhere on this scale, so pick the column you're comfortable with before you wire things up.

Your setup Full privacy Mostly private Performance over privacy Convenience
You talk to it via Your own web app, over your private tunnel Telegram Telegram Telegram
Hosted on Your machine Your machine Your machine A cloud server you rent
The AI model Local (LM Studio / Ollama) Local (LM Studio / Ollama) A cloud AI provider A cloud AI provider
Notes & data live On your machine On your machine On your machine On the cloud host
Who can see your words Only you You + Telegram You + Telegram + the AI provider Telegram + the AI provider + your host
Good when Your notes are sensitive and privacy comes first. You want Telegram's ease, but keep data & AI at home. You want the strongest model and will trade some privacy for it. You just want the easiest start and privacy isn't the concern.

Telegram bot messages aren't end-to-end encrypted, so Telegram can see what you send its bot — that's why the web app (reached over your own tunnel, browser-to-server) is the private surface. A cloud model only sees your prompts when LLM_ALLOW_CLOUD is on (off by default); see Cloud & privacy boundary below.

Install & run

Node 24+ and a local model, then a handful of npm scripts. All state lives in one SQLite file.

CommandWhat it does
Node >= 24Pinned because Fanad uses the built-in node:sqlite module (unflagged on 24+).
npm run devRuns the server with --watch and auto-loads .env for local development.
npm startStarts the production server (Express API + built frontend) on PORT (default 8787).
npm run buildBuilds the web/ React app into static assets served in production.
npm run web:devRuns the Vite dev server for the web frontend (proxies /api to the Node server).
npm testRuns the built-in node --test suite.
npm run reindexRe-embeds every task/note/reward for all users after switching embed providers.

Surfaces & channels

Three surfaces, one shared brain. Both bots reach out via outbound connections — no public URL or inbound ports. Access is fail-closed: strangers get silence.

SurfaceWhat it does
Telegram botThe primary surface: text/photo capture, inline menus, two-step reaction acks, via grammY long-polling.
Telegram setupPaste a @BotFather token and allowed usernames into web Settings; the server validates and starts the bot.
Slack botOptional second channel at Telegram parity via Bolt Socket Mode: Block Kit buttons, mrkdwn, .ics uploads.
Slack setupPaste xoxb- bot token and xapp- app-level token into web Settings; secrets stored encrypted.
Web appA React chat UI with Settings, a your-data browser, reactions, and scroll-back history.
Web reachThe built frontend is served by the same Express server; reach it over LAN at http://<host>:8787 or via Tailscale.
Mac mini always-on deployA copy-pasteable guide to run Fanad + LM Studio on an always-on Mac mini via launchd/pm2 with Tailscale.

Bring your own local LLM

Local is the default and the privacy boundary. Run LM Studio or Ollama separately with both a chat and an embedding model loaded.

SettingWhat it does
LM Studio (local, default)Default local provider at http://127.0.0.1:1234/v1; auto-uses the loaded chat model.
Ollama (local)Alternative local provider at http://127.0.0.1:11434/v1, selectable in Settings.
BYO local model requirementRun LM Studio/Ollama with BOTH a chat and an embedding model loaded, or chat/embeddings error.
LLM setup in SettingsConfigure provider, base URL, chat/embed models, and (for cloud) an API key from the web UI; keys never returned.
LLM_PROVIDERSelects the chat provider (lmstudio|ollama|openai|gemini|anthropic); default lmstudio.
EMBED_PROVIDERSelects the embeddings provider independently (lmstudio|ollama|openai|gemini); default lmstudio.
Privacy is the default

Everything stays on your own machine with a local model doing the thinking — no cloud, no account, no telemetry, nothing leaving the box. That boundary is what makes an honest note possible.

Cloud providers & the privacy boundary

Cloud is off by default and hard-blocked at the factory. LLM_ALLOW_CLOUD is the real boundary — the UI gate is only the friendly front.

FlagWhat it does
LLM_ALLOW_CLOUDMaster flag unlocking cloud providers in Settings and on the write path; OFF by default, enforced by a 403 too.
Provider cloud hard-blockCloud providers are refused at the provider factory unless LLM_ALLOW_CLOUD is on.
OPENAI_API_KEY / _CHAT_MODEL / _EMBED_MODELBYO OpenAI credentials + model overrides; only usable when LLM_ALLOW_CLOUD is on.
GEMINI_API_KEY / _CHAT_MODEL / _EMBED_MODELBYO Google Gemini credentials + model overrides; gated by LLM_ALLOW_CLOUD.
ANTHROPIC_API_KEY / _CHAT_MODELBYO Anthropic Claude chat credentials + model (no embeddings); gated by LLM_ALLOW_CLOUD.
LMSTUDIO_BASE_URL / OLLAMA_BASE_URLBase URL of the local server; blank falls back to a provider-aware default.
LMSTUDIO_CHAT_MODEL / _EMBED_MODELThe exact chat and embedding model ids on the local server.
LMSTUDIO_API_KEYAPI key for the local server (LM Studio ignores it; defaults to 'lm-studio').

Channel & weather credentials

Bot tokens and weather keys, all stored encrypted and configured from Settings, not .env.

SettingWhat it does
TELEGRAM_BOT_TOKENTelegram bot token from @BotFather; enables Telegram via outbound long-polling. Blank disables it.
SLACK_BOT_TOKEN / _APP_TOKEN / _SIGNING_SECRETSlack credentials: xoxb- bot token plus xapp- app token (Socket Mode) or a signing secret (HTTP/Events).
WEATHER_PROVIDERWeather backend selector (default open-meteo, which needs no key).
OPENWEATHER_API_KEYAPI key for the OpenWeather provider (only if not the keyless open-meteo).
Web Settings (Telegram)Sets the bot token + allowed username and (re)starts Telegram; the token is stored encrypted.
Web Settings (Slack)Sets the Slack tokens/mode and allow-list, then (re)starts Slack; secrets stored encrypted.
Web Settings (Weather)Sets the location and unit and immediately refreshes conditions from Open-Meteo.
Web Settings (LLM)Configures provider, base URL, models and API key from the UI; cloud rejected when the flag is off.

Secret encryption (KEK)

Every stored secret is encrypted at rest. The off-box KEK is the only thing defending secrets against box theft.

SettingWhat it does
KEK envelope encryptionAES-256-GCM encryption of stored secrets so nothing is ever plaintext; decrypted only on read.
KEKThe off-box env encryption key; deleted from process.env at boot and the only defense against box theft.
KEK_FILEOverrides the on-box bootstrap-key file path (default <dataDir>.kek) to move it off the backup set.
Secret re-key migrationLifts every bootstrap-encrypted secret to the env KEK once one arrives, then retires the bootstrap key.

Feature toggles & opt-in

Tasks are the always-on core. Notes, Lists, Metrics, Vouch, Notebooks, and Timer are per-user opt-in and off by default.

SettingWhat it does
optin / optout / modulesPer-user opt-in for optional modules (all OFF by default; Tasks always-on) via chat or web.
Web feature-toggle checkboxesReturns and sets the acting user's module state; turning Notebooks off drops them to their main space.
Metrics module toggle (web)The per-user on/off for the Metrics & diet module (off by default).
Notebooks switch/create (web)List, switch into, and create the account's isolated sub-user spaces; hidden when the module is off.

Access: vouch & impersonation

Grow the whitelist by endorsement, and — on a single-operator host only — act as any user.

SettingWhat it does
Telegram authFail-closed: owner claims on first contact, plus a @username allowlist and social vouches; strangers dropped.
Slack authSame fail-closed control keyed on the Slack user id, with platform-namespaced vouches.
Vouch list + cascade-revoke (web)Lists who endorsed whom and soft-revokes a handle plus everyone in the subtree they vouched.
USER_IMPERSONATIONHost-only flag letting the web UI act as any user via X-Fanad-User; default OFF, keep off on multi-user deploys. The server prints a loud warning banner on every boot while it's on.
Impersonation picker (web)Lists accounts so the operator can switch which user the web acts as; empty unless USER_IMPERSONATION is on.
Acting-user resolverThe single seam mapping X-Fanad-User to an acting user, defaulting to root so a bad header can't escalate. When web login is on, the session decides instead and the header is ignored.
Web local accessBy default the web app has no login (auth mode none); it acts as root and is meant for LAN/Tailscale only. Turn on web login below for anything networked.

Web login (Settings → Security)

An opt-in login for the web UI: username + password + a mandatory authenticator (TOTP) code, enrolled by scanning a QR. Set everything up while the mode is still off, then flip the dropdown — it refuses to enable until the credentials are complete, so you can't lock yourself out. Telegram/Slack channels are unaffected.

SettingWhat it does
Auth mode (none | simple)The in-app dropdown. none = open web UI (today's trust model); simple = every web visitor signs in. Only the root user can change Settings while login is on.
Web login accountRoot's username + password (scrypt-hashed at rest). Changing the password signs out every other session.
Two-factor (required)Scan the QR with any authenticator app, verify a code to finish. The secret is stored encrypted under the KEK; a re-enroll keeps the old authenticator working until the new one is proven.
Allow new users to registerShows "Create an account" on the login screen. A registration isn't usable until its own 2FA is scanned and verified; each account is a fully separate tenant.
Web IP allowlistOptional, works in either mode: restrict the whole web UI to listed IPs/CIDR ranges. Loopback always passes and /api/health stays open (platform healthchecks). Needs TRUST_PROXY behind a reverse proxy.
AUTH_MODEEnv default for the mode before any in-app choice; the dropdown's stored value wins thereafter.
AUTH_RESETBreak-glass: set to 1 and restart to force login off (credentials and 2FA preserved) after a lockout — lost phone or lost KEK. Unset it afterwards.
TRUST_PROXYSet to 1 (or a hop count) behind Coolify/Traefik so the IP allowlist and login rate-limit see the real client address.

Deletion, retention & data

Deletion truly deletes — unless you first turn on a retention export. Browse and edit your own data anytime.

SettingWhat it does
/requestdeletionConfirm-gated full erase of a user's account and data.
Data retention toggleTurns on a full zip export of a user's data before /requestdeletion erases it; OFF by default.
Retention export zipSnapshots every row (including each notebook) into a timestamped zip before the wipe.
Data Browser ('Your data')A user-scoped browser over a whitelist of tables (app_settings excluded) for transparency and in-place edit/delete.

Diagnostics & logs

Optional panels for the operator. Both expose sensitive content over the unauthenticated local API, so both are off by default.

Endpoint / settingWhat it does
GET /api/healthUnauthenticated liveness probe — booleans only (ok, secrets encrypted, persist mounted, LLM reachable). Deployment detail (paths, KEK source, impersonation) is logged at startup instead of served here.
AI Activity Log viewer + toggleTails every LLM call (purpose/prompt/reply/latency) plus the /whatdo decision; live DB toggle, OFF by default.
DEBUG_LOGTees console logs into a ring buffer served to the web debug panel; never enable in production.
Debug Log viewerServes the captured server-log ring buffer to the web debug panel, only when DEBUG_LOG is set.

Persistence & deployment env

In production Fanad fails fast if its data volume isn't mounted, so the DB and KEK never land on ephemeral storage.

SettingWhat it does
PERSIST_DATANames the persistent volume (default /persist) where the DB + KEK live; prod boot fails fast if unmounted.
DATA_DIRExplicit override for where the DB + KEK live; the escape hatch that bypasses the persist fail-fast.
NODE_ENVWhen 'production', activates the persist fail-fast and force-disables SETUP_MODE.
PORTTCP port the Express server listens on (default 8787).
SESSION_SECRETUnused — web login sessions are opaque random tokens stored hashed in the DB, so no signing secret is needed.

Setup mode & config portability

Move a whole config between servers — but only in setup mode, because the backup contains decrypted secrets.

SettingWhat it does
SETUP_MODEUnlocks settings backup/restore; the backup holds decrypted secrets, so it's force-disabled in production.
Settings backup / restoreExport/import all settings (decrypted, re-encrypted on restore) to move a config; only when SETUP_MODE is on. The web-login config (mode, credentials, IP allowlist) deliberately never rides along.
GET /api/setupReports whether SETUP_MODE is active so the UI shows or hides backup/restore controls.

Connecting your channels — step by step

Fanad reaches you over Telegram and/or Slack. Both connect outbound — no public URL, no inbound ports — and you paste the tokens into web Settings, where they're stored encrypted (see Secret encryption). Here's how to get them.

Telegram — a couple of minutes

  1. In Telegram, open a chat with @BotFather and send /newbot.
  2. Give it a display name, then a username ending in bot (e.g. my_fanad_bot).
  3. BotFather replies with an HTTP API token like 123456789:AA…. Copy it.
  4. In Fanad, open web Settings → Telegram, paste the token, and add your own Telegram @username to the allow-list. Save.
  5. Fanad validates the token and starts the bot over grammY long-polling. Message your bot and send /howto to begin.

Access is fail-closed — the owner is claimed on first contact and everyone else stays silent until allow-listed or vouched. No webhook or open port is needed; the bot polls Telegram outbound.

Slack — optional, ~5–10 minutes

Fanad's Slack channel runs in Socket Mode (also outbound). You create a small Slack app and hand Fanad two tokens.

  1. Go to api.slack.com/apps → Create New App → From scratch; name it and choose your workspace.
  2. Socket Mode: open the Socket Mode page and enable it. When prompted, generate an app-level token with the connections:write scope — this is the xapp-… token. Copy it.
  3. Bot scopes: under OAuth & Permissions → Bot Token Scopes, add exactly what Fanad uses: chat:write, files:write (the calendar .ics), reactions:write, im:history, and im:write.
  4. Events: under Event Subscriptions, enable events and subscribe to the bot event message.im so Fanad receives your DMs. Button clicks arrive over Socket Mode automatically.
  5. Install: under Install App, install to your workspace and copy the Bot User OAuth Token — the xoxb-… token.
  6. In Fanad, open web Settings → Slack, paste the xoxb- bot token and the xapp- app-level token, add your Slack user to the allow-list, and save. DM the bot to confirm.
Tokens live in Settings, encrypted

Paste tokens into the web Settings UI rather than .env — they're encrypted with the KEK. The env vars TELEGRAM_BOT_TOKEN, SLACK_BOT_TOKEN, and SLACK_APP_TOKEN exist only as a first-boot bootstrap fallback.