Skip to content

Configuration

fast-classifier.config.ts
import { defineConfig } from 'fast-classifier/config'
export default defineConfig({
categories: [{ name: 'Dev', label: 'Inbox/Dev' }],
rules: [{ kind: 'domain', domain: 'github.com', category: 'Dev' }],
keepList: ['important@example.com'],
sweep: { targetLabel: 'Promotion' },
needsAction: { label: 'Needs action', windowDays: 60 },
})

fast-classifier init writes a fuller starter config with a domains() helper for bulk domain rules.

Explicit --config <path>fast-classifier.config.ts.mjs.js.json in the working directory → built-in defaults.

JSON configs work identically to TypeScript ones — rule patterns are plain strings in every format (the schema compiles them to case-insensitive regexes), so nothing is lost in translation.

Field What it configures
provider mail transport: type: 'jmap' | 'mcp' plus an optional baseUrl endpoint override (testing / self-hosted JMAP)
categories { name, label, description? } — the category names rules reference, and the Fastmail label each files into (labels may be nested paths like 'Inbox/Finance')
rules the classifier rules: { kind: 'sender', address } (exact address, wins first), { kind: 'domain', domain } (registrable root domain), or { kind: 'name', pattern, onlyForDomains? } (display-name regex, by default only consulted for relay domains)
keepList exact sender addresses that must never be swept, even when their mail says “unsubscribe” — enforced twice, server-side and client-side
sweep targetLabel (default 'Promotion'), textHeuristic (default 'unsubscribe'), optional after ISO date
needsAction label, threshold (default 3), languages (built-in keyword packs, default ['en']), optional highKeywords / exclusionKeywords (arrays of { phrase, weight } — when present they replace the packs), unreadBonus, personalNeedsReplyBonus, windowDays (default 60)
detection relay/account/personal-provider domain lists, the user’s own personalDomains, and the regex sources behind the human-sender detector — including brandNamePattern, which defaults to global household names only
ops batching and pacing: batchSize (≤ 50), batchDelayMs, stallBackoffMs, stallLimit, progressEvery, optional maxItems — see Ops Conventions

The config is validated with zod: rules must reference declared categories, duplicate category/domain/sender entries are rejected, and every regex source must compile — a bad pattern fails at load time with a pointed error, never as a raw SyntaxError mid-run.

You do not have to write rules from scratch — the suggest engine drafts a first config from what is actually in your inbox. It scans read-only, skips domains your config already covers, and matches the rest against a built-in catalog of about 200 globally recognizable root domains grouped into 12 generic categories (Finance, Shopping, Travel, Development, Social, Jobs, News, Entertainment, Health, Telecom, Cloud, Accounts). Domains not in the catalog come back as unknown, sorted by volume, for you to decide on — the catalog never guesses.

Three ways to run it:

  • CLI: fast-classifier suggest prints the suggestions plus a paste-ready config fragment — see the CLI reference.
  • MCP: the suggest_rules tool returns the same result as structuredContent with a configFragment string, so an agent can build your config for you.
  • Library: suggestRules(analyzeReport.domains, compiled) and toConfigFragment(result, accepted) from fast-classifier.

The fragment includes any category definitions you are missing, in defineConfig style — paste it into your config, run plan to check coverage, then write rules for the unknown domains the report surfaced. Iterate until coverage is where you want it.

The needs-action scorer ships two built-in keyword packs: en (the default) and de. High-signal phrases (“action required”, “deadline” / “Frist”, “Mahnung”) score +3; receipt/shipping/newsletter exclusions score −2.

needsAction: { languages: ['en', 'de'] } // opt into the German pack alongside English

Explicit highKeywords / exclusionKeywords arrays replace the enabled packs entirely — packs and custom lists never merge, so what you write is exactly what scores.

detection.brandNamePattern feeds the human-sender detector: a sender whose address or display name contains a brand token is never treated as a human awaiting your reply. The built-in default covers global household names only (uber, amazon, paypal, github, …) so it holds for any user. Add your regional or niche brands by overriding it:

detection: {
brandNamePattern: 'uber|amazon|paypal|github|my-regional-bank|my-local-delivery',
}

examples/fast-classifier.config.ts carries the origin session’s full 55-token list as exactly this kind of override.

See examples/ in the repo for a full real-world config — the German-power-user setup that reached 87% coverage (14 categories, 81 domain rules, the bilingual languages: ['en', 'de'] packs, and the full brand-pattern override) — plus its JSON twin and minimal sweep-only / needs-action-only / MCP-transport variants.