Skip to content
Public template

About

Every conversation, one clear line. A self-hosted tracker that reads your mailbox (and LinkedIn) every morning and tells you who is waiting for whom.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Threadline

Every conversation, one clear line.

License: MIT

Threadline is a private, self-hosted tool for the conversations you are keeping alive: who you are talking to, where each conversation stands, who owes whom a reply, and what to do next.

Once a day it reads your new LinkedIn messages and your mailbox (Gmail, Outlook.com, iCloud, Yahoo, Fastmail or any IMAP mailbox), plus, if you use them, your Outlook or Google calendar, your one-to-one Telegram chats, your Slack direct messages and your work Teams chats. It throws away the noise and groups what is left into one timeline per person. An AI step, run by Claude on your own subscription, then judges each person: the status, who is waiting on whom, the next action and when it is due. You see the result on a private dashboard, and a short summary e-mail reaches you every morning, when a mailbox that can send it is connected (for example Gmail with an app password). one timeline per person. An AI step, run by Claude on your own subscription, then judges each person: the status, who is waiting on whom, the next action and when it is due. You see the result on a private dashboard, and a short summary e-mail reaches you every morning, when a mailbox that can send it is connected (for example Gmail with an app password).

Threadline started as a job-search tool and is being generalised. You pick a preset — job search, sales outreach, fundraising, freelance clients or general networking — or define your own categories. See docs/customising.md.

The people list of the Threadline dashboard: one row per person with their category, last contact, status, who is waiting on whom, the next action, its due date and the signal. One row is marked Overdue.

Try the demo

Live demo: https://demo.threadlineapp.com

See the dashboard with made-up data before setting anything up. You need Node.js 22 or newer; no accounts and no settings. The green Code → Download ZIP button on this page is all the download you need (if you already installed Threadline, part 1 of the guide, use its threadline folder instead). Then in a terminal, inside that folder:

cd frontend
npm install
npm run demo

npm install may print warnings about vulnerabilities or install scripts: they concern developer tools only and can be ignored. Then open http://localhost:5173. You are signed in as an invented owner who does sales outreach. You can correct people, answer the review questions and change the categories on the Settings page; nothing is saved, and reloading the page starts again from the same made-up data. A demo build is made with npm run build:demo; a normal build never contains the demo. To put the demo online as its own website, follow docs/demo-site.md.

What it looks like

The home page, and the questions the AI step was unsure about:

The home page: the demo notice, the meetings coming up, five counters such as Actions for me and Overdue replies, and a grid of how many people in each category have each status. The To review page: three questions the assistant is unsure about, such as whether two names are the same person, each with Yes and No buttons.

The same people grouped by organisation, so you can see where you stand with each one as a whole:

The Organisations page: one row per organisation with how many people are there, badges such as Overdue: 1 and Your turn: 1 showing where things stand with them as a whole, and the most recent contact.

One person's page with every message in one timeline, and the people list on a phone:

One person's page: the e-mails and the meeting exchanged with Maya Lindqvist in one timeline, beside a summary of what is going on, the next action and its due date, and your own notes on her. The people list on a phone: one card per person with the same details as the table, and a menu bar along the bottom of the screen.

Every picture comes from the demo, so every name, company and address in it is made up.

What it is, and what it is not

  • It is a personal tool: one copy per person, on accounts you own.
  • It is read-only. It never writes to any of the places it reads, and never sends anything to anybody except the one summary to you.
  • It is not a CRM, an outreach or auto-reply tool, or a hosted service. Never run one copy for several people: the database has a single owner and no separation between users.

Who it is for

Anyone who juggles many one-to-one conversations across LinkedIn and e-mail and keeps losing track of who is waiting for an answer: job seekers, founders raising money, freelancers, people doing sales or partnership outreach.

How it works

LinkedIn messages ─────────┐
Gmail, Outlook or any      │
  IMAP mailbox ────────────┼─▶ collect ─▶ drop noise ─▶ group by person ─▶ Claude judges each person
Outlook or Google calendar ┤                                                        │
Telegram, Slack and        │                                                        │
  Teams chats ─────────────┘                                                        │
                                                                                    ▼
                                                           your Supabase database (checked before saving)
                                                                        │
                                                        ┌───────────────┴───────────────┐
                                                        ▼                               ▼
                                               private dashboard              morning summary e-mail

Everything runs once a day on GitHub Actions in your own private copy, on your own Claude subscription, so your computer can be off. There is no server of its own: the backend is a command-line tool (tracker) that a Claude Code session runs step by step, following .claude/commands/daily-run.md. A Claude cloud routine can run the same recipe instead (the alternative route, for Outlook-only set-ups). The design is described in docs/architecture.md.

What you need

Item What it is used for Cost
A computer with macOS, Linux or Windows (10 or 11) Running the set-up once. After that it can stay off –
A paid Claude plan (Pro, Max or Team) The Claude Code session that runs everything and does the judging. The daily run uses part of your plan's usage limits Your existing subscription; no separate API key
A GitHub account Your private copy of the code, and GitHub Actions, which starts the run every day. The installer adds the GitHub CLI (gh), which signs you in, makes your copy and saves your settings Free (2,000 Actions minutes a month for private repositories at the time of writing; a month of runs uses about 150–300)
A Supabase account (sign up with Continue with GitHub) The database and the dashboard sign-in. The set-up creates the project after one click on Authorize. Its built-in sign-in e-mail only reaches the address the Supabase account was registered with (or members of its organisation) unless you set up your own sending service (custom SMTP) Free plan
At least one mailbox: Gmail, Outlook.com/Hotmail, iCloud, Yahoo, Fastmail or any IMAP mailbox The mail that is read (read-only). Gmail and the others need an app password, which also sends you the summary; Outlook needs a one-time sign-in Free (Fastmail: a paid plan above Basic)
Optional: a LinkedIn developer app Reading your LinkedIn messages; only for members located in the EEA or Switzerland (see below) Free
Optional: a Microsoft personal account The Outlook calendar, read with the Outlook sign-in Free
Optional: a Google Calendar Your meetings, read through the calendar's secret address: no Google sign-in. Any other private .ics address (iCloud, Fastmail, Proton) works too Free
Optional: a Telegram account Reading your one-to-one Telegram chats with people (not groups, channels or bots). You make your own small app at my.telegram.org and log in once; the set-up walks you through it Free
Optional: a Slack workspace Reading your Slack direct messages, through a small read-only app you create in it with one link Free
Optional: a work or school Microsoft account Reading your Teams chats. Some companies block this; then only their IT admin can allow it Free
Optional: Node.js 22 or newer Only the manual way of switching on the dashboard's Refresh now button; also needed for the demo above Free
Optional: an Azure account Only to use your own Microsoft application instead of the public one Free
Only for the Claude cloud route: a Gmail account connected to Claude Sending the summary from a Claude cloud routine, when you read Outlook alone Free

No other paid service is needed, unless you choose a paid mailbox such as Fastmail. The dashboard is shared, at https://app.threadlineapp.com: your data stays in your own Supabase database, and only you can sign in. Setting it up takes about 20 minutes in total, making the accounts included; LinkedIn comes later, in about 5 minutes. The first summary e-mail follows about ten minutes later, if you have a mailbox that can send it, for example Gmail with an app password.

LinkedIn: only in the EEA and Switzerland

LinkedIn's official API for exporting your own messages (Member Data Portability) is available only to members located in the European Economic Area and Switzerland. Elsewhere, leave LinkedIn out: everything else works without it.

LinkedIn's copy of your messages also runs one to two days behind, so a LinkedIn message reaches Threadline a day or two after you receive it. Mail and calendar entries are read as they are at the moment of the run.

Connecting LinkedIn takes about five minutes the first time, after the rest of the set-up: you make a small developer application on LinkedIn's pages and uv run tracker setup linkedin walks you through it. When the key expires, getting a new one is one command and one click on LinkedIn's Allow button.

Set it up

The easy way: let Claude do it. You already need a Claude plan, and Claude can run the whole set-up for you: install the tools, make your private copy, start the guided set-up and check the result, while you create the accounts and click where it says. Keys go into a page in your browser, never into the chat. See docs/setup-with-claude.md: it is one sentence to paste into the Claude app.

By hand: create a free GitHub account, then a free Supabase account with Continue with GitHub. Then paste one line into a terminal.

On macOS or Linux:

curl -LsSf https://raw.githubusercontent.com/roccoterr97/threadline/main/install.sh | sh

On Windows, in PowerShell:

irm https://raw.githubusercontent.com/roccoterr97/threadline/main/install.ps1 | iex

What happens next:

  1. The installer adds uv, the GitHub CLI and Claude Code if they are missing (and Git, on Windows), signs you in to GitHub in the browser, makes your private copy called threadline, downloads it to ~/threadline, and starts the guided set-up. The first time, it may also download Python itself, which can take a few minutes and needs the internet. Pasting the line again carries on where it stopped.
  2. The set-up runs 11 steps and asks only what you must do. You click Authorize on a Supabase page and type the code it shows; it then creates your project (or reuses the one named threadline), builds the database, makes your dashboard login for your Supabase account's address and switches sign-ups off. It asks your e-mail address, tells the provider from it, and opens the page where you make the app password (Outlook signs in with Microsoft instead). The rest takes the usual answer, each said in one line with the command that changes it: your computer's time zone, the usual categories, the shared dashboard (with your personal link) and 07:00 for the daily run. uv run tracker setup --ask-everything asks every question instead.
  3. Last, you click Authorize on a Claude page: the key that lets GitHub use your plan goes straight to GitHub (on some computers you paste it instead). It saves your settings on GitHub, starts the first run and watches it for two minutes (so a problem is said at once), and checks every connection. Its last screen names your personal link and the e-mail address that signs in, and lists every choice with the command that changes it. The first summary e-mail arrives about ten minutes later, then every day at the time you chose (this needs a mailbox with an app password; with Outlook alone the set-up tells you the options).
  4. LinkedIn is left for later, to keep the set-up short: the last screen names its command (uv run tracker setup linkedin, about 5 minutes, for profiles located in the EEA or Switzerland), and once the first summary is in, the dashboard and the morning e-mail offer it too. LinkedIn, Google Calendar, Telegram, Slack, Teams, the dashboard's Refresh now button and the Claude cloud route are optional extras: uv run tracker setup extras, any time. Your own copy of the dashboard on Netlify is optional too (uv run tracker setup dashboard). Refresh now also switches on the on-time morning start: GitHub often starts its scheduled runs hours late, so your Supabase project starts the daily run at your time instead, and GitHub's own schedule stays as a backup.

Add --browser to any uv run tracker setup command to have the questions asked on a page in your web browser instead of in the terminal: keys go into hidden fields, and the page is served to your computer only.

docs/setup-your-accounts.md walks through every step, with what you should see and a short list of the words it uses (IMAP, app password, secrets…). To come back to the set-up later, open a terminal and type cd ~/threadline/backend, then uv run tracker setup.

Day-to-day operation — what to do when something fails, renewing the LinkedIn key and the yearly Claude key, redoing the Microsoft sign-in, changing the time, pausing and resuming — is in docs/operations.md.

Command reference

Run every command from the backend/ folder as uv run tracker …. uv run tracker --help, or --help after any command, shows the same information.

Setup and checks

Command What it does
tracker setup The guided set-up: every unfinished core step in order, then a final check of every connection. It asks only what you must do (the Supabase code, your e-mail address and its app password) and takes the usual answer for the rest, listing every choice at the end with the command that changes it. Running it again carries on where it stopped
tracker setup --ask-everything The same, asking every question instead of taking the usual answers
tracker setup extras The optional steps, in order, each skippable: linkedin, google-calendar, telegram, slack, teams (only named there: run it by name), refresh and cloud
tracker setup <step> Run one step alone, even if it was done before, asking all its questions. The core steps, in order, are supabase, encryption, database, login, categories, timezone, mailbox, microsoft, dashboard, schedule and github; the extras are linkedin, google-calendar, telegram, slack, teams, refresh and cloud
tracker setup [<step>] --browser The same, asked on a page in your web browser instead of the terminal; what is said and asked is still shown in the terminal, answers never are
tracker setup supabase Sign in to Supabase in the browser (or paste an access token), create your project (or reuse one), and save its address and keys; the sign-in is never saved. Typing an existing project's address and keys is also offered
tracker setup mailbox Choose the mailbox to read (the full run asks only the address and tells the provider from it); for Gmail and other IMAP mailboxes, check an app password live and store it encrypted (for a custom provider it also asks for the sending server, SMTP host and port)
tracker setup schedule Write the daily time and time zone into the GitHub workflow, and into the database for the on-time morning start; commit and push the workflow only after a yes
tracker setup github Save your settings and the Claude key as your repository's Actions secrets and variables (with gh), or list the names to add by hand; settings you cleared locally are removed from GitHub too. With gh it then switches the workflow on and starts the first daily run after a yes (the full run does both without asking). It only ever uses your own private copy
tracker setup google-calendar Paste your Google Calendar's secret address in iCal format (the step shows the three clicks to copy it); it is read once to check it, saved, and sent to GitHub without a question. Enter skips
tracker setup telegram Make your own small Telegram app at my.telegram.org (the step shows how), then log in once with your phone number and the code Telegram sends; saved and sent to GitHub without a question
tracker setup slack Create a small read-only Slack app from one prefilled link, install it and paste its User OAuth Token; checked, saved and sent to GitHub. Run it again to add another workspace
tracker setup teams Sign in to Teams with a work or school account (its own sign-in, apart from Outlook) and switch Teams chats on, here and on GitHub. Asks nothing else
tracker setup refresh Switch on the dashboard's Refresh now button: deploy the refresh-now function and its settings through Supabase's Management API, with a GitHub key that can only start your workflow. It also switches on the on-time morning start: a timer in your database that starts the daily run at its time, every 15 minutes checking whether it is due
tracker setup cloud The Claude cloud route, the alternative to GitHub: what a Claude cloud routine needs
tracker doctor Check that every account and setting is in order
tracker sample load Write made-up sample records, their dates moved so they look as fresh today as on the day the sample was written; running it twice changes nothing
tracker sample clear Remove the made-up sample records, leaving real data untouched

Collecting

Command What it does
tracker microsoft login Sign in to the mailbox once; the key then renews itself
tracker microsoft forget Remove the stored mailbox key, so the next run needs a new sign-in
tracker collect linkedin [--since YYYY-MM-DD] [--show-folders] Read the LinkedIn archive and store the recent conversations; --show-folders also prints the distinct LinkedIn folder values
tracker collect email [--since YYYY-MM-DD | --refresh] Read every mailbox you set up (Outlook, Gmail…) and store the conversations worth keeping; --refresh reads only what is new since the mailboxes were last read successfully
tracker collect calendar Read the Outlook calendar and store the meetings with other people
tracker collect slack [--since YYYY-MM-DD] Read the direct messages of every Slack workspace you set up
tracker collect teams [--since YYYY-MM-DD] Read your Teams one-to-one and group chats and store the ones written in recently
tracker collect telegram [--since YYYY-MM-DD] Read your one-to-one Telegram chats with people and store the recent ones
tracker collect google-calendar Read the Google calendar from its secret address and store the meetings with other people
tracker collect all [--since YYYY-MM-DD | --refresh] [--record] [--run ID] Read every source at the same time, then store them one after the other. --record also records each source as its step of the run and carries on past a source that failed; with --refresh it records into the refresh that is open, never the daily run — this is the command the daily run uses
tracker people list Show everybody the collectors have found, most recent first
tracker people merge Join the records you have confirmed are one person
tracker people link Ask about records that look like one person or one opportunity
tracker people tidy people merge, then people link, as one command; if one fails its code is printed and the other still runs — this is the command the daily run uses
tracker people untangle Split up records that were really a shared sender, such as a hiring system

Without --since, the first run reads the last 30 days and later runs continue from the last successful run.

Assessment

Command What it does
tracker ai export [--limit N] [--batches DIR] [--results DIR] [--record [--refresh]] [--run ID] Write one file per group of people who still need a verdict, and make the directory their verdicts go in. --record also records the assess step of the run when there is nobody to assess; with --refresh, into the refresh that is open rather than the daily run
tracker ai import [--results DIR] [--batches DIR] [--record [--refresh]] [--run ID] Check the verdict files, save the ones that pass, and delete the files it applied. --record also records the assess step of the run: people assessed, and how many were sent to review; with --refresh, into the refresh that is open rather than the daily run
tracker ai status [--results DIR] [--batches DIR] Show what is waiting: people to judge, files to import, questions to answer
tracker ai clean Remove every exchanged file, including batches that were never answered

The judging itself happens in Claude Code with the /assess recipe (.claude/commands/assess.md); no command calls an AI service.

Profile

Command What it does
tracker profile choose [--preset NAME] [--guide PATH] Pick a preset, keep the suggested categories you use and add your own; saved to the database, and the assessment guide is rebuilt; --guide PATH chooses where it is written (default docs/assessment-guide.md)
tracker profile check [--file PATH | --preset NAME] Check your profile (or a shipped preset) and print its suggested categories and stage labels; changes nothing
tracker profile apply [--file PATH] [--categories] [--guide PATH] Save the stage labels and suggestions and rebuild the assessment guide from your categories (written to docs/assessment-guide.md, or to --guide PATH); --categories also replaces your categories with the file's list

See docs/customising.md.

The daily run

Command What it does
tracker run start [--trigger github|cloud|refresh|mac|manual] [--prepare] [--force] Open today's run and print its identifier (default trigger: cloud). --prepare goes on to tracker healthcheck and tracker profile apply and ends with ready: yes or ready: no — this is how the daily run opens. It refuses while another run of the same kind is going; --force starts anyway and closes that one as interrupted
tracker healthcheck The daily run's own check of the configuration, the database and the secret store, run by run start --prepare. By hand, tracker doctor says more
tracker run step --step STEP --result success|failed [--found N] [--new N] [--error-code CODE] [--error-detail TEXT] [--run ID | --refresh] Record what one part of the run did. STEP is collect_linkedin, collect_email, collect_calendar, collect_google_calendar, collect_telegram, collect_slack, collect_teams, assess or summary_email. --refresh records into the refresh that is open rather than the daily run
tracker run finish [--run ID | --refresh] [--clean] Close the run with the status its steps add up to. --refresh closes the refresh that is open rather than the daily run. --clean then removes the exchanged files, as tracker ai clean does — this is how the daily run closes
tracker summary build [--out PATH] [--run ID] [--send-again] Write the morning summary to a file, ready to send, and say how it is sent (delivery: smtp or gmail_connector). When another daily run already sent today's summary it writes nothing, records the e-mail as skipped and prints summary skipped · …; --send-again builds it anyway
tracker summary send [--file PATH] [--run ID] [--send-again] Send that file from your own mailbox by SMTP, exactly as built and only to your configured recipient, and record the step. Like summary build, it skips the e-mail when today's summary already went out with another daily run, unless given --send-again

The workflow .github/workflows/threadline-run.yml runs /daily-run every day, or by hand with mode daily or refresh (new messages only, no e-mail). Without its secrets it finishes green and does nothing. With the on-time morning start switched on, your Supabase project starts the daily run at its time, and a run started by GitHub's own (often late) schedule stops by itself when the day's run already started (docs/refresh-now.md).

Customising

Choose your categories on the dashboard's Settings page (it asks before removing one), or in the terminal with tracker profile choose. Categories, presets and wording are explained in docs/customising.md. What every status and waiting-on value means is defined in docs/assessment-guide.md.

Privacy and security

  • Everything lives in accounts you own: your database, your GitHub repository and your Claude subscription. The shared dashboard page only shows what your own database sends it after you sign in. Nothing is sent to the project's authors. The Claude key goes only into your own repository's secrets; Threadline never stores it.
  • The mailbox and calendar are read with read-only permissions. Nothing is ever written to LinkedIn, the mailbox, the calendar or any chat. The Google calendar's secret address, the Telegram login and the Slack keys are kept like passwords: in .env and as GitHub secrets, never in a log.
  • Message text is treated as untrusted input. Only a restricted helper with no shell and no network reads it, and its answers are validated strictly before anything is saved, so an e-mail saying "ignore your instructions" cannot act.
  • Conversations judged to be noise keep no subject and no text. The files that carry message text to the AI step are deleted once their verdicts are saved.
  • The dashboard's public key reads nothing on its own: the database only answers the one signed-in owner.
  • Notes you type on a person's page are yours alone: they are never sent to the AI step and never put in the summary e-mail.

The full model — what is stored where and how untrusted text is contained — is in SECURITY.md. Report vulnerabilities privately through GitHub, as described there.

Folder map

Folder What is in it
backend/ The Python jobs and the tracker command-line tool
backend/tests/ Unit tests; they never touch the network
frontend/ The dashboard (Vite, React, TypeScript)
website/ The public website: what Threadline is, the guided set-up, the way to your dashboard (Vite, React, TypeScript)
supabase/migrations/ The database structure and its access rules, one file per change
.claude/ The daily-run and assessment recipes, the assessor helper and the session's permissions
.github/workflows/ The checks every change must pass, and threadline-run.yml, the daily run
docs/ Setup, operations, architecture, the assessment guide, the coding standards and the README's pictures (docs/images/)

Development

cd backend
uv sync
uv run ruff check .
uv run pyright
uv run pytest -q

cd ../frontend
npm ci
npm run lint
npm run typecheck
npm test
npm run build

The home-screen icons in frontend/public/icons/ are drawn by npm run icons from the logo and the colour tokens; run it again after changing either, and commit the new files.

All of these run in CI on every push and pull request. See CONTRIBUTING.md before opening a pull request, and CLAUDE.md for the engineering standards.

Licence

MIT © 2026 roccoterr97

About

Every conversation, one clear line. A self-hosted tracker that reads your mailbox (and LinkedIn) every morning and tells you who is waiting for whom.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages