memoria
Join the private beta

claims → proofs

Verify

For the reader who assumes marketing is lying. Each claim maps to something you can check yourself: what runs where, a 30-second test, the complete list of network calls, and the one cloud feature, disclosed.

claim 1 · searches between your apps

Architecture

Sources are read into a local index. A local model reads the index and answers. The only component that can leave the machine is dashed: it is off until you switch it on.

Memoria architecture Sources on the left flow into a local index on disk, then a local model, then answers. Every arrow is read-only. A dashed line to the Claude API is off by default and needs an explicit toggle with a visible indicator. your machine Gmail Google Drive Slack Notion · GitHub browser history local files read-only · GET / search only local index SQLite + vectors ~/.memoria/ on disk local model Qwen3 · GGUF · CPU/GPU runs offline answers with the path, the source, the why audit.jsonl every read, appended Claude API OFF by default explicit toggle visible indicator your own key
Every arrow into the index is a read. Solid lines are the default configuration. The dashed line exists only when you switch it on.
index
SQLite FTS5 + sqlite-vec (multilingual-e5-small, int8, CPU) in ~/.memoria/. Delete the folder, the index is gone.
local model
Qwen3 1.7B (4B upgrade), Q4_K_M GGUF, in-process. Downloaded once on click, sha256-pinned. Search works without it at a lower tier.
remote sources
Queried live through read-only APIs. Shown, not archived. Each labelled when it answers.
audit
Every access appended to ~/.memoria/audit.jsonl: counts and redacted parameters. Blocked outbound attempts too.

claim 3 · local

The airplane-mode test

Thirty seconds, no tools.

  1. Turn Wi-Fi off. Unplug Ethernet.

    macOS: menu bar → Wi-Fi → off

  2. Open Memoria and search for something in a local folder or in your browser history.

    default hotkey ⌘⇧Space

  3. Results appear. The status bar names the sources that answered; the brain chip reads local.

    cloud sources report offline, local ones answer

  4. Open the result. It is the file on your disk or the page in your history.

    Enter reveals in Finder, or opens the browser

  5. Still offline, open the audit log. Every read is there, with counts, no payloads.

    Settings → Privacy → Audit log → Open file

Second opinion: ask the OS which processes hold connections while you search. Empty is the expected result.

$ lsof -i -P -n | grep -i memoria
# (no output: no open sockets while searching with cloud features off)
$ tail -n 3 ~/.memoria/audit.jsonl
{"ts":"…","source":"localfs","action":"search","count":3,"params":{"roots":2}}
{"ts":"…","source":"history","action":"search","count":25,"params":{"profiles":1}}
{"ts":"…","source":"network","action":"blocked-outbound","reason":"paranoid"}

Little Snitch or LuLu show the same. Paranoid mode (Settings → Privacy) refuses everything at the network layer, downloads included.

claim 3 · local · the complete list

Every network call this app can make

One network layer. Each call declares a purpose and its allowed hosts; a process-wide guard refuses anything else, from our code or a dependency, and logs the refusal. A static test fails the build if any file outside that layer touches the network. This table is the registry, in words.

hosts the engine can ever contact

api.anthropic.com · api.github.com · api.notion.com · gmail.googleapis.com · huggingface.co · oauth2.googleapis.com · slack.com · www.googleapis.com

plus, from the desktop shell and only on click: the signed update feed.

Runtime calls

#PurposeEndpointsTriggerPayload (outbound)Opt state
1 Cloud LLM (Anthropic): parse memory, translate keywords, rerank shortlist
POST https://api.anthropic.com/v1/messages
A search runs with the cloud brain ON (localOnly: false, OFF by default; the first enable shows the exact payload and must be acknowledged) AND an Anthropic key is present AND the hourly deep-search meter allows AND paranoid mode is off. The outbound indicator is shown before the search.
parse-memory: claude-haiku-4-5, ≤1024 output tokens, once per search, only when no local brain is installed or its output failed validation. Field: memory
translate-keywords: claude-haiku-4-5, ≤512 tokens, only for words not in the local cache. Field: words ≤12 chars
rerank: claude-haiku-4-5 (claude-sonnet-5 from round 2), ≤2048 tokens, only when round 1 was not confident, ≤20 candidates. Fields: memory, items[].title ≤120, uri ≤200, snippet ≤200, author ≤80, timestamp
Secrets redacted before sending. Never sent: file contents beyond the capped excerpt, folders, index, audit log, recall profile, credentials. Base URL pinned; ANTHROPIC_BASE_URL is ignored.
Opt-in
Opt-in (default localOnly: true; key required; first-enable acknowledgement)
2 Validate an Anthropic key
GET https://api.anthropic.com/v1/models?limit=1
The user pastes a key in setup or Settings → Models → Validate & store. One call per click.
Header x-api-key with the pasted key; no body.
User action
User action
3 Slack: message search, people lookup, connection check
POST https://slack.com/api/search.messages
POST https://slack.com/api/users.list
POST https://slack.com/api/auth.test (connect check)
A search runs while a Slack USER token is configured and Slack is neither disabled nor paused; users.list only during identity resolution; auth.test once on connect.
search.messages: a query of ≤6 keywords, ≤3 person-name variants, from:, after:/before: day bounds, has:link; ≤4 calls per search, ≤2 pages each
users.list: ≤3 pages of 200 (no query text)
auth.test: the token only
Opt-in
Opt-in (connect a token)
4 Google Drive: file search, Google Docs excerpt, token refresh
GET https://www.googleapis.com/drive/v3/files
GET https://www.googleapis.com/drive/v3/files/{id}/export (text/plain)
POST https://oauth2.googleapis.com/token (access-token refresh)
A search runs while the keychain holds Drive OAuth tokens and a Google client is configured, and Drive is neither disabled nor paused; identity resolution reads the owners of the 100 most recently modified files.
files.list q= built from ≤6 keywords / person variants / media hints / owner email / time bounds
files.export text/plain of ≤5 Google Docs under 1 MiB per search (20 KiB read)
Token refresh: the refresh token + client credentials
Opt-in
Opt-in (OAuth consent)
5 Gmail: message search, metadata/body fetch, token refresh
GET https://gmail.googleapis.com/gmail/v1/users/me/messages
GET https://gmail.googleapis.com/gmail/v1/users/me/messages/{id}
POST https://oauth2.googleapis.com/token (access-token refresh)
A search runs while the keychain holds Gmail OAuth tokens and a Google client is configured, and Gmail is neither disabled nor paused; identity resolution runs one list (10 results) + ≤6 metadata gets.
messages.list q= with ≤6 keywords, a from:/to: group, filename:/domain group, after:/before:; ≤25 ids
messages.get metadata for ≤12 candidates; full text/plain body (20 KiB cap) for the first 5 only
Token refresh: the refresh token + client credentials
Opt-in
Opt-in (OAuth consent)
6 Google OAuth consent (Drive, Gmail): code exchange and account label
https://accounts.google.com/o/oauth2/v2/auth — opened in the SYSTEM BROWSER, not by this process
redirect to a loopback listener http://127.0.0.1:<port>/callback (this process listens, never dials out)
POST https://oauth2.googleapis.com/token (authorization-code exchange)
GET …/drive/v3/about or …/gmail/v1/users/me/profile (account email for the confirmation line)
Connect Drive or Gmail from setup or Settings → Sources → Sign in with Google. The listener closes when the flow ends.
Consent URL: client_id, scope drive.readonly or gmail.readonly (incremental), random state
Code exchange: the one-time code + client id/secret. Tokens go to the OS keychain, never to disk or logs
User action
User action
7 Notion: page search, people lookup, connection check
POST https://api.notion.com/v1/search
GET https://api.notion.com/v1/users
GET https://api.notion.com/v1/users/me (connect check)
A search runs while a Notion integration token is configured and Notion is neither disabled nor paused; /v1/users only during identity resolution; /v1/users/me once on connect.
search.query for ≤3 sub-searches (≤4 keywords / person / media hint), filter: page. No page content is ever fetched: titles and metadata only
/v1/users: ≤3 pages of 100 (no query text)
Opt-in
Opt-in (token)
8 GitHub: code / issue / repo search, own gists, starred repos, people lookup, connection check
GET https://api.github.com/search/code, /search/issues, /search/repositories
GET https://api.github.com/gists, /user/starred, /user/repos
GET https://api.github.com/search/users, /users/{login}
GET https://api.github.com/user (connect check)
A search runs while a GitHub token is configured and GitHub is neither disabled nor paused (≤3 requests in flight); people routes only during identity resolution; /user once on connect.
q= of ≤3 keywords (+ the next 3 as a fallback), author:/user: login when resolved; per_page ≤50
Opt-in
Opt-in (token)
9 Download the local brain (Qwen3 1.7B default / Qwen3 4B Instruct upgrade, Q4_K_M GGUF)
GET https://huggingface.co/unsloth/Qwen3-1.7B-GGUF/resolve/main/Qwen3-1.7B-Q4_K_M.gguf
GET https://huggingface.co/unsloth/Qwen3-4B-Instruct-2507-GGUF/resolve/main/Qwen3-4B-Instruct-2507-Q4_K_M.gguf
The user clicks Download (Settings → Models, or onboarding step 2). Never automatic. An interrupted download resumes with an HTTP Range request.
GET only, plus Range: bytes=<offset>- on resume. sha256 and size pinned; a mismatch is discarded. Hugging Face redirects to its LFS CDN inside the same call.
User action
User action
10 Download the embedding model (multilingual-e5-small, int8 ONNX, ~135 MB)
GET https://huggingface.co/Xenova/multilingual-e5-small/resolve/main/onnx/model_quantized.onnx
GET …/tokenizer.json, …/config.json, …/tokenizer_config.json
AUTOMATICALLY, once, right after onboarding completes: the semantic index of the ticked folders starts in the background and needs the model. Onboarding states this (size, checksum, disk budget) before the user finishes. Never re-downloaded once verified.
GET only. Four files, each sha256-pinned; the model is used only after every hash matches.
Automatic after onboarding
Automatic after onboarding (disclosed there); otherwise user action
11 Download a voice model (whisper.cpp ggml base / small)
GET https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.bin
GET https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.bin
The user clicks Download voice model (Settings → Hotkey & voice / Models, or onboarding step 2). Never automatic. Resumable.
GET only, plus Range: on resume. sha256 and size pinned.
User action
User action
12 Desktop app update check and signed download (Tauri updater)
GET latest.json from the app update feed (the host is not published on this website yet)
GET the signed artifact URL named in latest.json, only after a second click
The user clicks Settings → About → Check for updates. Never on launch, never on a timer. Download and install is a separate, second click; the install is minisign-verified against the public key shipped in the app.
GET only. The updater sends its User-Agent and the current version/target; nothing about the user or their data. Each check/install is written to the audit log.
User action
User action (manual button)

Derived from the app's network registry document, 2026-09-05. When the registry changes, this table is regenerated with it.

Latent calls, closed

  • node-llama-cpp: clone/build llama.cpp when no prebuilt binary matchesgithub.com (git clone), api.github.com (release lookup)CLOSED: build pinned to never, download skipped. A missing binary is a typed error and search stays at Tier 0.
  • Anthropic SDK: ANTHROPIC_BASE_URL redirectwhatever the environment variable would point toCLOSED: both constructors pin the base URL to api.anthropic.com; the registry additionally allows only that host.

Installer-time and external

  • Windows installer: WebView2 bootstrapper downloadMicrosoft WebView2 bootstrapper (Microsoft CDN)The installer runs on a Windows machine without WebView2. Installer-time, never app-time.
  • Opening a result in the system browserany http(s) URI of a result, opened by the OS default browserThe user presses Enter or clicks a result. The app itself makes no request.

Things checked that make no call

  • Telemetry, crash reporting, analytics: none in source and none in dependencies (no sentry, posthog, segment, amplitude, mixpanel, bugsnag).
  • License checks: none. Deep-search metering is a local JSON file.
  • Voice: audio is written to a temp WAV and passed to the bundled whisper-cli; no network.
  • Browser history: local SQLite copies only. Local files: filesystem walk, ripgrep subprocess, local SQLite index.
  • Keychain: OS keychain; no network. Engine ↔ shell: JSON-RPC over stdio, no sockets. The only listening socket ever opened is the ephemeral loopback OAuth callback, closed after the flow.

With all cloud features off, the calls are:

"All cloud features off" is the default: local only, no Anthropic key, no source tokens or OAuth grants.

  1. Zero calls during search, indexing over an already-downloaded model, voice, onboarding scan, audit, recall, graph and metering. An automated airplane-mode test asserts this against the network guard on every build.
  2. One automatic download, disclosed in onboarding: the embedding model (~135 MB, huggingface.co, sha256-pinned) right after onboarding completes, because the semantic index starts then. It never repeats once verified.
  3. Downloads on click only: the local brain and the voice model (huggingface.co, sha256-pinned, resumable).
  4. Update check on click only: Settings → About → Check for updates. Nothing on launch, nothing on a timer.
  5. Installer only (Windows): the WebView2 bootstrapper when WebView2 is absent.
  6. Paranoid mode refuses all of the above at the network layer, downloads and update check included.

the one cloud feature

The Claude API disclosure

The app can call Anthropic's Claude API. It ships off. Turning it on is a toggle in Settings → Privacy and requires your own API key. There is no Memoria account and no key of ours in the binary.

When on. Three narrow jobs, only when the local model could not do them: parse your memory into claims, translate keywords missing from the local cache, rerank at most 20 results when round one was not confident. The exact payload is shown the first time you enable it; you must acknowledge it.

What is sent. Your typed memory. For the rerank, per candidate: title ≤120 chars, URI ≤200, snippet ≤200, author ≤80, timestamp. Secrets redacted first. Never sent: file contents beyond that excerpt, folders, index, audit log, recall profile, credentials.

How you know. Before a cloud search the status bar shows an outbound indicator. Every cloud call is written to the audit log. The base URL is pinned to api.anthropic.com. An hourly meter caps deep searches; when it refuses, the search runs local-only and says so.

POST https://api.anthropic.com/v1/messages · claude-haiku-4-5 (sonnet-5 from round 2) · ≤2048 output tokens · OFF by default · your key · logged

skeptical faq

Questions a careful person asks

How is this different from a screen recorder?

A screen recorder keeps an archive of everything you saw and heard. Memoria records nothing. It searches what already exists: your files, your browser history, the read-only APIs of the apps you connect. The only thing it builds is an index of local text, in a folder you can delete. No screen capture, no audio archive, no timeline. Voice input is transcribed on device and the audio discarded.

What if the company changes hands?

A store of your life outlives a company's plans and goes where the company goes. Memoria's answer: store as little as possible, only on your disk, with no account to transfer. If the company disappears, the app keeps working offline and the source is readable.

Why should I believe "local"?

Do not believe it; check it. A local store of screenshots of everything, readable by any process, is not the same thing. Memoria's index holds text you already have, in your home folder, with the same protection as the rest of it. And the network layer is enumerable: the table above is every host the process can reach, the airplane-mode test is a build-time assertion, paranoid mode is a hard refusal. Little Snitch will show you what this page says.

What do you see about me?

Nothing from the app. No account, no telemetry, no crash reporter, no analytics, no licence server. This website stores the email you submit, when you submitted it, and the page and campaign parameters that came with the visit. It also counts a few anonymous page events. No third-party analytics.

The tradeoff: we do not see crashes either. If something breaks you have to tell us, and share the log if you choose. An opt-in crash reporter is on the list; off by default like everything else.

Is it open source?

Source-available, not open source. The code will be published so the claims on this page can be read rather than believed: the read-only guard, the network registry, the airplane-mode test. The licence restricts redistribution and commercial use, because a one-person company cannot survive being forked by a larger one on day one. Timing and licence ship with the beta.

What are the OAuth scopes?

Exactly these. None permits writing. Today you create the credentials yourself (your own Slack app, Google Cloud OAuth client, Notion integration, GitHub token); a vendor-owned OAuth app is an open decision and would be listed here.

SourceAuthExact scopesWhose credentialsWhat is readOS permission
Local files None
filesystem read of the folders you tick
n/a Default roots ~/Documents, ~/Desktop, ~/Downloads. Filename walk (50k entries cap), content search via ripgrep (5 MiB/file, 8 s), optional local hybrid index (.txt/.md/code/.pdf/.docx, 2 MB text cap; media files metadata-only). Symlinks never followed; node_modules, .git, Library, .Trash, dist, .cache pruned. None for the default folders.
Browser history (Chrome, Edge, Brave, Firefox) None
read-only copy of the History / places.sqlite database
n/a The history database is copied to a temp dir and opened read-only; url/title LIKE match; ≤25 rows per profile. The live database is never opened or locked. None.
Browser history (Safari) None
read-only copy of ~/Library/Safari/History.db
n/a Same as above, Safari schema. Full Disk Access for Memoria.app (macOS). Without it the copy fails and the app shows the exact System Settings path.
Slack User token (xoxp-…). Bot tokens are rejected up front.
search:read
users:read
A Slack app you create in your own workspace; you copy the token into Memoria. Stored in the OS keychain. Message search results (≤500 chars kept per message), people list for name resolution. Nothing is posted. None.
Google Drive OAuth 2.0, desktop loopback flow, access_type=offline
https://www.googleapis.com/auth/drive.readonly
An OAuth client of type Desktop app in your own Google Cloud project. Tokens stored in the OS keychain. File list matches; text/plain export of ≤5 Google Docs under 1 MiB per search (20 KiB read). Other file types: name and metadata only. None.
Gmail OAuth 2.0, same client as Drive, incremental consent
https://www.googleapis.com/auth/gmail.readonly
Same user-created OAuth client as Drive. gmail.readonly is a Google restricted scope: on your own project in testing mode it works for up to 100 test users. ≤25 message ids per search, metadata for ≤12, full text/plain body (20 KiB cap) for the first 5 only. HTML-only bodies fall back to the API snippet. None.
Notion Internal integration token (ntn_… / secret_…)
Read content (the integration's only capability)
An internal integration you create; each page or database must be shared with it. Stored in the OS keychain. Page titles and metadata only. Block content is never fetched. None.
GitHub Fine-grained personal access token
Contents: read
Issues: read
Pull requests: read
Gists, Starring: read (optional, for own gists and starred repos)
A PAT you create. Stored in the OS keychain. Code, issue and repository search hits; own gists; starred repos; ≤3 profiles during name resolution. None.

Derived from the app's source inventory, 2026-09-05.

waitlist

Checked enough? Leave an email.