Skip to content
Merged

V2 #4

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Android inference speed depends on these reaching llama-cpp-sys-2's build
# script: it hardcodes -march=armv8-a (2014 baseline, no dotprod/i8mm/fp16
# SIMD), and env CFLAGS are appended after its own flags, so the last -march
# wins. Set here (not in shell wrappers) because the Gradle daemon invokes
# cargo with its own frozen environment — exports in scripts/android.sh never
# reach it. Target-scoped names make this a no-op for desktop builds.
[env]
CFLAGS_aarch64_linux_android = "-march=armv8.6-a+fp16"
CXXFLAGS_aarch64_linux_android = "-march=armv8.6-a+fp16"
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@

## The Æther That Is

> **New here?** Read the plain-language introduction: [What is ÆTHER?](docs/WHAT-IS-AETHER.md)

ÆTHER is a Tauri-native desktop browser built for local knowledge work. It turns ordinary browsing into a private research loop: save useful pages, organize them into knowledge hubs, embed them on-device, and ask AiON for citation-backed answers grounded in what you actually captured.

It is not a cloud wrapper and it is not an external chatbot sidebar. The local wisdom path runs in the app process through GGUF models and a bundled llama.cpp runtime.
Expand All @@ -58,8 +60,8 @@ It is not a cloud wrapper and it is not an external chatbot sidebar. The local w
- **iCE Maps**: generate layered concept atlases for research topics. Create a Cartography of Topics for new research hooks.
- **Native Shell**: Tauri, Rust commands, Vite renderer, and no required local server sidecar.

| Grounded local answers | Complexity maps |
| --- | --- |
| Grounded local answers | Complexity maps |
| ------------------------------------------------------------------ | ------------------------------------------------------------ |
| ![AiON grounded answer](docs/images/readme/aether-aion-answer.png) | ![iCE complexity map](docs/images/readme/aether-ice-map.png) |

## Privacy Ice Wall
Expand All @@ -75,18 +77,18 @@ Local by design:
- AiON answers generated by in-process local models
- iCE topic maps

Normal browsing is still normal browsing. Websites can make their own network requests, track sessions, run JavaScript, and communicate with their servers.
Normal browsing is still normal browsing. Websites can make their own network requests, track sessions, run JavaScript, and communicate with their servers.
The privacy boundary applies to ÆTHER's indexing and intelligence pipeline, not to third-party websites loaded in the browser.

## Local Wisdom Setup

Fresh installs use **AiON Launch**, the in-app setup flow for downloading local models into the app-data model directory. The same setup flow is available later from Settings for repair or manual installation.

| Model | Role | Official source | Size |
| ------------- | ----------------------------------------------------------------- | ------------------------------------- | -------: |
| **AiON MiST** | Required embedding model for search, capture, and retrieval | `Qwen/Qwen3-Embedding-0.6B-GGUF` | ~0.64 GB |
| **AiON LiTE** | Smaller, faster chat model for everyday answers and summaries | `google/gemma-4-E2B-it-qat-q4_0-gguf` | ~3.35 GB |
| **AiON WiSE** | Larger chat model for richer synthesis and iCE maps | `google/gemma-4-E4B-it-qat-q4_0-gguf` | ~5.15 GB |
| Model | Role | Official source | Size |
| ------------- | ------------------------------------------------------------- | ------------------------------------- | -------: |
| **AiON MiST** | Required embedding model for search, capture, and retrieval | `Qwen/Qwen3-Embedding-0.6B-GGUF` | ~0.64 GB |
| **AiON LiTE** | Smaller, faster chat model for everyday answers and summaries | `google/gemma-4-E2B-it-qat-q4_0-gguf` | ~3.35 GB |
| **AiON WiSE** | Larger chat model for richer synthesis and iCE maps | `google/gemma-4-E4B-it-qat-q4_0-gguf` | ~5.15 GB |

Install choices:

Expand Down Expand Up @@ -444,7 +446,7 @@ Install Android Studio, then install these SDK pieces through Android Studio's S
- Android NDK
- Android Emulator, if you want emulator testing

Set the Android environment variables in your shell profile:
The `bun run android:*` scripts go through `scripts/android.sh`, which resolves `ANDROID_HOME`, picks the newest installed NDK, and exports the NDK variable spellings the whole toolchain needs (llama.cpp's build script reads `ANDROID_NDK_ROOT`, which the Tauri CLI does not set). If your SDK lives somewhere other than `~/Library/Android/sdk`, set the variables in your shell profile:

```bash
export ANDROID_HOME="$HOME/Library/Android/sdk"
Expand Down Expand Up @@ -507,7 +509,7 @@ Android outputs are generated under:
src-tauri/gen/android/app/build/outputs/
```

Current mobile limitation: the React shell can be packaged for Android, but ÆTHER's current live browser tab surface uses Tauri desktop child webviews. That desktop-only browser surface must be replaced with an Android-compatible browser path before the Android app behaves like the macOS Tauri app.
Android browser behavior: Tauri's per-tab child webviews (`Window::add_child`) are desktop-only, so on Android each browser tab gets a real native `android.webkit.WebView` managed by the Kotlin `TabsPlugin` (`src-tauri/gen/android/.../TabsPlugin.kt`), layered above the app UI exactly where the renderer's tab surface sits. Rust stays the single source of truth for tab state: it drives the plugin through the same `*_native_webview` functions the desktop uses, and navigation, title, and find-in-page events flow back into Rust so the address bar, tab titles, and back/forward history stay correct. The hardware back button walks the in-app layers (find bar → page history → dashboard) before backgrounding the app.

## Linux Build

Expand Down
Binary file modified build/icon.ico
Binary file not shown.
106 changes: 106 additions & 0 deletions docs/WHAT-IS-AETHER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# What is ÆTHER?

**ÆTHER is a web browser with a memory.**

A normal browser forgets. You read twenty pages while researching something, close the tabs, and a week later all that's left is a vague bookmark folder and the hope that you'll find the right page again. ÆTHER is built around a different idea: the pages you find valuable should become _your_ knowledge — saved, organized, searchable by meaning, and ready to answer questions — without any of it ever leaving your computer.

Think of it as three things merged into one app:

1. **A browser** - tabs, address bar, search. Nothing to relearn.
2. **A filing cabinet that organizes itself** — one click saves the page you're reading into a topic collection.
3. **A research assistant who has actually read your files** — ask a question, get an answer built from the pages you saved, with citations pointing back to them.

And the key twist: the "assistant" is an AI that runs entirely **on your own machine**. No cloud, no account, no subscription, no telemetry. Once the models are downloaded, the intelligence works with the network cable unplugged.

---

## The core loop

Everything in ÆTHER revolves around one simple cycle:

```
Browse → Capture → Organize → Ask → (discover something new) → Browse …
```

**Browse.** Use ÆTHER like any browser. Open tabs, search, read.

**Capture.** Found something worth keeping? Hit capture. ÆTHER extracts the readable text of the page (the article, not the ads), stores it locally, and quietly indexes it by _meaning_ — so later you can find it even if you don't remember any exact words from it.

**Organize.** Captures live in **Knowledge Hubs** — collections you create per topic ("Thesis sources", "Kitchen renovation", "Rust learning"). ÆTHER can even suggest which hub a page belongs in, based on what it's about.

**Ask.** This is where it pays off. Open the **AiON** panel and ask a question in plain language. Instead of guessing from generic internet knowledge, AiON answers _from the pages you captured_ and cites them, so every claim is one click away from its source. You can ask about a whole hub, or just about the page you're currently reading.

**Discover.** Answers, related-capture trails, and topic maps surface connections you forgot you had — which usually kicks off the next round of browsing.

---

## A tour of the rooms

ÆTHER is organized as a few distinct "surfaces", each with one job:

### 🏠 The Dashboard

The calm home screen. Your saved **Portals** (favorite sites, one tap away), your Knowledge Hubs with everything captured in them, and your saved iCE maps. This is where you browse _your own_ collection instead of the web.

### 🌐 The Browser

Ordinary web browsing with tabs — plus a capture button and page-aware AI actions always within reach. A new tab opens on a start page with your portals and a search box, not a wall of noise.

### 💬 AiON — the sidekick panel

A sidebar that follows you everywhere. It answers questions grounded in your captures, shows a **semantic trail** of saved pages related to what you're currently reading, and helps route new captures to the right hub. Answers render as clean text with citations and can be copied out.

### 🧊 iCE — the depth explorer

The **Information Complexity Explorer** is ÆTHER's most unusual tool. Give it a topic, and the local AI generates an "iceberg" map of it: the widely-known basics at the surface, and progressively deeper, more specialized layers as you descend. It's a way to _see the shape of a subject_ — and to find research hooks you didn't know existed. Maps can be saved and revisited from the dashboard.

_(For the curious, there are also experimental surfaces — Flow, which draws a graph of how your captures relate to a query, and AiR, an automatic document renderer — tucked behind a developer-mode switch.)_

---

## What does "local AI" actually mean here?

Two small AI models live inside the app, running on your own processor:

- an **embedding model** (the "librarian") that converts every captured page — and every question you ask — into a mathematical fingerprint of its meaning, so search works by _what things are about_, not just keywords;
- a **chat model** (the "writer") that reads the most relevant captured passages and composes the answer you see, with citations.

On first launch, ÆTHER offers to download a recommended model pack (a couple of gigabytes, from official sources). That's the only big download; after that, everything runs offline. You can pick a lighter, faster writer or a heavier, smarter one depending on your hardware.

**The honest privacy picture:** everything ÆTHER _itself_ produces — captured text, hubs, indexes, questions, answers, topic maps — stays on your machine, full stop. But normal browsing is still normal browsing: the websites you visit see your requests and run their scripts just like in any browser. ÆTHER's privacy promise is about its intelligence pipeline, not a cloak of invisibility for the web.

---

## What it is — and isn't

**ÆTHER is good at:**

- Being your everyday browser while quietly building a private research library
- Answering "where did I read that…?" — by meaning, not keywords
- Grounded Q&A over your own sources, with citations you can verify
- Mapping how deep a topic goes before you dive in (iCE)
- Working offline, on your hardware, with zero recurring costs

**ÆTHER is not:**

- A cloud chatbot — it won't answer from the whole internet, and that's the point: answers come from _your_ captured sources
- A privacy shield for browsing itself — websites behave as they do in any browser
- A lightweight app for very old machines — the local AI wants a reasonably modern computer (8 GB of RAM is the practical floor; see the README for specifics)

---

## Where it runs

macOS (Apple Silicon and Intel), Windows, Linux (including ARM devices like the Raspberry Pi 5), and an Android build with native browser tabs. One codebase, no server component, nothing to host.

---

## Try it in five minutes

1. Install ÆTHER and let **AiON Launch** download the recommended models.
2. Browse to any article you find interesting and hit **capture**.
3. Make a hub for the topic and drop the capture in (or accept the suggested hub).
4. Capture two or three more related pages.
5. Open **AiON** and ask a question about the topic.

The answer you get will be built from those pages — cited, private, and generated entirely on your machine. That's ÆTHER in a nutshell: **browse the web, keep the good parts, and let your own library talk back to you.**
22 changes: 15 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
{
"name": "aether-browser",
"version": "1.0.28",
"version": "1.0.29",
"productName": "ÆTHER",
"description": "Local AI-powered browser and knowledge hub",
"keywords": [
"knowledge",
"ai",
"local",
"research",
"browser",
"rag"
],
"author": "canpixel.com",
"homepage": "https://canpixel.com",
"license": "PolyForm-Strict-1.0.0",
Expand All @@ -17,16 +25,16 @@
"release": "bun scripts/release.ts",
"start": "tauri dev",
"dev": "tauri dev",
"dev:vite": "vite --host 127.0.0.1 --port 1420",
"dev:vite": "vite",
"build:vite": "vite build",
"prepare:dmg-background": "bash scripts/prepare-dmg-background.sh",
"build": "bun run typecheck && tauri build --bundles app && bun run dmg",
"dmg": "bash scripts/make-styled-dmg.sh",
"android:init": "tauri android init",
"android:dev": "tauri android dev",
"android:build": "tauri android build",
"android:build:apk": "tauri android build --apk",
"android:build:aab": "tauri android build --aab",
"android:init": "bash scripts/android.sh init",
"android:dev": "bash scripts/android.sh dev",
"android:build": "bash scripts/android.sh build",
"android:build:apk": "bash scripts/android.sh build --apk",
"android:build:aab": "bash scripts/android.sh build --aab",
"linux:arm64:build": "bash scripts/build-linux.sh",
"linux:arm64:export": "bash scripts/build-linux.sh",
"linux:arm64:deb": "LINUX_BUNDLES=deb bash scripts/build-linux.sh",
Expand Down
51 changes: 51 additions & 0 deletions scripts/android.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
# Wrapper for `tauri android <dev|build|init>`.
#
# The Tauri CLI needs ANDROID_HOME / NDK_HOME, and llama-cpp-sys-2's build
# script separately requires one of ANDROID_NDK / NDK_ROOT / ANDROID_NDK_ROOT —
# it does not read NDK_HOME, so a plain `tauri android build` fails while
# compiling llama.cpp. This resolves the SDK/NDK once and exports every
# spelling the toolchain wants.
set -euo pipefail

ANDROID_HOME="${ANDROID_HOME:-$HOME/Library/Android/sdk}"
if [ ! -d "$ANDROID_HOME" ]; then
echo "Android SDK not found at $ANDROID_HOME — install it or set ANDROID_HOME." >&2
exit 1
fi

if [ -z "${NDK_HOME:-}" ]; then
NDK_HOME="$(find "$ANDROID_HOME/ndk" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort -V | tail -1)"
fi
if [ -z "$NDK_HOME" ] || [ ! -d "$NDK_HOME" ]; then
echo "Android NDK not found under $ANDROID_HOME/ndk — install one via sdkmanager or set NDK_HOME." >&2
exit 1
fi

export ANDROID_HOME NDK_HOME
export ANDROID_NDK_ROOT="${ANDROID_NDK_ROOT:-$NDK_HOME}"
export ANDROID_NDK="${ANDROID_NDK:-$NDK_HOME}"

# llama-cpp-sys-2 hardcodes -march=armv8-a (2014 baseline) for Android, which
# leaves ggml's dotprod/i8mm/fp16 quantized-matmul kernels compiled out — CPU
# prefill runs several times slower than the silicon allows. Env CFLAGS are
# appended after the build script's own flags, and clang honors the last
# -march, so this override wins. armv8.6-a mandates dotprod+i8mm+bf16 (any
# Snapdragon 8 Gen 1+ / recent flagship); override AETHER_ANDROID_MARCH for
# older devices, e.g. armv8.2-a+dotprod+fp16.
: "${AETHER_ANDROID_MARCH:=armv8.6-a+fp16}"
export CFLAGS_aarch64_linux_android="-march=${AETHER_ANDROID_MARCH}${CFLAGS_aarch64_linux_android:+ ${CFLAGS_aarch64_linux_android}}"
export CXXFLAGS_aarch64_linux_android="-march=${AETHER_ANDROID_MARCH}${CXXFLAGS_aarch64_linux_android:+ ${CXXFLAGS_aarch64_linux_android}}"

# llama.cpp does not compile for 32-bit ARM, and every device that can run
# ÆTHER's local AI stack is arm64 anyway — so builds default to aarch64 only
# unless the caller picks targets explicitly.
args=("$@")
if [ "${1:-}" = "build" ]; then
case " $* " in
*" --target "*) ;;
*) args+=(--target aarch64) ;;
esac
fi

exec bun tauri android "${args[@]}"
51 changes: 51 additions & 0 deletions scripts/make-windows-icon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""Generate build/icon.ico from resources/icon.png.

The source artwork uses the macOS convention of a large transparent inset,
which makes the Windows taskbar icon render visibly smaller than other apps.
This script crops the artwork to its visible bounds (plus a small margin) so
the .ico fills its canvas the way Windows icons are expected to.

Usage: python3 scripts/make-windows-icon.py
"""

from pathlib import Path

from PIL import Image

ROOT = Path(__file__).resolve().parent.parent
SOURCE = ROOT / "resources" / "icon.png"
TARGET = ROOT / "build" / "icon.ico"

# Fraction of the cropped artwork size kept as transparent margin on each side.
MARGIN = 0.02
SIZES = [16, 24, 32, 48, 64, 128, 256]


def main() -> None:
image = Image.open(SOURCE).convert("RGBA")
bbox = image.getchannel("A").getbbox()
if bbox is None:
raise SystemExit(f"{SOURCE} is fully transparent")

left, top, right, bottom = bbox
margin = round(max(right - left, bottom - top) * MARGIN)
left = max(left - margin, 0)
top = max(top - margin, 0)
right = min(right + margin, image.width)
bottom = min(bottom + margin, image.height)

# Pad the crop to a square so the artwork is not stretched.
width = right - left
height = bottom - top
side = max(width, height)
square = Image.new("RGBA", (side, side), (0, 0, 0, 0))
square.paste(image.crop((left, top, right, bottom)), ((side - width) // 2, (side - height) // 2))

frames = [square.resize((size, size), Image.LANCZOS) for size in SIZES]
frames[-1].save(TARGET, format="ICO", append_images=frames[:-1], sizes=[(s, s) for s in SIZES])
print(f"Wrote {TARGET} with sizes {SIZES} (content fills {(1 - 2 * MARGIN) * 100:.0f}% of canvas)")


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "aether"
version = "1.0.28"
version = "1.0.29"
description = "Local AI-powered browser and knowledge hub"
authors = ["canpixel.com"]
edition = "2021"
Expand Down Expand Up @@ -33,3 +33,13 @@ uuid = { version = "1", features = ["v4", "serde"] }
[target.'cfg(target_os = "macos")'.dependencies]
# Cargo unions features across target tables, so on macOS llama.cpp gets metal + sampler.
llama-cpp-2 = { version = "0.1.146", default-features = false, features = ["metal"] }

# llama.cpp compiled at -O0 (the dev-profile default for native deps) runs CPU
# inference 20-50x slower than release. Desktop dev hides this behind Metal;
# Android dev builds do real CPU prefill, so keep the inference stack optimized
# even in dev.
[profile.dev.package.llama-cpp-sys-2]
opt-level = 3

[profile.dev.package.llama-cpp-2]
opt-level = 3
Loading
Loading