Privacy

Saykeep is a local-first application. Your audio, recordings, transcripts, and speaker labels are processed on your machine and stored only in your configured storage folder; summaries come from an OpenAI-compatible endpoint you configure — none is set by default, so summaries stay off until you choose one: a local server, or a service of your choice with your own API key. This is not a promise you have to trust — it is a property you can verify.

What stays, what leaves

You decide where summaries go. Everything else stays.

Speech-to-text, transcripts and speaker labels always run on your computer — that part is not a setting. The only choice is who computes your meeting summaries. One setting. Three positions.

Stays on this Mac — always
  • Your audio
  • Your transcripts
  • Speaker labels
  • Your files and your licence
Leaves this Mac
Nothing of yours.
Summaries are computed here too.
The transcript text, and your optional “about you” note.
Straight to the provider you chose, under your own key.
Nothing of yours.
The summary step is skipped.
Your transcript goes to a model server on this Mac that you install and run — llama.cpp, LM Studio, Ollama or anything OpenAI-compatible — at the address you enter (the default setting is llama.cpp's local address). If your Mac can run a local model, everything stays home, and Wi-Fi can be off the whole time.
Use any OpenAI-compatible service with your own API key. Saykeep sends the transcript text — plus the optional “about you” note, if you set one — and nothing more: never your audio, directly from your Mac, with no Saykeep server in between. Your key, their privacy terms, your call.
No endpoint configured? Recording, transcription and speaker labels all work in full — only the summary stays yours to write.

Flip it all you like — the left column never changes.

Every connection the app can make — including the ones that have nothing to do with summaries — is listed below and, in full, in NETWORK.md. You can change this setting at any time.

Recording is always visible — there is no covert mode

Saykeep records out in the open. Whenever meeting capture is active, an always-on indicator shows it: the menu-bar icon, a status line with elapsed time and live audio meters, and the recording card. No setting or code path can suppress it. There is no hidden, discreet, or low-visibility recording mode in the product; this is a locked, test-enforced invariant, not a UI default that could be quietly changed. The first meeting you record also opens a consent notice, because in some places every participant must agree before you record (see the FAQ). Obtaining that consent is your responsibility; Saykeep’s job is to make recording impossible to miss.

You decide what’s kept

Saykeep doesn’t hoard recordings. Once a meeting is transcribed, it can delete the audio for you — asking after every meeting, or automatically cleaning up audio older than a number of days you choose. The guard rail: audio is deleted only when a successful transcript exists, and transcripts and summaries are never deleted automatically. You can also delete any meeting entirely from the recordings browser, or erase your Saykeep content — recordings, models, settings — in one guarded step (you type ERASE to confirm). It’s all plain files in your own folder; what stays is up to you.

The short version

During normal use, nothing leaves your machine except — if you enable meeting summaries — the transcript text sent to the endpoint you configure — and none is set out of the box, so summaries are off until you choose one (point it at a local server on 127.0.0.1 and it stays on your machine). On first run, the speech models download once (full list below).
No accounts, no login, no cloud sync. There is no Saykeep server.
No telemetry, analytics, crash reporting, or usage tracking in the app. None of it is in the code. This website is a separate matter — it does measure visits, and says below exactly how.
No license phone-home. License keys are verified offline against a compiled-in public key — there is no activation server and no network call.
The diagnostics export writes a local file you choose whether to share; it makes no network call and it redacts secrets.
Every connection the app can make
The summary endpoint you configureYour meeting transcript text when a summary is generated, and a model-list request (nothing of yours) when you press Test in Settings. By default the endpoint is a server on your own Mac; if you point it at a cloud API, your transcript goes there by your choice.
huggingface.coThe one-time speech-model download on first run — and never again: a model that is already on disk loads from disk, without contacting huggingface.co. (0.4.0 sent one small metadata request each time a model loaded — the model's name and a library version, no token; found while fact-checking this page, fixed in 0.4.1.)
models.saykeep.appOne ~30 MB speaker-label model, downloaded once, anonymously, and never again.
saykeep.appThe update check: at most once a day, only if you said yes at setup (Yes is pre-selected). It carries your Saykeep version and nothing else, and installs an update only when you consent.
huggingface.co/apiOnly if you add a Hugging Face token in Settings and press Test.

The one exception during normal use — the update check

Saykeep can check whether a newer version exists. This check is off by default and never opens a connection unless you turn it on. When enabled, it makes at most one request per day to saykeep.app for a small signed file listing the current version. That request carries a standard User-Agent naming the app and its version (Saykeep/<version> Sparkle/2.9.6) and nothing else — no machine id, no account, no licence key, no cookie, no identifier that could single you out or link two requests together. If you choose to install an update, Saykeep downloads and installs it only on your consent — never silently, and never while a meeting is recording. Leave the check off and Saykeep makes no update calls at all.

How to verify

1. Watch it live. On macOS run Little Snitch; on Linux run sudo ss -tunp | grep saykeep or lsof -i -a -p <pid>. With summaries pointed at a local endpoint and models already downloaded, you will see zero outbound connections during normal use.
2. Inspect your config. Settings → Advanced → “Export diagnostics…” shows your resolved LLM endpoint URL (credentials redacted) so you can confirm exactly where a summary would go.
3. Read the full list below. Every connection the app can make is enumerated in the annex — it is the same file we keep in the source tree, reproduced below in full — byte for byte.
Two tests you can run yourself

Block the app in your firewall

Little Snitch, LuLu, a corporate proxy — take your pick. Saykeep keeps working: transcription and speaker labels never needed the network.

Turn Wi-Fi off

Once the models are on disk, record an hour-long meeting with the network off. The transcript and the speaker labels come out the same — the simplest audit there is.

Codename note: the document below is Saykeep’s in-repo trust artifact, reproduced verbatim so it stays a single source of truth. It refers to the app by its internal codename, “Whisperer” — that is the same application as Saykeep. Every socket, host, and guarantee below applies to Saykeep exactly as written.
# NETWORK.md — every connection Whisperer can make

Whisperer is a **local-first** application. Your audio, recordings, transcripts, and summaries are
processed on your machine and stored only in your configured storage folder. This document is an
exhaustive, behavior-verifiable inventory of **every** network connection the app is capable of
making — nothing else happens. You do not have to take our word for it: run a network monitor
(Little Snitch on macOS, `lsof -i` / `ss -tunp` on Linux, a firewall log on Windows) and confirm the
app opens only the connections listed here.

*This file is kept in sync with the code. If you find an outbound connection not listed here, that is
a bug — please report it.*

## The complete list

| # | Host | When | What is sent | Contains your data? | Avoidable? |
|---|------|------|--------------|---------------------|------------|
| 1 | **The LLM endpoint you configure** (`llm.endpoint_url`; **no default** — until you set one, summaries are off and nothing is sent) | When a meeting summary is generated, and a `/models` health probe when you test the connection | The meeting **transcript text** (for summarization) or a models list request (for the probe) | **Yes — transcript text.** It goes only to the endpoint *you* set. Out of the box no endpoint is set, so nothing is sent at all; point it at a server on your own machine and nothing leaves it. If you point it at a cloud API, your transcript goes there **by your choice** | Yes — disable summaries / leave the endpoint unset. Meetings still record and transcribe fully locally |
| 2 | **huggingface.co** (and its model CDN) | First time a model is needed: the Whisper-family speech-to-text models, and — only if you opt in to the Parakeet engine in Settings — the Parakeet-TDT-0.6B-v3 weights from the Hub repos `mlx-community/parakeet-tdt-0.6b-v3` (macOS) or `istupakov/parakeet-tdt-0.6b-v3-onnx` plus the `istupakov/silero-vad-onnx` segmenter it needs for long audio (Linux/Windows), each pinned to a fixed revision. Diarization models come from here **only if you added a Hugging Face token** (optional fallback — the default path is row 5) | A standard model **download** request; for the diarization models, your Hugging Face **token** in an `Authorization` header (only if you provide one) | No — these are downloads *to* your machine. Your token authenticates the download; no audio/transcript is uploaded | Partly — once models are cached locally they are not re-downloaded. Diarization is optional |
| 3 | **huggingface.co/api** (token check) | Only when you click "Test" on the optional Hugging Face token field in Settings ▸ Diarization | Your HF token in an `Authorization` header, to verify it can access the diarization model | No | Yes — it only runs when you test the token |
| 4 | **saykeep.app** (the update appcast) | At most once/day, only if you enabled updates (you are asked once at first run) | A GET of a static XML file. The request carries a standard User-Agent naming the app and its version — `Saykeep/<version> Sparkle/2.9.6` — and nothing else: no machine id, no account, no licence key, no cookie, no identifier that could single you out or link two requests together. Downloads and installs the release only when you consent | Your Saykeep version, and that a copy of Saykeep is checking. Nothing else about you or your machine | Turn it off in Settings ▸ General ▸ Check for updates and no request is ever made |
| 5 | **models.saykeep.app** (the speaker-label model mirror) | Once, only if speaker labels (diarization) are enabled and the model is not yet on disk — shown in the "Getting Saykeep ready" download window | An anonymous GET of a static ~30 MB tarball (`diarization-v1.tar.gz`) — no machine id, account, token, or any identifier (the request carries only a generic HTTP-client name). The file is checksum-verified before it is installed | Nothing about you or your machine is sent | Yes — turn speaker labels off (Settings ▸ Diarization) and no request is ever made; once installed it is never re-downloaded |

*Row 2 note (2026-09-05): 0.4.0 also made one small metadata request to huggingface.co each time
a cached Whisper model loaded (at launch, when the meeting model loaded, and on every switch
between the two) — the loader was handed a Hub repo name instead of the on-disk path. Found and
proved on the wire while fact-checking the website
(`docs/ceo/evidence/2026-09-04-hf-launch-metadata-request.md`), disclosed here the same day, and
**fixed in 0.4.1**: a cached model now loads from its local snapshot with no request (wire proof:
three requests → zero across load → meeting → dictation). It carried no token and no identifier.
If you are still on 0.4.0, update, or block huggingface.co — the model loads from disk either way.*

*Row 4 note: this check is off by default and never opens a socket while off.*

*Row 4 honesty note (corrected 2026-08-30): this row previously claimed the check sends "no version
string". That was wrong — the update engine (Sparkle) sends its standard User-Agent, which names the
app version, and nothing in the app overrides it. The row now states what actually goes over the
wire. Verify it yourself with Little Snitch or `tcpdump` per the "watch it live" section below —
that is the point of this document.*

*Row 5 note: this is the token-free replacement for the Hugging Face diarization download (rows 2–3). Those two rows now apply only if you chose to add a token yourself.*

That is the entire list.

## What Whisperer **never** does

- **No telemetry, analytics, crash reporting, or usage tracking.** No analytics SDK is linked into
  the app — and you can hold us to it from the outside: the rows above are everything, and the wire
  is checkable.
- **No accounts, no login, no cloud sync.** There is no Whisperer server. Nothing is uploaded.
- **Your recordings, transcripts, and summaries are never sent anywhere** except the transcript text
  to the LLM endpoint *you* configured (row 1). They are stored only in your storage folder.
- **No license phone-home.** License keys are verified **offline** against a compiled-in public
  key — no activation server, no network call, ever.
- **The diagnostics export** (Settings → Advanced) writes a **local file** you choose whether to
  share; it makes no network call, and it redacts secrets.

## Local helpers are not network connections

Whisperer runs some local subprocesses — `pw-record` (Linux system audio), the bundled
`whisperer-syscapture` (macOS system audio), `osascript`/`caffeinate` (macOS), and your OS's
file-reveal command. These are **on-device** and open no network sockets.

## How to verify

1. **Watch it live:** macOS → Little Snitch; Linux → `sudo ss -tunp | grep whisperer` or `lsof -i -a -p <pid>`; Windows → Resource Monitor / firewall log. With summaries pointed at a local endpoint and models already cached, the only connection you will see during normal use is the **update check** — at most one request a day to the Saykeep appcast, and only if you said yes to it at first run (row 4). You will see your Saykeep version in that request's User-Agent, and nothing else identifying. The one other connection you may see is a **single** download of the speaker-label model the first time you open Saykeep with speaker labels enabled (row 5); it never repeats once the model is on disk. Turn the update check off in Settings and — once that one-time model download has happened, or with speaker labels off — you will see **zero** outbound connections.
2. **Inspect your config:** Settings → Advanced → "Export diagnostics…" shows your resolved
   `llm.endpoint_url` (credentials redacted) so you can confirm where summaries would go.
3. **Hold us to the contract:** Whisperer is closed-source, so instead of "read the code," this file
   *is* the auditable surface — the only things that ever open a socket are the rows above. If you
   ever observe a connection that is not listed here, that is a bug; report it and we will treat it
   as one.

Verify it yourself — the walkthrough

macOS — Little Snitch

Install Little Snitch (or its free companion, Little Snitch Mini), open its network monitor, and use Saykeep normally — dictate, record a meeting, generate a summary against your local endpoint. What you will see, once the models are on disk: nothing. No account, no telemetry, no licence check — unless you turned the daily update check on, or pointed summaries at a remote endpoint. Every one of those is listed in the table above.

Little Snitch — Saykeep
Outbound connections: 0
during a 42-minute recorded meeting

Linux — ss / lsof

$ sudo ss -tunp | grep saykeep
# (no output — zero sockets)
$ lsof -i -a -p $(pgrep saykeep)
# (no output — zero sockets)

If you ever see a connection that isn’t on the published list, that’s a bug — tell us.

This website is a separate thing — and it does measure visits

Everything above is about the Saykeep app on your machine: no analytics, no telemetry, no crash reporting, and you can watch the wire to prove it. This page — saykeep.app — is an ordinary website, and websites count visitors. We would rather tell you plainly than have you find it in a network monitor.

Cloudflare Web Analytics counts page views on every visit. It sets no cookies, stores nothing on your device and builds no profile of you: Cloudflare processes your IP address to derive an approximate country and does not retain it. It is how we know whether anyone found the site at all. There is nothing to opt out of, because nothing is left on your machine.
Google Analytics 4 runs only if you accepted it in the banner. Until you do, no Google script is fetched and no Google cookie exists — the tag is not on the page at all. If you accept, Google sets analytics cookies (names beginning _ga) and receives your visit — pages viewed, device type, approximate location from your IP — on our behalf. We switch off Google signals and ad personalisation, so it is measurement only, never advertising. Google is a US company, so data may be processed outside the EU under its standard contractual clauses.
Declining costs you nothing — the site is identical either way, and we don’t ask again on every page. Your answer is kept in your own browser (localStorage, key sk-consent) and is never sent to us. Change it whenever you like from the “Cookies & analytics” link in the footer: withdrawing stops the tag and expires the _ga cookies. An ad-blocker, or JavaScript off, keeps Google out too.

The two never meet. No analytics of any kind runs inside the app, and nothing on this website can see what you record, dictate or transcribe. Checkout is the third-party exception: it runs on Lemon Squeezy’s systems under Lemon Squeezy’s privacy policy.