After the setup wizard: tuning what you get

Start here: run docker compose up -d --build in the project directory and open http://127.0.0.1:3000; the setup wizard (/setup) asks for your language, market, profile, targets, sources, AI and Telegram and writes the files for you (see setup.md). This guide is for after the wizard: it explains the same settings page by page, so you can tune them later. Each step says which page to use, what to fill in, and how to confirm it took effect. The mock-ups use example values (a Berlin / Germany setup); yours will show your own market, cities and currency.

Web address: http://127.0.0.1:3000 on this machine; from a phone or another device, use your Tailscale address (public_base_url in data/config/notify.yaml). Changes made on the pages are written back to the YAML files in the project directory with comments preserved; editing the YAML directly works too, and the two are equivalent. (The project directory is the one that contains package.json, compose.yaml, config/ and data/.)

中文版

How it works

By default a cycle runs automatically on weekdays at 07:00 and 17:00 and at 09:00 on weekends, in your configured time zone (schedule.yaml). Each cycle goes through 6 stages, and each condition you set affects exactly one of them. Click a block to jump to the matching setup step.

Rank value = AI match score + signal bonus (capped). Jobs removed by the hard filter are not scored or notified, but they stay in the database and you can keep them manually at any time with "Keep anyway".

Where do I set the roles I want and my filters?

There are two kinds, and the first two cards are the easiest to mix up: "What to search" decides which jobs get pulled from the sites; "What I want" decides which of the pulled jobs stay and how they are scored.

My profile → What I want /me

Of the jobs pulled in, which stay and which are filtered out?
  • Target roles (also used for scoring)
  • Location, work mode, remote scope, seniority
  • Salary floor, contract type
  • Exclude student roles, companies, keywords

File data/profile/preferences.yaml → Step 3

Settings → Search /settings

Which keywords and cities to search on the sites?
  • Keyword groups (each group is OR-ed into one query)
  • Cities (each group is queried once per city), remote scope
  • Look-back hours per cycle, max results per group per city

File data/config/search.yaml → Step 4

My profile → About me /me

On what basis does the AI judge whether I fit?
  • Languages and levels, skills and years, years of experience
  • Home city, whether you need visa sponsorship, a summary for the AI

File data/profile/me.yaml → Step 2

Interests + notifications /interests /settings

Which jobs rank higher and get pushed to your phone?
  • Compensation / benefit signal words and bonus weights, watched companies
  • Notification threshold (default 70, 60 with a salary or equity signal), quiet hours
  • Saved filter views (switch at the top of the Today page)

→ Step 6, Step 7

⚠️ Keep both sides in sync. If you add a target role in /me but no matching keyword in the /settings search, the system will never go looking for it; conversely, if you add a search keyword but not the target role, the pulled jobs will get a lower title-match score. When you change one side, change the other.

1Open the web UI and confirm the system is healthy

/sourcespnpm run doctor
  1. Open http://127.0.0.1:3000 in a browser (on a phone, first connect with the Tailscale app, then open the Tailscale address). Left navigation: Today, Jobs, Favorites, Pipeline, Sources, Insights, Settings, My profile, Interests.
  2. Run a health check in the project directory and read the summary on the last line:
cd <your project directory>
pnpm run doctor
All ✅ Everything is fine, go on to the next step.
Some ⚠️ It works, but something needs attention (for example a channel is missing its key). Each item has a "what to do" line below it.
Some ❌ Something is broken. Follow the "what to do" line before continuing.

Write pnpm run doctor, not just pnpm doctor (the latter is a built-in pnpm command). doctor is read-only: it sends no messages, calls no AI and costs nothing. The Sources page in the web UI shows the same information: each channel's status, usage and latest run, plus the heartbeats of the runner and ai-worker.

2Write "About me" — the basis for AI scoring

/me → About medata/profile/me.yaml

This part is sent to the AI as a summary for scoring. Fill in at least skills or the summary, otherwise scoring pauses (a banner appears at the top of the Today page).

127.0.0.1:3000/me
About me
Languages, skills and the summary are sent to the AI as a summary for scoring. Do not write your name, contact details or ID numbers.
1Languages
zh · nativeen · C1de · A2+ Add language
Language code (en, de, zh) + CEFR level. Your German level is compared with the JD's German requirement.
2Skills
Python · 6 yrsTypeScript · 4 yrsLLM / RAG · 2 yrs+ Add skill
Name + years; list the ones relevant to your target roles.
3Years of experience / home city
8Berlin
4Needs visa sponsorship
Need the employer to provide visa sponsorship
5Summary for the AIA few sentences: what you have done, what you are good at, what direction you want your next job to take.The most important text for AI scoring. Do not write your name, email or phone number.
SaveRevertSaved
After you change your profile: old scores are marked "stale" and re-scored automatically in later processing cycles; you can also click "Re-score stale scores" in /settings to do it right away. Every save keeps a history snapshot, and you can roll back at the bottom of the /me page.

3Write "What I want" — roles and filters

/me → What I wantdata/profile/preferences.yaml

These conditions drive the hard filter (non-matching jobs are filtered out directly, not scored or notified), and target roles also make up 40% of the pre-score.

127.0.0.1:3000/me
What I want
Target roles, locations, salary floor and exclusions, used for the hard filter and scoring.
1Target roles
AI EngineerLLM EngineerForward Deployed EngineerAI Solutions EngineerType and press Enter to add
Compared with the job title: whole phrase present = 1, all words present but not adjacent = 0.8, more than half matched = 0.5.
2Locations
BerlinMünchen+
On-site / hybrid jobs outside these cities are filtered out.
3Work mode
On-siteHybridRemote
4Remote scope
Not acceptedWithin EU, employable in GermanyAnywhere in EUWorldwide
5Seniority
AnyEntryMidSeniorLead
6Salary floor (EUR, gross/yr)70000Filtered only when a stated salary ceiling is below it; jobs with no stated salary are kept.
7Contract type
Permanent acceptFixed-term acceptFreelance acceptTemp agency exclude
8Exclude
Exclude student roles (working student, internship, thesis, …)
Companies:Type and press EnterKeywords:Sales+
SaveRevert

How each item takes effect

#ConditionWhen it does not matchAdvice
1Target rolesNo filtering, only affects the scoreList every synonymous title (AI Engineer / Applied AI Engineer…)
2–4Location, work mode, remote scopeFiltered, with a reason such as "location outside target area"If you only want remote, remove "On-site" and "Hybrid"; if you do not want remote at all, choose "Not accepted"
5SeniorityFiltered, reason "level mismatch"If unsure, leave "Any" and let the AI judge
6Salary floorFiltered only when a stated ceiling is below the floorMost German JDs state no salary, so nothing is wrongly dropped
7Contract type"Exclude" is filtered directly; anything not in "accept" is also filtered; JDs that state no contract type are keptChoose "exclude" for temp agencies (Zeitarbeit)
8Student roles, company and keyword blocklistsFilteredKeywords match whole words in the title and body, so be careful with very broad words
Newly fetched jobs are filtered by the new conditions immediately. To re-filter the jobs already in the database too, run pnpm refilter --dry-run in the project directory to see a before/after comparison, then run it again without --dry-run to apply (no fetching, no AI calls).

4Set the search criteria — where and what to search

/settings → Searchdata/config/search.yaml
127.0.0.1:3000/settings
ScheduleSearchChannelsAINotifications
Keyword groups
Each keyword group is OR-ed into one query, then queried once per city.
1engineering
AI EngineerLLM EngineerMachine Learning Engineer…+
fde_solutions
Forward Deployed EngineerAI Solutions Engineer…+
german
KI EngineerKI-Entwickler+
Group IDs use lowercase letters, digits and underscores. You can add new groups.
Location & remote
2Cities
BerlinMünchen+
Each keyword group is queried once per city.
Remote scope
NoneWithin EU, employable in GermanyAnywhere in EUWorldwide
Time window & limits
3Look-back window (hours)
Morning 24 Evening 10 Weekend 24
Max results per group per city40
Saverunner loaded (04:12)

How the query count works

Queries per channel per cycle ≈ number of keyword groups × number of cities (plus one more for remote). 3 groups × 2 cities = 6 queries. More groups give better coverage, but paid channels cost more and free sites are more likely to rate-limit you.

When a save takes effect

On save, the settings are validated with the same rules as the runner; the runner hot-reloads within a few seconds, and "runner loaded" appears next to the button. The next collection uses the new criteria, with no restart needed.

While you are here, look at the "Schedule" tab: by default cycles run at 07:00 / 17:00 on weekdays and 09:00 on weekends, in the time zone you chose in the wizard (the shipped default is America/New_York); cycles missed while the host was off are made up after it boots.

5Confirm the collection channels — which sites to pull from

/settings → Channels/sourcesdata/config/sources.yaml.env

Channels are the data sources. Switch them on and off and set budgets in /settings → Channels; see each channel's status and test it in /sources.

127.0.0.1:3000/sources
arbeitsagentur (Federal Employment Agency)OKFree · 40 this month · Test Collect now
linkedin-brightdataOKPaid · $0.00 / $5.00 · free quota 225 / 5000
greenhouse · lever · ashby …OKCompany career pages (ATS); the list is ats_boards in sources.yaml
tavilyMissing keyDiscovers career pages of new companies · needs TAVILY_API_KEY
TypeChannelsWhat to prepare
Official APIs and public company feeds, on by defaultGreenhouse, Lever, Ashby, Personio, SmartRecruiters; Federal Employment Agency (Germany market only)Nothing to do; add the companies you care about under ats_boards
Scraping, off by defaultIndeed, StepStone (Germany pack), LinkedIn guest, XINGTheir terms of service may forbid automated access. Switch one on only after reading Scraping; the wizard and /sources/registry ask you to confirm
Paid (monthly budget)LinkedIn Bright Data, Apify, JSearchPut the key in the .env file in the project directory and set a monthly budget in /settings; switch the channel off if you do not want it
Discover new companiesTavily (default) / BraveTAVILY_API_KEY, 1000 free calls per month
Needs loginLinkedIn session (off by default, uses your own account), Gmail alert emailsSee the LinkedIn session and Gmail alerts guides; optional
How to confirm a channel works: click Test on the channel card in /sources. It runs only the first query, fetches at most 5 results and parses them in memory; it does not write to the database or count against the budget, and the result is shown right under the button. Paid and login-based channels have no Test button; use "Collect now".
Fill in keys yourself: open the .env file in the project directory with a text editor and fill them in; after saving, run docker compose up -d runner so the runner picks up the new values. .env is not in git, and never paste keys into a chat.

6Set up notifications — which jobs go to your phone

/settings → Notificationsdata/config/notify.yaml
/settings · Notifications
Notify threshold70A job is pushed only when its rank value reaches this.
With T1 / T2 signal60Lower threshold for jobs with a stated salary or equity.
Max per cycle10The rest you read in the web UI.
Quiet hours
22:00–07:00
Alerts are not held back by quiet hours.

Each job pushed to Telegram looks like this, and the buttons write straight into your application records:

87 · AI Solutions Engineer
Example GmbH · Berlin · Hybrid
T1 €85–100kEnglish role
Match: LLM application experience, customer delivery…
👍 Interested🚫 Ignore📄 Tailor résumé
⭐ Save

Each cycle also ends with a summary message: new jobs, jobs that passed the filter, qualified jobs, failed sources. Receiving the summary = that cycle finished.

First time with Telegram (it is off by default): follow the Telegram guide to create a bot with @BotFather and put TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID into .env; then click Send Telegram test message in /settings → Manual actions. If it arrives on your phone, setup is done.

7Tune the ranking: signal bonuses, watched companies, views

/interestsdata/config/interests.yaml

💶 Signal words and weights

These phrases in a JD earn a bonus: T1 a stated amount, T2 equity / options, T3 salary description, T4 other. You can adjust each tier's weight and the bonus cap; after saving, rank values are recomputed immediately without calling the AI.

👀 Watched companies

Add companies you especially want to join. Any new job from them is pushed on its own, regardless of the score threshold and filters. You can add a company before it appears in the database.

👁️ Saved views

After setting filters in /jobs (score, city, remote, German requirement…), click "Save current filters as a view" and you can switch to it with one click at the top of the Today page; you can set one default view.

Every Monday at 08:00 the AI proposes adjustments based on your actions (marking interested / ignore). They appear on the /interests page and only take effect once you approve them.

8Run a cycle by hand and look at the results

/settings → Manual actions//jobs/<id>/pipeline
  1. In /settings → Manual actions click Run a cycle now (you can tick only a few channels). Live progress shows under the button: "Cycle N: x / y queries finished, z fetched". You can also run pnpm cycle:run in a terminal.
  2. After collection, the ai-worker starts scoring; scores usually trickle in within a few minutes.
  3. Open Today: new jobs are sorted by rank value, with per-source counts at the top.
  4. Click a title to open the detail page: the original JD / translation, the AI's match reasons and gaps, compensation signals, German requirement, and the "View original post" link (to record that you applied, click "Mark as applied"; opening the original post does not change the status).
  5. If you want to apply, click "Interested", then drag the status in the Pipeline board.
127.0.0.1:3000/
AllDefault viewHigh pay + Berlin
87
AI Solutions Engineer
Example GmbH · Berlin · Hybrid · 2 hours ago
T1 salaryEnglish
👍
74
Applied AI Engineer
Sample AG · München · Remote (EU)
T2 options
👍
58
ML Engineer (m/w/d)
Demo SE · Berlin · On-site · German B2
👍
Interested
AI Solutions Eng.
Applied AI Eng.
Preparing
FDE · Example
Applied
LLM Eng.
Interviewing
Offer / Closed

What to check when results look wrong

SymptomWhere to look first
A job you wanted did not appearIn /jobs turn on "Show filtered" and search for it: the top of the detail page states the filter reason (for example "location outside target area"). You can click "Keep anyway", or go back to Step 3 and loosen the conditions
Jobs of this kind were never fetchedWhether the Step 4 keyword groups contain that title; whether the Step 5 channels are healthy
Scores stay emptyWhether /me has skills or a summary; the "AI has no backlog" and "ai-worker heartbeat fresh" items in pnpm run doctor
Scores are all lowWhether the Step 2 summary and skills are specific enough; whether the Step 3 target roles cover the common titles
No notification on your phoneWhether it is quiet hours; whether the rank value reaches the threshold; send a Telegram test message from /settings

✓Every day after

  • Nothing to start: once the host is set up as in the deployment guide, Docker and the ai-worker start automatically at boot, and 3 cycles run on their own every day.
  • Watch Telegram: qualified new jobs arrive one by one; handle them with 👍 / 🚫 directly, and tap ⭐ to save ones for later.
  • When you have time, open Today or Jobs to browse what was not pushed; follow progress on the Pipeline board, which reminds you when a reply is overdue.
  • On Monday, check the AI's suggested adjustments on /interests and approve or reject them.
  • When something feels off, run pnpm run doctor; Telegram also sends proactive alerts (channel circuit-breaker, budget at 80%, ai-worker offline, etc.).

More: daily use usage · settings page details settings · deployment and health-check items deploy · configuration reference configuration