Abyss — Windows-first starter (Phase 1 foundation)
Privacy browser that wraps stock Mullvad Browser / Tor Browser (no Gecko fork) and puts a
pure-Rust daemon, abyss-core, between the browser and the network. This repo is the Phase 1
foundation from the spec (§4, §6–10, §22, §29, §32).
Status: pre-alpha starter code. Not audited. Do not rely on it for safety yet.
What's new in 0.7
- Extensions page (puzzle icon in the sidebar, or Ctrl+K "Extensions"). Install an unpacked
Chrome/Edge extension by pasting its folder path; Abyss validates
manifest.json(MV2/MV3), copies it (symlinks skipped, 96 MB / 6000 file cap) to%LOCALAPPDATA%\Abyss\extensions, and shows what it can access from the manifest it really declares. Enable/disable, choose which spaces it runs in (Surface, Shield, Ghost), remove with Undo (restorable until next launch). Tabs load extensions through WebView2 (AreBrowserExtensionsEnabled+AddBrowserExtension, via wry'sextensions_path), copied into that tab's own temp profile. Rules, unit-tested: Abyss never loads extensions; Ghost is opt-in with a warning; extensions that declareproxyornativeMessagingare never loaded in Tor spaces. Trade-off: WebView2 does not run extensions in InPrivate, so a tab that loads extensions uses the per-tab temp profile without the InPrivate flag (still wiped on close, Panic and exit). - HTTPS-only interstitial. If a page Abyss upgraded to HTTPS fails (certificate error, unreachable, refused, timeout), the shell shows a full-page warning instead of silently falling back. "Open over HTTP anyway" allows that host over HTTP in that tab only, in memory.
- Tor-space fingerprint normalisation (Ghost and Abyss): before the first load, a DevTools
Emulationoverride sets the time zone to UTC, locale andAccept-Languageto en-US, and a reduced Chromium user agent without theEdg/token or "Microsoft Edge" brand. Not covered: canvas, WebGL, audio, fonts, screen/window size. See limits below.
What's new in 0.4
- Find in page (Ctrl+F): a floating pill with match count and previous/next. Uses the
WebView2 Find API (
ICoreWebView2_28) with the default find UI suppressed; on runtimes without it, falls back towindow.find()run throughExecuteScriptonly when you search. - Per-site zoom (Ctrl+= / Ctrl+- / Ctrl+0) through the controller
ZoomFactor, remembered per host in memory for the session only (wiped on Panic). - Site panel: click the lock in the address pill for host, connection (Direct / Tor / Closed),
trackers blocked in the tab, a per-tab JavaScript switch, zoom, New circuit (Ghost/Abyss) and
Clear this site (DevTools
Storage.clearDataForOriginfor the origin, then reload). - Tabs: right-click menu (Pin, Duplicate, Move to space, Close others, Close), middle-click to close, Ctrl+Shift+T reopens closed tabs (memory only, wiped on Panic). Move to space reopens the URL in a fresh tab of the target space, so it gets that space's network and profile.
- Shortcut sheet (Ctrl+/ or ?), three one-time tips, five accent swatches for Surface (Tor spaces keep their identity colour), spring sliding indicator on segmented controls, press/focus states on every control, icon empty states, static loading skeleton.
What's new in 0.3
- Much lighter at idle. No
backdrop-filteranywhere (frosted glass is a pre-blurred copy of the wallpaper painted withbackground-attachment: fixed), no infinite animations except the page-loading line, only transform/opacity transitions. The shell no longer polls every 600 ms: it polls fast only while something moves, slows to 4 s when idle, stops while minimised/hidden, and Rust pokes it on page-load/title/new-window events.set_web_rectand native view moves only happen when the rect really changes. TOTP ticks locally only while the Vault is open. - Tor is lazy: arti bootstraps only when the first Ghost/Abyss tab opens (or the burner / Settings › Start now asks for it). Surface-only sessions never start Tor.
- One shared tokio runtime with 2 workers. Hidden tabs are
SetIsVisible(false)with a LOW WebView2 memory target and are suspended (TrySuspend) after 5 min in the background; tabs are hidden while the window is minimised. Release profile:opt-level=3, fat LTO,codegen-units=1,panic=abort, stripped. - New look: no clock; Windows caption buttons; Arc-style sidebar (URL pill, space switcher, pinned squircles, collapsible sections, drag-to-reorder tabs, tooltips, score pill), hover-to- peek when collapsed (Ctrl+S); compact home widgets + Quick sites; wallpapers Silk (default), Glass, Dune, Solid graphite, or a custom image (downscaled to ≤1920 px, kept in prefs); Settings › Reduce effects.
- Idle probe:
python3 ui/companion/tests-ui/perf.py(CDP metrics over 10 s per scenario).
The Abyss app (ui/companion)
A real browser shell built on Tauri v2 multi-webview (feature unstable): the frameless
window hosts one local shell webview (sidebar, home, sheets) and every tab is its own
child WebView2 positioned in the web card.
Wired for real:
- Tabs: new / close (webview destroyed + profile dir wiped) / switch, URL & search (Brave,
DuckDuckGo, Startpage, Leta, Mojeek, SearXNG; bangs
!b !ddg !sp !leta !mj !sx), back / forward / reload, live title, progress line, pinned sites, command bar with fuzzy results (tabs, RAM-only session history, actions). - Shortcuts, also while a page has focus (WebView2 AcceleratorKeyPressed): Ctrl+T/K/L, Ctrl+W, Ctrl+Tab, Ctrl+1–9, Ctrl+Shift+G/A/B/L/V/X, Ctrl+S, Ctrl+,, F5, Alt+←/→.
- Spaces Surface / Shield / Ghost / Abyss. Every tab:
incognito, own data dir under abyss-ram's volatile root, its own loopback SOCKS5 listener as the only proxy,--host-resolver-rules=MAP * ~NOTFOUND(no local DNS), WebRTC limited to proxied routes, QUIC / background networking off, web messages disabled (pages get no IPC), permission requests denied, downloads blocked. - Surface: tracker blocklist (abyss-block,
resources/blocklist.txt) + direct dial; live counter. - Ghost / Abyss: in-process arti; isolation key = first-party site + tab nonce. Fail closed while
Tor bootstraps. "New circuit" bumps the site generation and kills the tab's live tunnels.
.oniononly here; other spaces offer "Open in Ghost". - Abyss: JavaScript off (per-tab toggle),
--jitless --noexpose_wasm, crimson edge. - Shield: fail closed (WireGuard engine not built yet); config import is stored in the Vault.
- Vault (abyss-vault): PIN onboarding, unlock/lock, logins, RFC 6238 TOTP, aliases, onion bookmarks, idle auto-lock, clipboard auto-clear, capture-excluded window while open.
- Burner: mail.tm over Tor only, explicit button, OTP extraction (abyss-burner).
- Leak check: live proxy listeners, launch flags, Tor state, real
check.torproject.orgover Tor. - Panic: closes every webview, wipes profiles, forgets circuits, locks the vault, clears clipboard.
Honest limits: WebView2 is Chromium, so fingerprints are not Tor-Browser-uniform; Tauri still
injects its IPC bootstrap script into tab webviews (IPC is refused for remote origins and web
messages are disabled); profiles are wiped from disk, not RAM-backed, on Windows; the Abyss
micro-VM and the WireGuard engine are not implemented; the system-wide WFP kill-switch is not
engaged by the app (Tor runs in-process and msedgewebview2.exe is shared), the app fails closed
at its proxy instead. UI click-through test: python3 ui/companion/tests-ui/run.py.
Layout (spec §29)
crates/
abyss-core/ daemon: config, per-launch token, IPC (named pipe / unix socket), launcher, Panic
abyss-tor/ SOCKS5h server + per-site isolation + arti dialer (feature `arti`, default on)
abyss-firewall/ WFP kill-switch (Windows FFI), Linux TODO
abyss-vault/ redb + Argon2id + XChaCha20-Poly1305 + zeroize, DPAPI sealing on Windows
abyss-ram/ ephemeral per-mode profiles, wipe, VirtualLock, clipboard wipe, hibernation check
abyss-block/ Bloom-filter tracker sinkhole with subdomain suffix matching
abyss-leaktest/ DNS / WebRTC / IPv6 / kill-switch / exit-IP checks as structured results
abyss-wg/ STUB (Shield / WireGuard, phase 4)
abyss-vm/ STUB (Abyss Mode micro-VM, phase 5)
abyss-burner/ STUB (burner mail; OTP extractor implemented)
abyss-quarantine/ STUB (download quarantine, phase 4)
engine/profiles/ user.js + policies.json per mode (surface, shield, ghost, abyss)
ui/chrome/ userChrome.css — Arc-style sidebar + floating web card (spec §16)
ui/companion/ Tauri v2 companion skeleton (separate crate, not in the workspace)
docs/ IPC protocol, example config + blocklist
fuzz/ placeholder (phase 6)
.github/workflows/ CI: windows-latest + ubuntu-latest, fmt / clippy / test
What is built vs TODO
| Area | Built | TODO |
|---|---|---|
Daemon (abyss-core) |
tokio daemon; 32-byte random IPC token per launch (zeroized); JSON protocol status, new_circuit{site}, panic, vault_lock, leak_check; named pipe (Windows, first_pipe_instance) / unix socket 0600 fallback; token handoff file |
explicit pipe DACL; owner-only ACL on handoff file; mode switching at runtime; multiple simultaneous modes |
Tor (abyss-tor) |
SOCKS5 CONNECT (no-auth + RFC 1929 user/pass), domain/IPv4/IPv6, SOCKS5h; credentials site:nonce (or user=site, pass=nonce as Tor Browser sends) → isolation key → arti IsolationToken; new-circuit-for-site bumps generation; sinkhole hook; arti TorClient::connect_with_prefs with .onion enabled |
circuit map, bridges/PTs, vanguards, captcha-hop, arti state dir in RAM |
Kill-switch (abyss-firewall) |
WFP: FwpmEngineOpen0, Abyss sublayer, persistent block-all for the browser app-id (fail closed), dynamic-session permit only to 127.0.0.1:<core port>; Panic deletes the permit; presence check for leak test |
BOOTTIME filters; elevated abyss-fw-svc helper (today core must run elevated); exact filter enumeration; uninstall cleanup; Linux netns+nftables |
| Vault | create / unlock / lock / put / get / delete / list; Argon2id → KEK wraps random DEK; XChaCha20-Poly1305 with entry name as AAD; DPAPI (CURRENT_USER) seal of the wrapped DEK on Windows; tests | TPM 2.0 / Windows Hello (NCrypt); duress PIN; encrypted names; backup export; auto-lock |
| RAM / zero-trace | per-mode ephemeral profile, zero-overwrite + delete on close/Panic/drop; /dev/shm on Linux; VirtualLock helper; clipboard wipe; hiberfil/pagefile detection |
real RAM volume on Windows (see limits); registry checks for hibernate/crash dumps |
| Blocker | Bloom filter (no deps) + exact confirm, suffix matching, hosts / plain / ` | |
| Leak test | pref-based DNS / WebRTC / IPv6 checks, WFP rule present | live exit-IP check through SOCKS; active direct-socket probe |
| Launcher | copies mode user.js into the ephemeral profile + appends SOCKS5h prefs (network.proxy.socks_remote_dns=true, random port, TRR off); --no-remote --new-instance --profile; TOR_PROVIDER=none/TOR_SKIP_LAUNCH=1 so Tor Browser uses our Tor; kill-switch engaged before spawn; refuses Shield (no WG yet) and refuses Tor modes if Tor failed to bootstrap |
installing policies.json into <install>/distribution/; userChrome copy; AppContainer sandbox |
| Panic (§22) | fixed order: firewall block-all → close sockets (abort SOCKS, clear isolation) → lock vault/zeroize → kill browser (+VM TODO) → wipe profile + clipboard; per-step timing | < 50 ms measurement in CI; UI animation |
| Companion | Tauri v2 skeleton: commands core_status, new_circuit, leak_check, vault_lock, panic_now over the named pipe; minimal HTML |
onboarding, settings, vault UI, display-affinity screenshot block |
| Abyss Mode VM, WireGuard, burner, quarantine | stubs with TODOs | phases 3–5 |
Unsafe policy
Every Abyss crate is #![forbid(unsafe_code)] except three crates with Win32 FFI, which use
#![deny(unsafe_code)] at the root and #![allow(unsafe_code)] only inside a cfg(windows) module:
abyss-firewall::wfp (WFP), abyss-ram::win (VirtualLock, clipboard) and abyss-vault::dpapi
(CryptProtectData). forbid cannot be locally overridden, hence deny + scoped allow. Each FFI call has a SAFETY: comment.
Build on Windows
- Install Rust stable (https://rustup.rs, default
x86_64-pc-windows-msvctoolchain). - Install Visual Studio 2022 Build Tools with "Desktop development with C++" (MSVC + Windows SDK). arti pulls in C code (sqlite, zstd, lzma), so the C toolchain is required.
- Install Mullvad Browser and/or Tor Browser normally.
- Create
%APPDATA%\Abyss\config.json(seedocs/config.example.json) and pointtor_browser_exeat...\Tor Browser\Browser\firefox.exeandmullvad_browser_exeat...\Mullvad Browser\Browser\mullvadbrowser.exe. Run from the repo root soprofiles_dir: "engine/profiles"resolves, or give an absolute path. - Build and test:
powershell cargo test --workspace cargo run -p abyss-core -- --mode ghost # Tor Browser via arti cargo run -p abyss-core -- --no-browser # daemon only cargo run -p abyss-core --no-default-features # no arti: SOCKS refuses everything (fail closed)Run the terminal as Administrator for the WFP kill-switch to engage; otherwise core logsKILL-SWITCH NOT ENGAGEDandleak_checkreports it. - Companion:
cd ui/companion && cargo install tauri-cli --version "^2" && cargo tauri dev(needs WebView2, preinstalled on Windows 11).
Panic: Ctrl+C in the core console, or IPC {"cmd":"panic"}. To remove the persistent WFP
block after testing, delete the "Abyss kill-switch" sublayer filters (e.g. netsh wfp show filters
to inspect; uninstall helper is TODO). While it is installed and core is not running, the configured browser has no network at all — that is the point.
Honest limits (read before trusting it)
- Not audited, not fuzzed. Starter code.
- Windows RAM profiles are disk-backed today (random dir in
%LOCALAPPDATA%\Abyss\volatile, zero-overwritten and deleted). On SSDs, overwrite is not a guarantee. True RAM-only needs a RAM-disk driver (TODO). - Kill-switch needs admin; without elevation there is no kill-switch. WFP app-id binds to the executable path, not a true process tree.
- Abyss Mode has no micro-VM yet: it launches Tor Browser on the host with Safest prefs and logs a warning.
- Shield mode refuses to launch (WireGuard not built).
policies.jsonis not auto-installed (needs write access to the browser install dir);user.jsis applied every launch.- Changing Tor Browser's prefs/chrome (userChrome.css, extra prefs) can make you less uniform than stock Tor Browser users. The CSS styles chrome only, but every deviation should be reviewed against spec §13.
- arti's Tor state/cache dirs use arti defaults (on disk); moving them into the volatile root is a TODO with a guard-stability trade-off.
- Leak test exit-IP check is a stub.
- Tor-space fingerprint: only time zone, locale and user agent are normalised (0.7). Canvas, WebGL, audio, fonts and screen size are the real machine's, so Ghost/Abyss tabs are not as uniform as Tor Browser. The overrides and the extension/HTTPS interstitial code are Windows-only and have not been run on a real Windows machine yet.
Verification done in the dev sandbox (Linux aarch64, no MSVC)
cargo test --workspace(with arti) — 21 unit tests pass (vault roundtrip / wrong-PIN / lock / AAD; Bloom + suffix matching; SOCKS handshake + isolation + sinkhole end-to-end; isolation rotation; leak checks; profile wipe; IPC parsing; shipped Ghost/Abyss profiles pass pref leak checks).cargo clippy --workspace --all-targetsclean (with and without arti).cargo check / clippy --workspace --no-default-features --target x86_64-pc-windows-gnuclean — this type-checks allcfg(windows)code (WFP, DPAPI, VirtualLock, clipboard, named pipe server).- Not verified: MSVC build, running anything on Windows, arti on the Windows target, the Tauri companion (not compiled), userChrome.css against a live ESR build.
Built With
- rust
- turi