Aegis is an agentic web browser.
It gives agents one persistent browser runtime, one local control plane, and one correct production path:
- run
aegisfor the local headful browser app - start
aegis serve - control the browser over the local HTTP API
- run in
headlessorheadfulmode against the same session
Aegis is for agent workflows that need a real browser engine, not a fake DOM.
Core capabilities:
- real browser navigation
- live command execution against the page
- semantic matcher-based control targeting for reactive sites
- headless and headful control
- DOM snapshots
- ordered runtime events
- session import and export
- deterministic traces
- replayable browser runs
The browser engine is a CEF-backed native runtime with platform-specific host edges for macOS and Linux.
There is one supported runtime model:
- one persistent
serveprocess - one local HTTP API
- one browser session per runtime
There is no production per-command relaunch path.
Local release rule:
- install one stable local release app at a platform-native path
- macOS:
~/Applications/Aegis.app - Linux:
~/.local/share/aegis/Aegis - use its bundled CLI as the canonical runtime entrypoint
- do not rebuild or reinstall during normal
aegisusage - use
./install.shas the canonical one-shot local install path
Runtime state rules:
- Chromium browser profiles are not a persistence API
- Chromium credential storage and autofill are enabled by default in the production runtime
- persistent agent state lives under
~/.aegisby default, or$AEGIS_HOMEif set - session persistence goes through
GET /session,POST /session,POST /session/save, andPOST /session/load --profile <name>selects~/.aegis/profiles/<name>/session.json~/.aegis/settings/*.jsonis the canonical home for concern-specific local settings~/.aegis/settings/credentials.jsoncontrols Aegis-owned login capture behavior~/.aegis/secrets/profiles/<profile>/secrets.jsonis the canonical home for Aegis-owned saved secrets- saved browser credentials live under each profile's
secrets.credentials.entries - trace persistence goes through
POST /trace/enable - if
--start-urlis omitted, the runtime boots into a local no-network bootstrap page - the canonical control style is semantic
matchtargeting forclickandset_value, not long-lived raw DOM ids - Aegis-owned credential capture is stored under
~/.aegis; browser-managed credential storage and autofill use Chromium's runtime profile behavior
The main binary is aegis.
Human-use shortcut:
aegiswith no arguments opens the local headful Aegis appaegis openexplicitly opens the same installed appaegis ...with arguments uses the installed bundled CLI- macOS bundled CLI path:
~/Applications/Aegis.app/Contents/MacOS/aegis_cli - Linux bundled CLI path:
~/.local/share/aegis/Aegis/bin/aegis_cli
Top-level commands:
openserveusageexamplesconfig getconfig setconfig secrets-getconfig secrets-setconfig credentials-listconfig credentials-setconfig credentials-removeconfig credentials-cleartrace replaynative statusnative doctornative configurenative buildnative installnative paths
Global runtime flags:
--mode headless|headful--start-url <url>--host-lib <path>overrides the resolved native host library;servedefaults to the workspace release runtime and refreshes it when sources are newer--profile <name>
Built-in CLI guidance:
aegis --helpgives the high-level command map plus quick startsaegis usageprints the recommended production workflowaegis examplesprints copy-pasteable commands for common tasks
One-shot local install:
./install.shWhat it does:
- builds the release binary
- installs the platform-native local app directory
- installs the bundled CLI into that app directory
- installs the canonical shell launcher at
~/.local/bin/aegisor~/bin/aegis - bootstraps and verifies the canonical
~/.aegisstate tree
The older helper path now delegates to the same installer:
./scripts/install_local_release.shCanonical verification uses Fozzy:
./scripts/run_fozzy_full.shInspect or set Aegis-owned config:
aegis config get agent
aegis config set agent --json '{"default_profile":"work"}'
aegis config get credentials
aegis config set credentials --json '{"auto_store":false}'Inspect or set Aegis-owned per-profile secrets:
aegis config secrets-get --profile work
aegis config secrets-set --profile work --json '{"github":{"username":"saint","password":"..."},"api_keys":{"openai":"..."}}'Manage Aegis-owned saved browser credentials:
aegis config credentials-list --profile work
aegis config credentials-set --profile work --json '{"origin":"https://github.com","username":"saint","password":"...","username_field":"login","password_field":"password","form_label":"Sign in"}'
aegis config credentials-remove --profile work --origin https://github.com --username saint
aegis config credentials-clear --profile workSecrets rules:
- secrets live only in
~/.aegis/secrets/... - Aegis does not read or write Chrome/Brave cookies, login databases, or Safe Storage entries
- Aegis auto-stores credentials by default when it sees username/password entry followed by a submit-like click in the active profile
- users can disable that behavior in
~/.aegis/settings/credentials.json - users can inspect and clean up cached credentials through the CLI without touching browser-managed storage
aegis --mode headful serve --addr 127.0.0.1:7878For live agent debugging:
- use
--mode headful
For unattended execution:
- use
--mode headless
If --start-url is omitted, Aegis starts on the local bootstrap page and only pays network cost
when the agent explicitly navigates.
Base address defaults to http://127.0.0.1:7878.
Core routes:
GET /GET /manifestGET /versionGET /contextsPOST /contextsGET /contexts/:context_idDELETE /contexts/:context_idGET /contexts/:context_id/healthzGET /contexts/:context_id/readyzGET /contexts/:context_id/runtimePOST /contexts/:context_id/navigatePOST /contexts/:context_id/executeGET /contexts/:context_id/domGET /contexts/:context_id/eventsGET /contexts/:context_id/events/liveGET /contexts/:context_id/sessionPOST /contexts/:context_id/sessionPOST /contexts/:context_id/trace/enableGET /healthzGET /readyzGET /runtimePOST /navigatePOST /executeGET /domGET /eventsGET /events/liveGET /sessionPOST /sessionPOST /trace/enable
aegis serve now fails fast if the browser cannot reach an operational runtime. A running
server does not merely mean the control plane is bound; it means the active page reached a
verified automation-ready state. /healthz and /readyz stay false until the browser,
renderer context, and live runtime API are all usable.
GET / and GET /manifest expose a stable JSON discovery surface with the route list and
supported command types so agents do not need to guess the control plane.
Named contexts let one server own multiple isolated browser runtimes at once. The legacy root
routes still target the default context, while /contexts/:context_id/... gives explicit control
for host/guest, admin/user, or inviter/invitee flows without spinning up a second HTTP server.
Returns:
host_librarybrowserruntime
The runtime object includes:
bootstrappedbootstrap_duration_msdom_nodeslatest_event_sequencecurrent_urlcurrent_titledocument_ready_statemedialast_live_state_refresh_at_ms
The response also includes:
browser.renderer_readybrowser.runtime_readybrowser.browser_availablebrowser.browser_closedbrowser.load_in_progressprofile.profileprofile.path
Readiness contract:
/healthzand/readyzare only healthy when the control plane is up and the runtime is operational- an operational runtime requires
bootstrapped = true,browser.browser_available = true,browser.renderer_ready = true, andbrowser.runtime_ready = true renderer_readymeans the main-frame renderer context existsruntime_readymeans Aegis verified the livewindow.__aegisautomation API and can actually dispatch work
Navigate the live browser session:
curl -X POST http://127.0.0.1:7878/navigate \
-H 'content-type: application/json' \
-d '{"url":"https://example.com"}'Run commands against the current page:
curl -X POST http://127.0.0.1:7878/execute \
-H 'content-type: application/json' \
-d '{
"commands": [
{"type":"eval","code":"document.title"}
]
}'Supported command types:
evalclickhoverset_valuepress_keywait_forscrolldraggeometry
eval.code should be a JavaScript expression such as document.title.
Aegis also normalizes a leading return ...; if you accidentally send one.
drag supports drag-by-delta and drag-to-point flows, plus optional handle hints such as
start, end, left, and right. delta_x / delta_y move relative to the matched target's
drag anchor, while to_x / to_y target absolute viewport coordinates. Native input[type=range]
controls also update their value and emit input / change events during drag. geometry
returns a first-class element geometry snapshot.
Execution model:
navigatereturns ordered navigation/events quickly and invalidates the cached DOM treeGET /domor a node-ID command such asclick/set_valuematerializes a fresh DOM snapshot on demandhoverand matcher-basedpress_keyresolve against the live DOM with strict action-aware rankingwait_forexecutes inside the runtime owner loop, polling live page metadata, selectors, scroll position/change, media readiness, and optional DOM conditions until the condition is satisfied or times outexecutecan omit the snapshot for low-latency commands likeevalandscroll- incremental state flows through
GET /events
Example drag and geometry batch:
curl -X POST http://127.0.0.1:7878/execute \
-H 'content-type: application/json' \
-d '{
"commands": [
{
"type":"drag",
"match":{"role":"slider","name":"Timeline","actionable":true},
"delta_x":240,
"handle":"end",
"steps":8
},
{
"type":"geometry",
"match":{"role":"slider","name":"Timeline","actionable":true}
}
]
}'Return the current DOM snapshot:
curl http://127.0.0.1:7878/domRead ordered runtime events:
curl 'http://127.0.0.1:7878/events?since=0'Event stream semantics:
- events are ordered
sequenceis monotonically increasingGET /eventsdrains pending native/browser events into the runtime stream before it respondssince=<n>returns events withsequence > nGET /events/livestreams Server-Sent Events for live observers without polling your own loop
Runtime event types:
dom_mutationnavigationnetworkfor request/response/finished/failed lifecycle updateswebsocket_open,websocket_handshake,websocket_frame,websocket_closelog
GET /doctor and GET /runtime also expose the live page truth the runtime is tracking:
runtime.current_urlruntime.current_titleruntime.document_ready_state
command_ready now indicates that the runtime is fully operational right now: browser
available, renderer context ready, runtime API verified, and no active degraded/wedged state.
Import or export session state:
- cookies
- local storage
- session storage
- network overrides
Persist the current runtime session to the active profile file:
curl -X POST http://127.0.0.1:7878/session/saveReload the active profile file into the live runtime:
curl -X POST http://127.0.0.1:7878/session/loadEnable deterministic trace capture:
curl -X POST http://127.0.0.1:7878/trace/enable \
-H 'content-type: application/json' \
-d '{"path":"traces/run.json"}'Replay later with:
aegis trace replay traces/run.jsonThe canonical control loop is:
- start
serve - check
GET /runtime POST /navigate- inspect
GET /domorGET /events POST /execute- continue from
GET /events?since=<latest_sequence> - persist state with
GET /sessionif needed - enable traces for important runs
Measure cold-start and first-command latency with the real bundled runtime path:
python3 scripts/measure_startup.py --mode headful
python3 scripts/measure_startup.py --mode headful --samples 5The report includes:
process_spawn_msserve_ready_banner_msruntime_poll_attemptsruntime_ready_msfirst_command_msruntime_beforefirst_executeruntime_after
When --samples is greater than 1, the harness prints median and max timings plus the full per-sample reports.
Aegis uses a local native host library:
- macOS packaged installs use
~/Applications/Aegis.app/Contents/Frameworks/libaegis_host.dylib - Linux packaged installs use
~/.local/share/aegis/Aegis/lib/libaegis_host.so - macOS workspace builds use
native/build/macos/Release/libaegis_host.dylib - Linux workspace builds use
native/build/linux/release/libaegis_host.so
Native helper commands:
aegis native status
aegis native doctor
aegis native configure
aegis native build
aegis native install
aegis native pathsInstall a stable local release app bundle:
./install.shRun the full local production-like verification flow:
./scripts/verify_local_release.shRun the Fozzy verification gate directly:
./scripts/run_fozzy_full.shUse aegis native doctor when you need one canonical preflight report for expected CEF paths,
canonical install paths, workspace build outputs, required tools, and configure/build/install
readiness.
That installs a stable local app, installs the canonical aegis launcher, and gives the runtime
a stable local app path. On macOS it also clears quarantine attributes and supports code signing.
The Fozzy gate is the canonical validation surface:
tests/aegis_core.fozzy.jsonvalidates core Rust/test/clippy behaviortests/aegis_host_backed.fozzy.jsonvalidates the host-backed runtime flowtests/aegis_native_doctor.fozzy.jsonvalidates the shared native preflight contractfozzy.tomlpins.fozzy/as the artifact root
For distribution-grade macOS installs, aegis native install and ./install.sh also honor:
AEGIS_CODESIGN_IDENTITY="Developer ID Application: ..."to sign the bundle with a real identityAEGIS_CODESIGN_OPTIONS="runtime"to opt into hardened runtime options during signingAEGIS_CODESIGN_ENTITLEMENTS=/absolute/path/to/entitlements.plistto attach app entitlements
On macOS, the installer signs nested helpers/frameworks explicitly and runs
codesign --verify --strict after installation. When a real signing identity is provided, it
also runs spctl --assess so the install fails immediately if Gatekeeper would reject the bundle.
The checked-in local entitlements template lives at native/mac/aegis.entitlements. The local
verification script exports that file automatically unless AEGIS_CODESIGN_ENTITLEMENTS is already
set.
Without a paid Apple Developer account, Aegis can still do a lot locally on macOS:
- build release binaries
- use one stable installed app path
- ad hoc sign the local app bundle
- clear quarantine attributes on that local bundle
What local-only setup cannot bypass:
- macOS Automation / Accessibility / similar privacy approvals
- the benefits of Developer ID signing and notarization
Those system approvals still require one user approval path in macOS.
The native build expects the platform CEF SDK at:
- macOS:
third_party/cef/cef_binary_146.0.6+g68649e2+chromium-146.0.7680.154_macosarm64 - Linux:
third_party/cef/cef_binary_146.0.6+g68649e2+chromium-146.0.7680.154_linux64
That binary payload is intentionally not tracked in Git.
The practical agent guide lives at:
docs/agent-control.md