Skip to content
Merged
67 changes: 47 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@
> [!IMPORTANT]
> **Aether Browser v0.1.0 is a private release-candidate build in progress.** This
> repository is not approved for public release and is not presented as production ready.
> Runtime and API source are present, while complete security integration, dedicated-runner
> Runtime, API, authority, navigation, and egress boundaries are present. Dedicated-runner
> evidence, container acceptance, exact-main CI, and a real demo remain unclaimed until they
> are merged and independently verified.
> are independently verified.

## One browser, two views

Expand All @@ -51,9 +51,10 @@ same browser session.
- Structured text and accessibility state come before pixels; screenshots remain available
when visual context is necessary.

The checked-in runtime already models headed Chrome, structured snapshots, bounded actions,
and single-session ownership. The noVNC host integration and security-lane convergence are
still private-RC work, so this README treats them as contracts—not as completed release proof.
The checked-in runtime models headed Chrome, structured snapshots, bounded actions,
single-session ownership, fail-closed authority, and pinned browser egress. Chromium is forced
to disable non-proxied WebRTC UDP so WebRTC cannot bypass the TCP proxy boundary. noVNC host
integration and full private-RC acceptance still require exact-main proof.

| Surface | Focused v0.1 behavior | Evidence in this checkout |
|---|---|---|
Expand All @@ -62,14 +63,13 @@ still private-RC work, so this README treats them as contracts—not as complete
| Interaction | `click`, `type`, `scroll`, and `press`; selector-first with bounded coordinate fallback | Runtime source + [API contract](docs/API.md) |
| Session lifecycle | One UUID session, explicit states, vision budget, expiry, and idempotent end | Runtime source + [architecture contract](docs/ARCHITECTURE.md) |
| Human view | The same headed display exposed locally through noVNC | Architecture contract; integration proof pending |
| Authority and navigation | Observer/controller roles plus HTTP(S), redirect, and address-policy checks | Documented contract; security integration pending |
| Authority and navigation | Observer/controller roles plus HTTP(S), redirect, address, and pinned-egress checks | Source + security tests |

## Quick start

> [!NOTE]
> This is the integrated RC launch shape. On the exact docs-only commit in this pull request,
> the required security modules have not merged into `main` yet, so the command is not presented
> as runnable proof. Use it after security integration lands and exact-main validation is green.
> This local command exercises the loopback application shape. It does not start Chrome's Linux
> display services or constitute container, noVNC, or exact-main release proof.

The integrated Python package requires **Python 3.11+**, an installed Chrome channel usable by
Patchright, and—on Linux—an active headed display. The package does not itself install or start
Expand All @@ -79,13 +79,13 @@ Xvfb, x11vnc, noVNC, or websockify.
python3.11 -m venv .venv
. .venv/bin/activate
python -m pip install -e .
python -m uvicorn aether_browser.main:app --host 127.0.0.1 --port 8092
python -m aether_browser.main
```

This is the intended application entry point and loopback default after convergence. The
security lane is adding the fail-closed authority and navigation-policy wiring required for the
RC; until that work is merged, keep both API and noVNC on loopback and do not treat this command
as a completed quick-start proof.
Keep both API and noVNC listeners on numeric loopback. Named `localhost`, wildcard binds, and
direct non-loopback binds are rejected so DNS, container, or interface ambiguity cannot silently
broaden the authority boundary. The supported module launcher validates and owns the Uvicorn bind
and disables proxy-header interpretation; do not bypass it with a raw Uvicorn CLI invocation.

| Setting | Current default |
|---|---:|
Expand All @@ -102,8 +102,33 @@ curl http://127.0.0.1:8092/browser/health
```

Strict loopback local mode may run without bearer tokens only while remote mode is disabled and
both listeners remain loopback-bound. Authenticated deployments use distinct observer and
controller tokens; see the [transport and authority contract](docs/API.md#transport-and-authority).
both listeners remain numeric-loopback-bound. Authenticated local mode may use distinct observer
and controller tokens.

### Authenticated remote API

v0.1 never binds the API directly to a remote interface. Remote clients must terminate HTTPS at
a trusted proxy on the same host, while Aether Browser continues to listen on numeric loopback.
The deployment must set all of the following or startup fails closed:

- `AETHER_BROWSER_REMOTE_MODE=1`
- `AETHER_BROWSER_REVERSE_PROXY_EXPOSED=1`
- a numeric-loopback `AETHER_BROWSER_API_BIND` (normally `127.0.0.1`)
- a non-loopback `AETHER_BROWSER_API_HOST`
- `AETHER_BROWSER_TRUSTED_PROXY_CIDR` as one exact loopback `/32` or `/128`
- `AETHER_BROWSER_TRUSTED_PROXY_SCHEME=https`
- distinct strong `AETHER_BROWSER_OBSERVER_TOKEN` and
`AETHER_BROWSER_CONTROLLER_TOKEN` values
- `AETHER_BROWSER_TEST_MODE=0` and no `AETHER_BROWSER_TEST_ORIGINS`

The proxy must strip `Forwarded`, every `X-Forwarded-*` header, `X-Real-IP`, and
`X-Original-Host`; Aether Browser rejects them and validates the raw peer plus Host authority.
Never proxy port 6080 or the noVNC paths. See the
[transport and authority contract](docs/API.md#transport-and-authority).

Container gateway integration for this proxy-only contract remains pending. Container
acceptance must use an isolated namespace/gateway or an exec-based probe; it must not make the
API or noVNC listener non-loopback merely to make a host-side test reachable.

## API surface

Expand Down Expand Up @@ -146,18 +171,20 @@ pretending v0.1 is a multi-worker pool. See the full
The safety model is intentionally based on a smaller surface:

- API and noVNC defaults are loopback-only; noVNC stays loopback-only in v0.1.
- Non-loopback API binding requires explicit remote mode and distinct strong observer/controller
tokens under the contract.
- Direct non-loopback API binding is rejected. Remote API use requires the complete trusted
same-host HTTPS proxy tuple and distinct strong observer/controller tokens.
- Top-level HTTP(S) destinations and redirects must be revalidated against credential, scheme,
address-class, rebinding, and browser-initiated navigation rules.
- Chromium disables non-proxied WebRTC UDP; routed HTTP(S) and WebSocket traffic remains subject
to the pinned TCP egress boundary.
- URLs, selectors, input, text, accessibility trees, screenshots, coordinates, timeouts, and
vision steps are bounded.
- The public API contains no arbitrary JavaScript, DevTools, upload, clipboard, download,
extension, shell, filesystem, credential, or cookie-import operation.
- Ending, expiry, failure, and shutdown converge on owned-resource cleanup.

These are contract requirements, not a claim that the still-unmerged security and acceptance
gates have passed. The release candidate must prove them before its status changes.
These boundaries are implemented, but the release candidate must still prove its remaining
container, dedicated-runner, exact-main, and acceptance gates before its status changes.

### Source recovery and exclusions

Expand Down
28 changes: 27 additions & 1 deletion docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,33 @@ The v0.1 runtime exposes one closed JSON API. Route names are intentionally stab

## Transport and authority

Use `Authorization: Bearer <token>`. In authenticated mode, the observer token permits health and snapshot access; the controller token permits create, navigate, interact, and end. Tokens never appear in URLs, responses, logs, screenshots, or examples. Strict loopback local mode may run without tokens only when remote mode is disabled and both API and noVNC listeners are loopback-bound.
Use `Authorization: Bearer <token>`. In authenticated mode, the observer token permits
health and snapshot access; the controller token permits create, navigate, interact, and end.
Tokens never appear in URLs, responses, logs, screenshots, or examples. Strict loopback local
mode may run without tokens only when remote mode is disabled and both API and noVNC listeners
are numeric-loopback-bound.

Direct non-loopback API listening is not supported in v0.1, even with bearer tokens. Remote API
clients are supported only through an explicitly configured same-host TLS reverse proxy. The
backend remains HTTP on a numeric loopback socket and requires the complete tuple below:

- `AETHER_BROWSER_REMOTE_MODE=1`;
- `AETHER_BROWSER_REVERSE_PROXY_EXPOSED=1`;
- a numeric-loopback `AETHER_BROWSER_API_BIND` (normally `127.0.0.1`);
- a non-loopback `AETHER_BROWSER_API_HOST` matching the external Host authority;
- an exact loopback `AETHER_BROWSER_TRUSTED_PROXY_CIDR` (`/32` or `/128`);
- `AETHER_BROWSER_TRUSTED_PROXY_SCHEME=https`; and
- distinct strong observer and controller tokens;
- `AETHER_BROWSER_TEST_MODE=0`; and
- no `AETHER_BROWSER_TEST_ORIGINS`.

Partial proxy configuration fails startup. Uvicorn proxy-header interpretation is disabled.
`Forwarded`, every `X-Forwarded-*` header, `X-Real-IP`, and `X-Original-Host` are rejected rather
than trusted. The raw TCP peer must match the configured exact loopback CIDR, and the request
must carry exactly one Host matching the effective API host. The TLS proxy must strip those
forwarding headers and must never route the noVNC surface. Start the API through the supported
`python -m aether_browser.main` launcher; a raw Uvicorn CLI can override validated listener
settings and is outside the transport contract.

| Route | Observer | Controller |
|---|---:|---:|
Expand Down
52 changes: 36 additions & 16 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@
Aether Browser is one headed Chrome session with two interfaces to the same runtime: a small HTTP API for agent control and loopback-only noVNC for human observation or takeover.

```text
API client ──HTTP──> FastAPI ──> authority + navigation policy
│
v
single-session manager
│
v
Patchright + Chrome
│
Xvfb display
│
x11vnc + websockify
│
Human browser <──loopback noVNC───┘
remote API client ──HTTPS──> trusted same-host TLS proxy
│
│ loopback HTTP; no forwarded headers
v
local API client ───────────────> FastAPI ──> authority + navigation policy
│
v
single-session manager
│
v
Patchright + Chrome
│
Xvfb display
│
x11vnc + websockify
│
Human browser <────────────loopback noVNC────────┘
```

## State and ownership
Expand All @@ -28,13 +32,29 @@ The session ID remains explicit in every session-scoped payload so a future mult

- API default: `127.0.0.1:8092`.
- noVNC default: `127.0.0.1:6080`.
- Non-loopback API binding requires explicit remote mode and distinct strong observer/controller tokens.
- noVNC remains loopback-only in v0.1.
- Actual API and noVNC listeners accept numeric loopback literals only.
- The supported module launcher owns the validated Uvicorn bind; raw Uvicorn CLI overrides are
outside the transport contract.
- Direct non-loopback API binding is rejected even when bearer tokens are configured.
- Authenticated remote API use requires explicit remote and reverse-proxy modes, a non-loopback
effective host, distinct strong observer/controller tokens, an exact loopback trusted-proxy
CIDR, an `https` proxy-scheme declaration, disabled test mode, and no test origins.
- The raw proxy peer and Host authority are validated. Uvicorn proxy-header parsing is disabled,
and forwarding headers are rejected instead of becoming authority inputs.
- noVNC remains literal loopback-only and is never included in the remote proxy surface in v0.1.
- Container gateway integration is pending. Isolated-namespace/gateway or exec-based acceptance
must preserve the loopback listener contract rather than widening either bind for reachability.
- Test-only local origins require explicit test mode and an exact allowlist; production defaults never enable that exception.

## Browser boundary

The browser accepts top-level HTTP(S) navigation only after address and DNS policy checks. Every redirect and browser-initiated top-level navigation is revalidated. Downloads are disabled; popups are denied or closed; new tabs remain bounded to session ownership. The public API exposes no script evaluation, DevTools, upload, clipboard, extension, shell, filesystem, credential, or cookie import operation.
The browser accepts top-level HTTP(S) navigation only after address and DNS policy checks. Every
redirect and browser-initiated network-producing top-level navigation is revalidated;
same-document and history-only changes do not produce a routed request and are not claimed as
revalidated. Chromium disables non-proxied WebRTC UDP so WebRTC cannot bypass the pinned TCP
proxy boundary. Downloads are disabled; popups are denied or closed; new tabs remain bounded to
session ownership. The public API exposes no script evaluation, DevTools, upload, clipboard,
extension, shell, filesystem, credential, or cookie import operation.

## Non-goals

Expand Down
Loading