diff --git a/.gitignore b/.gitignore index 67be676..02111f9 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,14 @@ nostr_core_lib/ # auto-generated embedded web content (built by embed_web_files.sh) src/embedded_web_content.c src/embedded_web_content.h + +# binary media files for local-site test (too large for git) +tests/local-site/assets/*.mp3 +tests/local-site/assets/*.m4a +tests/local-site/assets/*.mp4 +tests/local-site/assets/*.webm +tests/local-site/assets/*.ogg +tests/local-site/assets/*.ogv +tests/local-site/assets/*.wav +tests/local-site/assets/*.avi +tests/local-site/assets/*.mkv diff --git a/README.md b/README.md index c8f88cb..32827cf 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,13 @@ because trust moves to the layer where it belongs: your keys. 4. **Deprecated web security, deliberately.** Same-origin policy, CORS, and certificate enforcement are stripped so pages (and the agent runtime to come) can freely call any endpoint — including FIPS mesh services — without - the workarounds traditional browsers force on automators. + the workarounds traditional browsers force on automators. Security moves + up to the **Qube level** (isolation per VM), so the browser doesn't need + the traditional in-browser security model. +5. **Local file browsing.** Open and operate multipage websites directly from + `file://` with no web server — cross-origin access between local files, + `fetch()`/XHR, iframes, storage, media playback, all work. See + [Local file browsing](#local-file-browsing) below. ## Nostr interaction policy @@ -67,25 +73,28 @@ Conversations and skills are still stored as the same Nostr event kinds (30078 for conversations, 31123 for skills) with the same tags, so they're **compatible** across projects — just not live-synced. -## Non-goals (for now) - -- Agent integration, multi-window agent hosts, didactyl hosting, and the - broader "browser as agent runtime" vision are documented in - [`docs/architecture.md`](docs/architecture.md) and the linked plans, but - **not in scope for the first build**. First: a usable browser with basic - Nostr signing. Then the rest. - ## Current status -Working browser: a C99 + WebKitGTK window with multi-tab support, a URL bar -per tab, Nostr login, and `window.nostr` injection. Verified loading -`https://laantungir.net` cleanly. See -[`docs/webkit-poc-findings.md`](docs/webkit-poc-findings.md) for the friction -report from the POC phase (and the Servo fallback exploration). +Working browser with a broad feature set: -``` -[loaded] https://laantungir.net/ -- title: Laan Tungir -``` +- **Multi-tab browsing** with session restore, tab drag/reorder, context menus +- **Nostr login** (GTK dialog or CLI flags) with multiple methods: generate, + local (nsec), seed (BIP-39), readonly (npub), NIP-46, n_signer hardware +- **`window.nostr` injection** (NIP-07) into every page +- **Agent MCP server** — 100 tools for browser automation via Streamable HTTP + at `http://localhost:17777/mcp` +- **Local file browsing** — open and operate multipage websites directly from + `file://` with no web server (see below) +- **Bookmarks, history, search** — built-in bookmark manager, browsing history, + and search engine integration +- **Keyboard shortcuts** — standard browser shortcuts (Ctrl+T, Ctrl+W, Ctrl+L, + Ctrl+Tab, etc.) +- **Per-user profiles** — separate identities and settings per profile +- **Embedded web content** — `sovereign://` pages for settings, bookmarks, + profile, and agent chat/config + +See [`docs/webkit-poc-findings.md`](docs/webkit-poc-findings.md) for the +friction report from the POC phase. ## Build @@ -94,9 +103,14 @@ Requires Debian 13 (trixie) or similar with WebKitGTK 4.1 dev headers: ```bash sudo apt install libwebkit2gtk-4.1-dev make -./sovereign_browser [url] +./browser.sh start [url] # build + launch (detached, won't hang terminal) ``` +> **Note:** Always use `./browser.sh` to start/stop the browser. Running +> `./sovereign_browser` directly will hang the terminal because the GUI +> process keeps stdout/stderr open. See `./browser.sh --help` or +> [`.roo/agents.md`](.roo/agents.md) for details. + ## Command-line flags Run `./sovereign_browser --help` for the full list. Flags are parsed before @@ -158,21 +172,22 @@ credentials. ### Examples ```bash -# Existing behavior — still works -./sovereign_browser https://example.com +# Basic usage (via browser.sh — recommended) +./browser.sh start https://example.com # Generate a fresh key, skip the login dialog, open a page -./sovereign_browser --login-method generate --url https://example.com +./browser.sh start --login-method generate --url https://example.com # Local key from env, custom agent port -./sovereign_browser --login-method local --nsec "$NSEC" --port 18888 +./browser.sh start --login-method local --nsec "$NSEC" --port 18888 # Read-only (npub), no agent server, two tabs -./sovereign_browser --login-method readonly --npub npub1... \ +./browser.sh start --login-method readonly --npub npub1... \ --no-agent --url https://a.com --url https://b.com -# Via browser.sh (forwards extra args) -./browser.sh start --login-method generate --url https://example.com +# Open a local website (no web server needed) +./browser.sh start --login-method generate \ + --url "file:///path/to/website/index.html" ``` ## Architecture (summary) @@ -195,7 +210,7 @@ credentials. ▼ ┌─────────────────────────────────────────────────────┐ │ WebKitGTK (system lib, ~35 MB) │ -│ Blink-grade renderer + V8 + libsoup network stack │ +│ WebCore renderer + JavaScriptCore + libsoup net │ └─────────────────────────────────────────────────────┘ ``` @@ -211,8 +226,9 @@ infrastructure, multiple uses: | Scheme | Purpose | Status | |--------|---------|--------| -| `sovereign://nostr/*` | `window.nostr` bridge — web pages call `fetch('sovereign://nostr/signEvent')` to sign via the C-side `nostr_signer_t` | Planned | -| `sovereign://settings` | Browser-internal pages (like `chrome://settings`) | Planned | +| `sovereign://nostr/*` | `window.nostr` bridge — web pages call `fetch('sovereign://nostr/signEvent')` to sign via the C-side `nostr_signer_t` | ✅ Working | +| `sovereign://settings`, `sovereign://bookmarks`, `sovereign://profile` | Browser-internal pages (like `chrome://settings`) | ✅ Working | +| `sovereign://agents/chat`, `sovereign://agents/config` | Agent chat and configuration pages | ✅ Working | | `fips://` / `*.fips` | Route to FIPS mesh nodes via TUN interface | Planned | | `nostr://` | Fetch Nostr events from relays, render as HTML | Planned | @@ -277,79 +293,130 @@ is saved to `~/.sovereign_browser/session.txt`. See [`plans/browser-tabs.md`](plans/browser-tabs.md) for the full implementation plan. -### Agent tools +### Local file browsing -The browser embeds a WebSocket server (using libsoup) that allows external -AI agents to control the browser programmatically. Connect to -`ws://localhost:17777/agent` and send JSON tool commands. +sovereign_browser is designed to run websites directly from local files — +open an `index.html` from its directory and navigate as if it were served +over HTTP. No web server required. This is a deliberate design choice: +security is moved up to the Qube level, so the browser doesn't need the +traditional security restrictions (same-origin policy, CORS) that would +block local-file web apps. -The server starts before the login dialog and remains available throughout -the browser's lifecycle. The browser starts normally with the GTK login -dialog — no blocking wait. An agent can log in at any time: while the dialog -is showing, or after the browser is already running. If an agent logs in -while the dialog is open, the agent's login takes priority. +**What works on `file://` pages:** -**Login tools** (available before login): +- **Relative links** between pages — `index.html` → `about.html`, + `subdir/page.html`, `../index.html`, `../../../deep/page.html` +- **Cross-origin access** — all `file://` URLs are treated as same-origin. + Iframes can access their parent's DOM, `window.open()` popups are + accessible, and `fetch()`/XHR work across directories. +- **`fetch()` and `XMLHttpRequest`** — load local JSON, text, and other + files via relative paths (returns `status=0`, the `file://` convention) +- **External resources** — CSS stylesheets, JavaScript files, SVG images, + favicons, ``/`srcset`, ``/`` +- **Video and audio playback** — MP4 video and M4A audio play correctly + with full duration/dimension metadata +- **All browser storage** — `localStorage`, `sessionStorage`, `cookies`, + and `IndexedDB` all work fully (open, upgrade, put, get, count) +- **`history.pushState`/`replaceState`** — SPA-style routing works on + `file://` URLs +- **Query strings and hash fragments** — preserved in `location.href`, + accessible via `location.search`/`location.hash` +- **URL-encoded filenames** — `encoded%20name.html` (file on disk: + `encoded name.html`) loads correctly +- **Form submissions** — GET forms append query strings to the target + page; POST forms navigate (body silently dropped, no server) +- **`window.open()`** — opens local pages in new tabs -- `login_status` — check if logged in -- `login` — authenticate with method: `local` (nsec), `seed` (BIP-39 - mnemonic), `readonly` (npub), `nip46` (bunker:// URL), or `nsigner` - (hardware signer via serial/unix/tcp/qrexec transport) -- `logout` — clear signer and reset state -- `switch_identity` — change identity (same params as login) +**Known limitations (WebKit engine quirks, not bugs):** -**Browser tools** (available after login): +- `fetch()` for missing files throws `Load failed` instead of returning an + error Response (no HTTP status codes on `file://`) +- `