Skip to content

Latest commit

Β 

History

236 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Qwave β€” a browser that proves what it sends

Qwave CI Platform Swift License: MIT 8b.IS Docs Public Docs MEM|8 Wave

Qwave hero

zero-fear browsing Β· one sovereign core Β· two lanes

the WebKit engine you already trust, wrapped in boundaries you can read

stable keeps WebKit's defaults Β· nightly flips every experimental feature ON Β· the decisions live in Rust

Qwave is an open-source, WebKit-native browser for people who want stronger boundaries around browsing data without giving up the system engine. It runs on macOS 14+ and on iPhone, iOS 15+ (minimum device: iPhone 13), sharing one sovereign core between the two lanes. Part of the 8b.IS Ecosystem and documented in the 8b.IS Documentation Hub. It combines per-container storage universes, native content shields, tab hibernation, an on-device page summarizer, a Rust sovereign core (the egress allowlist and the MEM8 wave substrate, compiled as a zero-dependency staticlib and linked into the app), and an egress allowlist that makes Qwave's own network activity auditable.

Qwave is privacy-oriented software, not a promise of anonymity. Read the threat model, the network inventory, and the 8b.IS Architectural Documentation.

πŸ“‘ Contents

🧭 What makes it different

  • Container universes β€” each profile gets its own WebKit data store; ephemeral tabs use non-persistent storage and are never restored.
  • Native shields & MV3 DNR β€” EasyList/uBlock rules compiled to native WebKit C++ content rules at sub-millisecond speeds with per-host JavaScript controls.
  • Apple Silicon UMA SQLite β€” 256MB zero-copy mmap_size, WAL mode, 64MB cache, and 4 P-core sorting threads for instant URL completions and history search.
  • Persistent FaviconStore β€” container-isolated SQLite BLOB caching for instant favicon rendering with zero cross-container telemetry leakage.
  • On-device omnibox β€” suggestions from local history, bookmarks, open tabs, and actions, ranked on every keystroke. Network search suggestions are opt-in and off by default, so keystrokes never leave the Mac unless you turn them on.
  • Energy-aware tabs β€” background WebKit views can be hibernated while tab state, history, and scroll position remain restorable.
  • Rust sovereign core β€” the Category-A egress allowlist and the 79-byte MEM8 wave frame live in core/ (zero-dependency Rust) behind a small C ABI; Swift keeps the WebKit shell, the decisions live in Rust.
  • MemoryWave β€” container-scoped encrypted memory storage with opt-in, AI-agnostic inference providers. Stored memory bodies never reach a remote provider: WaveDirector composes recalled memories into the prompt only when the provider is on-device (WaveDirector.swift:415-421), and MemoryWavePolicy.decide denies a remote request that declares it carries them (.deny(.cognitiveEgress), MemoryWavePolicy.swift:81-82) β€” a declared-intent gate plus a caller-side guard, not a filter on the outgoing prompt. The caller-side guard is the one doing the work today; the policy is a tripwire behind it, dormant while ask declares stored memory only for the on-device provider (WaveDirector.swift:364). Recalled rows a model wrote or an automatic page capture produced are carried under a separate untrusted-data heading rather than beside the pins you wrote yourself (WaveDirector.recalledMemoryBlock, :49-70). One carve-out: a timeline summary with a remote provider sends the title, time, and host of every record in the window, and no snippets (WaveDirector.swift:199-200, MemoryTimeline.swift:63-80). Pages you explicitly summarise or ask about are sent to the provider you configured; see docs/NETWORK.md.
  • Summarize β€” on-device page summarization via Apple's FoundationModels. No streaming, no network, no model output that can act on the browser.
  • AutoFill β€” passwords and passkeys through an ASCredentialProvider extension and WebAuthn, kept in the OS / iCloud keychain and deliberately separate from the post-quantum / DeviceKeyManager stack.
  • Spaces β€” the container universes surface as first-class Spaces with a vertical tab sidebar; a system Focus filter can switch the active Space and tighten shields.
  • The iPhone lane runs the same sovereign core β€” QwaveIOS links the same Rust staticlib (mem8 waves, Phoenix, egress, telemetry) through the shared SovereignCore module, with a battery policy that reacts to Low Power Mode and thermal pressure, and native iOS UX (pull-to-refresh, keyboard shortcuts, share sheet, haptics).
  • Two search engines, both honest β€” Ecosia (the default) and DuckDuckGo: the only engines with a vetted keyless autocomplete endpoint. Qwave will not ship an engine whose suggestions it cannot fetch without a key.

πŸ”¬ How it works β€” concept diagrams

Container isolation

Containers

Each profile owns a persistent WKWebsiteDataStore; burner tabs get a non-persistent store and leave nothing behind. See ContainerRegistry, WebViewFactory.

               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
               β”‚                  Qwave.app                    β”‚
               β”‚                                               β”‚
 profile A ───▢│ WKWebsiteDataStore(.persistent)  cookies,     β”‚
               β”‚   └─ tabs A1…An                localStorage,  β”‚
               β”‚                              IndexedDB,       β”‚
 profile B ───▢│ WKWebsiteDataStore(.persistent)  service      β”‚
               β”‚   └─ tabs B1…Bm                 workers       β”‚
               β”‚                              ─────────────    β”‚
 burner tab ──▢│ WKWebsiteDataStore(.nonPersistent)            β”‚
               β”‚   └─ closed β‡’ gone, never restored           β”‚
               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Shields pipeline

Shields

The blocklist ships as a committed snapshot (zero launch egress), compiles to WKContentRuleList, and navigation waits for it. See ShieldsDirector, UBORuleListCompiler, docs/BLOCKLIST.md.

 blocklist.json ──(committed snapshot, scripts/update-blocklist.sh)──┐
      β”‚                                                              β”‚
      β–Ό                                                              β”‚
 UBOFilterParser ──▢ UBORuleListCompiler ──▢ WKContentRuleList       β”‚
      β”‚                                          β”‚                  β”‚
      β–Ό                                          β–Ό                  β”‚
 per-site toggles ◀── ShieldsDirector ◀── applyListsThen(to:)       β”‚
      β”‚                                          β”‚                  β”‚
      β–Ό                                          β–Ό                  β”‚
 HTTPSFirstUpgrader                NavigationCoordinator gates the   β”‚
                                   first navigation on shields       β”‚
                                   .whenReady() β€” never bypassed β”€β”€β”€β”˜

Energy governor and hibernation

A pure conditions β†’ tier β†’ policy mapping; memory pressure demotes the tier before anything is decided. See EnergyGovernor, TabHibernator, docs/ENERGY.md, docs/PERF.md.

 thermal .nominal ─┐
 lowPower off ─────┼──▢ tier .normal   ──▢ tabs alive, AI allowed
 occlusion false β”€β”€β”˜
                      underMemoryPressure ──▢ tier .conserve / .critical
                                                      β”‚
                                                      β–Ό
                                     hibernate background tabs,
                                     pause/suspend media,
                                     quietly defer AI inference

Summarize (on-device AI)

Explicit command only: extract, generate, render inert text. See SummarizeSession, docs/SUMMARIZE.md, readability-probe, foundation-models-probe.

 βŒ₯⌘S / toolbar ──▢ available?  macOS 26 + Apple Silicon + Apple Intelligence
        β”‚              β”‚
        β”‚              β”œβ”€ modelNotReady ──▢ re-check on foreground (self-heals)
        β”‚              └─ otherwise ─────▢ vanish cleanly, no grey-out, no nag
        β–Ό
 energy tier == .normal? ── no ──▢ quietly absent this moment
        β”‚ yes
        β–Ό
 extractor.js (F1 1.00 Γ—4, 0.96 docs) ──▢ LanguageModelSession.respond
        β”‚                                     respond-only: the stream path
        β”‚                                     refuses this workload on 26.4
        β”‚                                     retry ≀ 3 on nondeterministic
        β–Ό                                     refusals; neutral failure text
 inert selectable text panel ◀── nothing downstream of the model acts;
                                 zero network egress

The Rust core

The sovereign decisions β€” which hosts Qwave's own code may contact, and whether a MEM8 wave frame is intact β€” live in core/, a zero-dependency Rust crate. It compiles to a staticlib per Apple platform (core/build-apple.sh maps Xcode's ARCHS onto cargo targets and lipos the slices), is force-loaded into both app targets so every decision surface is present in every binary, and is spoken through a small C ABI (core/include/qwave_core.h) from the shared SovereignCore module in QwaveKit (Packages/QwaveKit/Sources/SovereignCore). The egress decision the omnibox makes before sending a suggestion query is the Rust core's, not Swift's β€” on the iPhone exactly as on the Mac.

 core/  (Rust, zero deps)
   β”œβ”€β”€ egress.rs     Category-A allowlist β€” permits(host), subdomain-aware
   β”œβ”€β”€ wave.rs       the 79-byte WaveInt frame: encode, validate, grid coord
   β”œβ”€β”€ phoenix.rs    the Phoenix protocol β€” marine gate, custodian, verdict
   β”œβ”€β”€ mem16.rs      governing/recovery sequences (mem|16-10 under `nightly`)
   β”œβ”€β”€ telemetry.rs  the PII scrubber + histogram aggregator (qwave-agg)
   └── rational.rs   the MEM8 rational, reduced + checked arithmetic

The WireGuard/VPN layer (PacketTunnel, WireGuardKit + Go bridge, Zig packet filter, VPNKit, PostQuantum) was removed β€” a tunnel is a different layer, not the browser's requirement. If it returns, it returns as a separate package.

MemoryWave

Container-scoped, encrypted memory substrate with an explicit provider seam β€” local by default, remote only when configured. See MemoryProvider, MemoryStore.

 page text ─▢ ArticleExtractor ─▢ MEM8 store (container-scoped, encrypted)
      β–²                                    β”‚
      └──────── MemoryProviding β—€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               default: OnDeviceProvider  (local-first; remote only when
               configured explicitly)

Egress audit

Qwave's own egress is held to a committed allowlist, and β€” since #77 β€” EgressGuard, a URLProtocol wired into every fixed-host client, checks it at runtime instead of only in review. The shields launch path is separately asserted request-free by a test whose reach is bounded (caveats below). See EgressAllowlist, EgressGuard, docs/NETWORK.md.

 any wired Qwave network client
      β”‚
      β–Ό
 EgressGuard (URLProtocol) ── checks EgressAllowlist.permits(host:)
      β”‚                        allowed β†’ steps aside, request proceeds
      β”‚                        blocked β†’ fails the request, logs, records
      β–Ό
 allowlisted hosts (3): github.com          Sparkle appcast
                        api.x.ai            default remote AI endpoint
                        duckduckgo.com      omnibox suggestions (opt-in)
      β”‚
      β”‚  + on a request Memory Wave marked, and only that request:
      β”‚    the endpoint host you configured, read live at check time.
      β”‚    Not on the allowlist; no other client can reach it.
      β–Ό
 launch path ── EgressGuardTests URLProtocol recorder ──▢ zero requests
                (no launch-time blocklist fetch since 0.4.4)

Two honest caveats remain. EgressGuard only reaches a client that installs it: URLSession.shared gets it for free from the process-wide URLProtocol.registerClass in main.swift, but any session Qwave constructs β€” including one built from URLSessionConfiguration.default β€” has to call EgressGuard.install(into:) explicitly (QwaveSupport/EgressGuard.swift:12-24, pinned by EgressGuardTests.swift:614-637). URLSession.mullvadPinned(), DuckDuckGoSuggestionProvider and Memory Wave's remote provider do; a new fixed-host client that skips this would not be caught here, only by review. Memory Wave's provider was itself the standing example of that gap: its session is ephemeral, it never called install(into:), and so api.x.ai sat on the allowlist without one provider request ever being checked against it. It is gated now, against the committed list plus β€” on its own request, and nobody else's β€” an exact match on whatever HTTPS endpoint you configured (EgressGuard.markUserConfiguredEndpoint(_:), stamped at MemoryWave/MemoryProvider.swift:108). The mark names no host, so it grants nothing by itself: the guard resolves it against MemoryWavePreferences.egressPermittedHost read live at check time (EgressGuard.swift:236). Nothing is cached, so changing the endpoint or switching the provider off revokes the previous host immediately, with no inference needed in between, and no other client in the process ever gains it.

Two Category-A clients are still not gated, and for two different mechanical reasons β€” conflating them once broke a feature. FaviconLoader builds its own session and never installs the guard, so the guard never sees it. The remote-markdown fetch is the opposite: it runs on URLSession.shared, which the global registration does reach, and is exempted by an explicit EgressGuard.markPageDriven(_:) marker at its single call site (BrowserCore/NavigationCoordinator.swift:363) β€” per request, not per host or per session. The shields launch-path assertion is the one thing here asserting the absence of a request rather than the shape of one: it registers a URLProtocol recorder over the shields launch path and asserts nothing was requested; the test builds its own ShieldsDirector rather than running the app's launch sequence, and WebKit's own network process (Category C) is invisible to any URLProtocol-based check, EgressGuard included.

πŸ› Architecture at a glance

Qwave browser

Qwave.app                         AppKit shell + SwiftUI settings
β”œβ”€β”€ QwaveKit                      local Swift package, Swift 6 language mode
β”‚   β”œβ”€β”€ BrowserCore                tabs, containers, navigation, hibernation
β”‚   β”œβ”€β”€ Shields                    content rules and HTTPS-First
β”‚   β”œβ”€β”€ Persistence                actor-isolated SQLite stores
β”‚   β”œβ”€β”€ MemoryWave                 encrypted MEM8 memory substrate
β”‚   β”œβ”€β”€ Summarize                  on-device page summarization (macOS 26+)
β”‚   β”œβ”€β”€ WebExtensions               MV3 registry and browser.* bridge
β”‚   β”œβ”€β”€ URLIdentity                 WHATWG/WebKit-compatible host identity
β”‚   β”œβ”€β”€ FeatureFlags                guarded WebKit SPI feature access
β”‚   β”œβ”€β”€ WebCredentials              keychain-only passwords + passkeys (AutoFill)
β”‚   β”œβ”€β”€ SovereignCore               the Swift face over the core's C ABI
β”‚   └── QwaveSupport                logging, keychain, egress guard
└── core/                          the Rust sovereign core (staticlib, C ABI)
    β”œβ”€β”€ egress.rs                   the Category-A allowlist
    β”œβ”€β”€ wave.rs                     the 79-byte MEM8 WaveInt frame
    β”œβ”€β”€ phoenix.rs                  the Phoenix protocol (marine gate…verdict)
    β”œβ”€β”€ telemetry.rs                the privacy scrubber + histogram aggregator
    └── build-apple.sh              ARCHS β†’ cargo targets β†’ lipo (both lanes)
QwaveIOS.app                      SwiftUI shell, iOS 15+ (iPhone 13 minimum)
β”œβ”€β”€ the same QwaveKit + core        one SovereignCore, one decision surface
β”œβ”€β”€ battery policy                  Low Power / thermal β†’ conserve / critical
└── native UX                       refresh, shortcuts, share sheet, haptics

Dependency direction is one-way (app β†’ QwaveKit; feature modules never reach into the AppKit shell) β€” see docs/ARCHITECTURE.md for the exact graph, isolation rules, data flow, and test boundaries.

πŸ—Ί Module map

  • BrowserCore β€” the convergence point: tabs, containers, navigation, hibernation. Key types: TabManager, ContainerRegistry, TabHibernator, EnergyGovernor, NavigationCoordinator, SessionRestorer, OmniboxSuggester. source
  • Shields β€” uBO β†’ content-rule compilation, per-site toggles, HTTPS-First. Key types: ShieldsDirector, UBORuleListCompiler, HTTPSFirstUpgrader. source Β· docs/BLOCKLIST.md
  • Persistence β€” actor-isolated SQLite stores. source
  • MemoryWave β€” encrypted memory substrate. Key types: MemoryStore, MemoryProvider (MemoryProviding), ArticleExtractor, WaveInt. source
  • Summarize β€” respond-only FoundationModels wrapper. Key types: SummarizeSession, SummarizePolicy, ArticleExtractor (byte-identical probe script). source Β· docs/SUMMARIZE.md
  • WebExtensions β€” MV3 registry and browser.* bridge. Content scripts are not active in the shipping app: BrowserWindowController.ensureWebView installs the bridge and nothing else, so a manifest's content_scripts entry is never injected into a tab and ContentScriptEngine is reached only from tests (WebExtensions/WebExtensionHost.swift:8-20). source
  • WebCredentials β€” keychain-only website logins and passkeys for AutoFill. Foundation + Security only; never links the crypto/VPN stack. Key types: WebCredential, WebCredentialStore. source
  • URLIdentity β€” WHATWG/WebKit-compatible host identity (fixes a shielding bypass, per research/collections-foundation/weburl.md). source
  • FeatureFlags β€” guarded _WKFeature access with the unavailable / emptySurface / available tri-state. source
  • QwaveSupport β€” QwaveLog (privacy-classified), keychain, egress allowlist. source

⚑ Install & update

Homebrew (macOS)

brew tap 8b-is/tap

# stable release (signed, notarised β€” Gatekeeper accepts it directly):
brew install --cask 8b-is/tap/qwave
brew upgrade --cask 8b-is/tap/qwave          # update to the next tagged release

# bleeding edge (nightly: every experimental WebKit feature ON, mem|16-10 linked):
brew install --cask 8b-is/tap/qwave-nightly
brew upgrade --cask --greedy 8b-is/tap/qwave-nightly  # re-pull the rolling build

The casks install the ready DMGs from the GitHub Releases β€” no build on your machine (the build-from-source formulae were retired: xcodebuild's sandboxed package resolution cannot nest inside Homebrew's build sandbox). The app lands in /Applications:

open -a Qwave

GitHub Releases (macOS)

One-command nightly installer

No Homebrew? The installer script does the same thing:

git clone https://github.com/8b-is/qwave.git && cd qwave
tools/install-nightly.sh            # build bleeding edge, swap into /Applications

# update to the latest main and stay bleeding edge:
git pull && tools/install-nightly.sh

Build from source

Requirements:

  • macOS 14 or newer (desktop lane)
  • iPhone with iOS 15 or newer (iPhone 13 minimum; phone lane)
  • Xcode 16+ (the package uses Swift 6 language mode)
  • XcodeGen
  • Rust (rustup) β€” the sovereign core builds a staticlib at compile time. Universal Release builds (-destination 'platform=macOS') need both Apple std targets: rustup target add aarch64-apple-darwin x86_64-apple-darwin.

Package tests:

swift test --package-path Packages/QwaveKit -c release

Generate and build the app:

xcodegen generate --spec project.yml
xcodebuild \
  -project Qwave.xcodeproj \
  -scheme Qwave \
  -configuration Release \
  -destination 'platform=macOS' \
  CODE_SIGNING_ALLOWED=NO \
  CODE_SIGN_IDENTITY= \
  build

# the iPhone lane (iOS 15 floor, iPhone 13 minimum):
xcodebuild \
  -project Qwave.xcodeproj \
  -scheme QwaveIOS \
  -configuration Release \
  -destination 'generic/platform=iOS Simulator' \
  CODE_SIGNING_ALLOWED=NO \
  CODE_SIGN_IDENTITY= \
  build

The nightly channel flips all experimental WebKit features ON and links the mem|16-10 sovereign library; stable keeps WebKit's defaults and Qwave's MIT posture.

Remove a previous version

osascript -e 'tell application "Qwave" to quit'
rm -rf /Applications/Qwave.app
# your profile, memory, and history (delete only if you want a clean slate):
rm -rf ~/Library/Application\ Support/Qwave
rm -rf ~/Library/Caches/is.8b.qwave
defaults delete is.8b.qwave 2>/dev/null || true

Qwave has no system extension in the current tree (the VPN layer was removed); nothing else lingers.

project.yml is the source of truth. Never hand-edit the generated Qwave.xcodeproj. Distribution signing follows docs/SIGNING.md; toolchain pinning lives in docs/PINNING.md. The iPhone lane ships the same WebKit + shields core in a SwiftUI shell (Sources/QwaveIOS); the VPN layer was removed β€” a tunnel is a different layer, not the browser's requirement.

βš™ Swift 6 engineering rules

  • QwaveKit targets use Swift 6 language mode and complete strict concurrency.
  • Persistence, MemoryWave, and service state use actors or explicit main-actor ownership; cross-boundary values are Sendable value types.
  • Async WebKit and persistence APIs stay async at their boundary; do not add blocking queues or semaphore bridges to silence compiler diagnostics.
  • New network clients must update the committed egress allowlist and tests.

πŸ“ Repository map

Packages/QwaveKit/       Swift package and headless tests
Sources/QwaveApp/        AppKit shell and SwiftUI panes
Sources/QwaveIOS/        the iPhone lane (SwiftUI shell, iOS 15+)
Sources/CredentialProvider/  the AutoFill app extension
core/                    Rust sovereign core (staticlib + C ABI header)
Resources/               plists, entitlements, bundled rules
docs/                    architecture, network, signing, design site
research/                evaluated platform/package notes + probes
tools/                   install-nightly.sh, telemetry-export
project.yml              XcodeGen source of truth

🏷 Versioning & releases

project.yml is the single source of truth for the version. Every target (Qwave, CredentialProvider, QwaveIOS) declares the same two numbers:

  • CFBundleShortVersionString β€” the semver, currently 2.0.4.
  • CFBundleVersion β€” major*10000 + minor*100 + patch (2.0.0 β†’ 20000). Sparkle compares this number, and the release workflow fails the tag if any target disagrees.

The v2.0.0 major covers the Rust-core rewrite: VPN layer removal, the sovereign core (core/), the stable/nightly channel split, and the removal of Go/Zig from the build. v2.0.4 is the current release (2026-10-03): signed, notarised, stapled, with the Sparkle appcast attached β€” existing installs update in place.

Releases: git tag v2.0.0 && git push origin v2.0.0 runs .github/workflows/release.yml (signed + notarised DMG and the Sparkle appcast when the secrets are configured; unsigned zip otherwise). Nightly ships as a rolling prerelease via scripts/release-nightly.sh. The full procedure lives in docs/RELEASING.md.

πŸ“š Documentation

Product and engineering specs:

Research (measured, version-stamped, Qwave-specific):

🚦 Status

Shipped through the v1.0.0 line: the browser core, shields, WebExtensions MV3 bridge, MemoryWave, Summarize (macOS 26+ on Apple Silicon with Apple Intelligence), the signed release workflow, and the Swift 6 concurrency migration β€” all covered by package tests.

v2.0.0 is the Rust-core line: the VPN layer (PacketTunnel, WireGuardKit, Go+Zig, VPNKit, PostQuantum) was removed, the sovereign decisions (egress, MEM8 waves, Phoenix, telemetry scrubbing) moved into core/, the stable/nightly channel split became real (nightly actually links the mem|16-10 library and flips the WebKit experimental features ON), and the WebAuthn rpId check now consults a real public-suffix list.

Also in flight: downloads UI, crash-safe session restore, a qwave://diagnostics telemetry page, VoiceOver accessibility on the chrome, on-device semantic memory recall, a command palette, first-run bookmark import with Spotlight entities, container-bound Focus filters with a Spaces sidebar, keychain-only AutoFill (passwords + passkeys), a zero-egress Safe Browsing host-set (shipped as a sample list β€” see docs/SAFE-BROWSING.md for sourcing a real feed), and the iPhone lane β€” now a full lane: the same WebKit + shields + Rust core (mem8/Phoenix/egress/telemetry), a Low Power / thermal battery policy, and native iOS UX, at the iOS 15 floor on an iPhone 13.

βš– License & legal

Qwave is released under the MIT License. Copyright Β© 2026 8b.is / Peter Lodri.

  • Privacy policy β€” what is stored, what leaves the device, and when
  • Third-party notices β€” licenses for the vendored PSL data, Sparkle, mem|16-10 (nightly only, AGPL-3.0), and the SwiftPM dependencies

About

🌊 Sovereign WebKit-native macOS browser combining Firefox Containers, Brave Shields, Tab Hibernation, and WireGuard VPN.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages