Projects

NullBrowser

github.com/akaieuan/null-browser · Read PHILOSOPHY.md

Repo, product site, and the living invariant list in the repo.

Earlier reference on this site: concept sketch · philosophy draft. The shipped build supersedes both, but these show how the constraints evolved.

Open source · pre-v0.1

Null

A browser that refuses to ship what the user didn't ask for. null.sh

What this is

Null is three commitments shipped as one project: a perspective arguing that browsers have quietly become advertising infrastructure and that the invariants usually waved away as "nice to have" are actually the only load-bearing specification; a shell built on Tauri 2.0 + the system WebView: not a Chromium fork, not a vendored engine, roughly 15k lines of Rust and TypeScript doing the work of a million; and a manifest, six rules that every feature has to earn against, enforced in code and in the PR template. The shipped app adds four separate AI surfaces (grounded tab chat, tab summarize, user-configured search, save to artifacts), a SQLite artifacts browser, and Tauri channel streaming for those flows, all inspectable. The argument, the implementation, and the constraints, in one place.

What I actually built

  • Six invariants, enforced in code. Zero telemetry. No default cloud connections. All AI inference local by default (Ollama). Every outbound connection visible in the inspector. Data on disk, not in an account. No dark patterns. Each one is a line item in docs/PHILOSOPHY.md and a gate in review.
  • The three questions. Every change that touches networking, storage, or AI routing answers from the diff alone: what does this store, transmit, remember?
  • Tauri 2.0 multi-webview shell. Every tab is its own child WebView with its own cookie jar. The React chrome lives in the main webview and never sees page content. Tab switching is native show/hide, not CSS visibility tricks.
  • AI as four collaborator modes, not an agent. Chat is grounded in the active tab through chatWithPage. It streams ChatEvents (grounded, chunk, done, error) over a Tauri channel. Summarize uses summarizeCurrentTab to read the page, stream a summary with optional focus, and save a persistent artifact. Search uses a user-configured search instance via searchGetInstance, rendered as SearchResult[] cards. Save uses saveCurrentTab to snapshot the page into the artifacts collection. Every mode streams on Tauri channels (no polling for those flows). Every cloud call passes a permission broker and shows in the network inspector before it leaves. "Assist, don't complete" is literal: the model never clicks, types, or navigates on its own. It answers about the tab, saves what you are already looking at, or searches at your request.
  • Artifacts as a first-class object. SQLite stores saved pages and summaries, with listArtifacts, getArtifact, and deleteArtifact, a list-and-detail browser, and markdown in the reader via react-markdown and remark-gfm, openable in a dedicated view in the drawer. A streaming ArtifactEvent path (extracted, chunk, saved, error) uses Tauri channels, local, visible in the inspector. The app does not use AI to take you somewhere new; it saves what the user already chose to open.
  • Local-first provider router. Ollama is the only provider enabled at startup. Anthropic and OpenAI-compatible providers are opt-in, per key, per call. Keys live in the OS keychain, not localStorage, not SQLite. ProviderRow in the profile dropdown lets you paste a key in place without leaving the chrome.
  • The inspector is a surface, not a devtool. Every subresource, every AI call, every connection. Origins can be blocked live through a SQLite-backed allowlist wired into a beforeRequest hook.
  • One button to forget everything. "Clear history & logins" wipes the SQLite history table and clears cookies, localStorage, sessionStorage, and IndexedDB across every live tab webview in one transaction.

Why it's unusual

Most "privacy browsers" are a Chromium fork with a VPN upsell and a logo change. Null is the opposite bet: a tiny Tauri shell where every feature has to justify existing against six invariants, and every outbound connection is visible without opening a devtool. I wrote the manifest that says browsers became ad infrastructure because nobody was enforcing the constraints that made them trustworthy, then built the shell that enforces those constraints in code, then wrote the review gates that keep them enforced as the project grows. Positioning, authorship, engineering, and governance are one piece of work. The manifest is not marketing. The manifest is the build.

How I describe the skill set

Rust and Tauri 2.0 runtime internals, multi-webview architecture, SQLite schema authorship and migrations, React 19 + TypeScript, Tailwind v4 design tokens, AI router and permission-broker design, OS-keychain credential handling, network instrumentation, open-source principled positioning, and the discipline to refuse features the invariants don't accept.

Most browsers ship what the vendor wants to ship. Null ships what the user consented to. Zero telemetry, no default cloud, local-first AI, every connection in the open. The invariants aren't copy. They're the build.