Skip to main content

aleth-reader

A browser extension that remembers what you read, into the memory engine running on your own machine. No account, no server, no upload.

It is made of two pieces:

  • extension/: the part in the browser. It watches what is on screen, scores which blocks are reading material, and sends the new sentences out.
  • host/: a small Python program the browser starts for it. This is the only way the extension can reach anything outside the browser, and all it can reach is the memory engine on this machine.

The extension has no network permission at all. It cannot fetch a page, call an API, check for updates or send a report, because it holds no permission that would let it and there is no such code in it. That is a stronger claim than "it only talks to localhost", and there is a command below that measures it rather than asking you to believe it.

What it stores

Only what you actually looked at. It polls the visible part of the page, scores each block the way a reader-view algorithm does (how much text it holds, how much of that text is links, whether its own class name says article or says cookie-banner), and keeps what passes. Read half a page and half a page is what is remembered.

It stores the text verbatim with the page's URL and title attached. Nothing is summarised, and no language model sees it.

It de-duplicates by sentence, so opening the same article tomorrow stores nothing, and an article that grew a paragraph stores only that paragraph.

There are no per-site rules. Nothing in the reading path knows the name of any website. The same code runs on a news site, a wiki, a PDF and a page nobody has ever seen before.

What it never stores

  • Anything in a sensitive category. Finance, health, adult, sign-in pages and webmail are blocked before anything is read. The list is evaluated inside the extension, on your machine. No server is ever asked about a domain you visited, which is the point.
  • Any site you put on the never-remember list. One click on the chip.
  • Anything at all while it is paused.
  • Chat threads, unless you switch bridge mode on yourself (see below). Reading mode stands down on a chat page.

The sensitive-category rules are a port of the ones in clients/aleth-ingest/aleth_ingest/browser_history.py, which is the source of truth. Both are driven by the same table of test cases from two test suites, so if one drifts from the other, a test goes red.

The categories are a default, not a verdict. The list is short on purpose (short enough to read), and it will not know about your bank if your bank's name carries no money word in it. Use the never-remember list for those.

The chip

A small chip sits in the corner of the page and says which of three things is true:

chip meaning
Remembering this page is being captured
Paused you paused it; nothing is being captured anywhere
Blocked this page will never be captured: a sensitive category, or your own never-remember list

Click the chip to pause or resume. Hover it for a Never here button. The toolbar icon carries a badge with the same state, so you can also see it without the page.

There is a fourth state the chip shows only in bridge mode: Capture broken. It means a chat page changed its shape and the bridge can no longer find the parts it needs. It is loud on purpose; see below.

Bridge mode (chat pages)

Off by default, and it stays off until you turn it on. When it is on, on a chat page (ChatGPT, Claude, Gemini, or any page with a compose box and a conversation in it) the extension:

  • stores each turn as it happens: your prompt when you send it, the reply when it stops growing;
  • offers relevant memory into the compose box before you send. It shows you what it found and you click Insert or dismiss it. It never edits what you are about to send.

Nothing leaves your machine except the prompt you send to the model you chose, by your own hand, through that site's own code.

Why "as it happens" matters. These pages destroy old messages from the page as you scroll away from them. Reading the thread afterwards reads whatever survived, and it fails quietly. Capturing the turn while it exists does not have that problem.

When a chat page changes its shape. It will; they all do. There is one generic adapter (find the text field, find the last reply) and a small table of per-site refinements, and a check that runs against the real page:

  • it runs automatically when bridge mode starts on a page;
  • you can run it any time from the popup with Check this chat page;
  • if it fails, the chip goes to Capture broken, the browser console gets a line naming which part broke, and the popup's counter goes up.

A failure is never silent. That is the whole design of that part.

PDFs

Honest answer, per browser:

  • Firefox renders PDFs with its own viewer (pdf.js), which puts a text layer in the page. The extension reads it like any other page, in place, and nothing is uploaded.
  • Chrome renders PDFs with a built-in plugin that a content script cannot read. On Chrome, a PDF is not captured. Nothing is uploaded either: it simply does nothing, and the chip shows the state honestly.

For PDFs on disk on either browser, use aleth-ingest instead: it reads local files directly and does not need a browser at all.

Install

You need a running Aleth memory engine on this machine (the host finds it through its discovery file, the same way every other client does).

1. The native messaging host

cd <path-to>\aleth-reader
pip install -e .
python -m aleth_reader_host install --browser chrome,chromium,firefox

Run it from a directory inside the engine's tree, or pass --discovery-dir. The browser starts the host with the browser's working directory, so the installer records where the engine's discovery file is; without that, every capture fails with "engine is not running" while the engine is running fine. The line it prints tells you which directory it found, or that it found none.

That writes a small manifest telling each browser what to run and which extension is allowed to talk to it. On Windows it also writes a launcher .bat (browsers there cannot run a .py directly) and a registry value under HKEY_CURRENT_USER pointing at the manifest. Nothing is written for other users of the machine and nothing needs administrator rights.

See what it would do first with --dry-run, and undo it with --uninstall.

2. The extension

Chrome (or Brave, Edge, any Chromium):

  1. chrome://extensions
  2. turn on Developer mode
  3. Load unpacked -> pick the extension folder

The extension's id is fixed (kmbfklemccbmfmhldhbjgdcidbiimhpe), because the manifest carries a public key. That is what lets the installer name the allowed extension before you have loaded it. Loading a differently-keyed build? Pass --extension-id to the installer.

Note that --load-extension on the command line is refused by current Chrome whatever flags you pass; loading it through the page above works normally.

Firefox:

python tools\build_extension.py --browser firefox
  1. about:debugging#/runtime/this-firefox
  2. Load Temporary Add-on
  3. pick dist\firefox\manifest.json

Firefox unloads a temporary add-on when it closes; that is Firefox's rule for unsigned add-ons, not ours.

3. Check it

Open the extension's popup and press Check the engine. It should report the engine's node count. If it does not, the host's log is at ~/.aleth-reader/host.log.

Check the privacy claim yourself

python tools\privacy_trace.py --static-only

That scans every file that ships for an outbound API (fetch, XMLHttpRequest, WebSocket, sendBeacon, EventSource, a remote script tag). The answer should be none.

To measure a running browser instead, get its process id and:

python tools\privacy_trace.py --browser-pid <pid> --host-match aleth_reader_host --duration 30

It lists every TCP endpoint the browser's process tree and the native host hold, split into loopback and remote. With only local pages open, the honest result is zero remote connections and one loopback connection: the host, to the memory engine.

A snapshot cannot see a connection that opened and closed between two samples, which is why it samples repeatedly and why the source scan exists beside it.

Tests

JavaScript (no dependencies, node's own test runner):

cd clients\aleth-reader
node --test "tests/*.test.mjs"

Python:

python scripts\safe_pytest.py clients\aleth-reader\host\tests\test_framing.py clients\aleth-reader\host\tests\test_host_dispatch.py clients\aleth-reader\host\tests\test_install.py clients\aleth-reader\host\tests\test_ports_parity.py

The parity test runs the extension's own JavaScript and compares its verdicts with the Python original, case by case, for both the blocklist and the URL normaliser. It fails rather than skips if node is missing, because a parity test that quietly does not run reports green.

The fixtures

fixtures/ holds four local pages: an article wrapped in the usual furniture, and three pages that reproduce the DOM shape of the three chat products. They are served over HTTPS by tools/fixture_server.py and loaded under mapped hostnames, so the reading path and each per-site override can be exercised in a real browser without an account and without touching the real sites. See docs/live-proof.md for the exact commands.

What has actually been proven, and what has not

Proven in a real browser, with the engine running (see docs/live-proof.md for the commands and the output):

  • text captured from a page and then recalled by meaning from the engine, with its source URL, by a separate process over the public wire;
  • a chat turn (the prompt and the reply, separately) captured as it happened and recalled the same way;
  • the bridge's conformance check passing against all three per-host overrides;
  • a sensitive category refusing to capture;
  • zero remote connections from the whole browser tree and the native host, under a sealed run.

Not proven, and honestly out of reach here:

  • A day of real reading. That needs the person whose reading it is.
  • The real ChatGPT and Claude.ai pages. Those need signed-in accounts. The fixtures reproduce each product's DOM shape, which proves the adapter and the capture path; they cannot prove the real site still uses those selectors today. That is what the conformance check is for, and it can be pointed at the real page in one command.
  • Firefox. There is no Firefox on the machine this was built on. The Firefox manifest is written, the build step produces a loadable directory and the installer writes the Mozilla registration, but nobody has yet watched it load.
  • The PDF path, for the same reason: the Firefox claim above is reasoning about pdf.js, not a measurement.

What this is not

It is not a scraper. It does not read pages you did not open, it does not follow links, and it does not fetch anything.

It is not a summariser. It stores what was on the page.

It is not a sync service. What it writes stays on this machine.

Metadata

Release files for aleth-reader-host 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aleth-reader-host 0.1.0
File Size Uploaded
aleth_reader_host-0.1.0.tar.gz 26.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aleth-reader-host 0.1.0
File Interpreter ABI Platform
aleth_reader_host-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 51.2 kB

Release files / aleth_reader_host-0.1.0.tar.gz

Download URL aleth_reader_host-0.1.0.tar.gz
Size 26.2 kB
Tags Source
SHA-256 checksum
How to use checksums
777ea9702c6e4b2c04e92750a1c5ed606ab4956d90b6eec1fba67ae2662ed4f6
BLAKE2b-256 checksum
How to use checksums
63883c460f0b0bf269b35ba88ea6e5769883fd80e4d25a40e05cce7634b67e77
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.15

Release files / aleth_reader_host-0.1.0-py3-none-any.whl

Download URL aleth_reader_host-0.1.0-py3-none-any.whl
Size 25.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7052eccca1d70ede2037d4a05ed8d7027f470fe1fed23227e40f8d4ecab96f86
BLAKE2b-256 checksum
How to use checksums
9baa75dec8904a28483935f614f1417d99dd84b83932d8364882978a93f2e69f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.15

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page