Aegis
A macOS background focus tool. Start a focus session with a topic and duration; the Chrome extension reports pages you visit to a local daemon, which uses an LLM (any OpenAI-compatible endpoint) to judge whether the page supports your study topic. Off-task pages are redirected to a local "keep focus" page.
- No browsing latency. Pages load normally; the check happens in the background and a caught distraction is redirected within ~1s of being judged.
- Locked sessions. While a session is active it cannot be stopped, reset,
or quit through the app — the daemon runs as a
launchdKeepAlive agent and session state persists atomically across restarts (crash/restart/corruption cannot silently void the lock). Be honest with yourself: this is a self-imposed commitment aid, not tamper-proof confinement — anyone who owns the machine can still unload the agent, disable the extension, use another browser, or edit the session file. It guarantees the lock is not lost accidentally; it does not prevent leaving intentionally.
Architecture
Chrome (extension: background.js)
│ tabs.onUpdated / onActivated
▼ POST /evaluate (async, non-blocking)
macOS daemon (aegis/) ──► SQLite cache ──► LLM (Groq/NIM/Zen/…)
│ whitelist → cache → LLM verdict
▼ {allowed: false, confidence >= 0.8}
redirect tab ──► http://127.0.0.1:8123/blocked ("Aegis" page)
Install
Aegis is a Python package installed with pipx; the daemon runs as a launchd
LaunchAgent managed by the aegis CLI.
brew install pipx
pipx install aegis-focus # from PyPI
aegis install # render the LaunchAgent plist and start the daemon
The package is published on PyPI as aegis-focus (the plain aegis name
was already taken by another project); the installed commands are still
aegis and aegis-daemon.
The plist points at the pipx venv's python, so the daemon always runs exactly the code the CLI manages. After a new release, upgrade and reload:
pipx upgrade aegis-focus
aegis restart # refuses while a focus session is active (--force overrides)
To install from this repository instead (development builds):
pipx install . # from the repo (or pipx install /path/to/aegis)
The Chrome extension is not part of the Python package: load it from the
extension/ folder of the repository
(that folder is also bundled in the PyPI source download).
CLI reference
aegis install install/repair the daemon agent (re-renders the plist, reloads)
aegis restart reload the agent; refuses during an active session unless --force
aegis status agent loaded? daemon healthy? session state?
aegis cleanup remove logs and the verdict cache (keeps config and the session lock)
aegis uninstall stop the daemon, remove the agent + logs + ALL state (confirms unless -y)
There is deliberately no aegis stop: the daemon is a KeepAlive agent —
you cannot stop it without removing it. aegis uninstall is the removal path.
A deliberate user can always escape (see "Locked sessions" above); the
commands are honest about it.
Configure the LLM
Create ~/Library/Application Support/Aegis/config.json:
{
"LLM_BASE_URL": "https://api.groq.com/openai/v1",
"LLM_API_KEY": "gsk_...",
"LLM_MODEL": "llama-3.3-70b-versatile",
"LLM_TIMEOUT": "60",
"BLOCK_CONFIDENCE": "0.8"
}
or export the same names as environment variables in the terminal you run the
daemon from (a launchd agent does not inherit your shell env; the rendered
plist only sets the PATH captured at aegis install time). After a
pipx ensurepath or a shell change, run aegis restart so the plist is
re-rendered with the new PATH (an unchanged plist is hot-restarted with
kickstart; a changed one is booted out and re-bootstrapped so launchd reads
the new program path). config.json is the reliable route for secrets under
launchd. If
no key is set the daemon still runs and blocks nothing (every verdict falls
back to allow). Invalid values (e.g. a bad AEGIS_PORT) fall back to defaults
instead of crashing.
Note: the Chrome extension hardcodes
127.0.0.1:8123(seeextension/background.js), soAEGIS_PORT/AEGIS_HOSTshould be left at their defaults for the extension to work.
Extra always-allowed domains can be added without touching code:
{ "EXTRA_WHITELIST": "khanacademy.org, brilliant.org" }
Whitelisted entries match subdomains too (e.g. en.wikipedia.org) and are
IDNA-normalized, so Unicode domains work. Public-suffix entries (e.g. co.uk,
com.au — domains that are really TLDs) are rejected with a warning: you
cannot whitelist an entire country's domains, and the rejection keeps a typo
from silently widening the allowlist.
Then load the extension:
- Open Chrome →
chrome://extensions - Enable Developer mode → Load unpacked
- Select the
extension/folder
Usage
- Click the A menu bar icon → Start Focus Session… → enter topic and duration (1–600 min). The session cannot be stopped early through the app (a deliberate user can always remove it — see "Locked sessions" above).
- When you finish (or the daemon restarts mid-session), the lock is restored
automatically from
~/Library/Application Support/Aegis/session.json(a backup copy.bakis kept and used if the lock file is ever corrupt).
Verdict pipeline
- No active session → allow.
- Whitelisted domains (
github.com,wikipedia.org, …) and browser-internal pages (chrome://,about:,edge://, Chrome Web Store, the Aegis daemon itself, …) → allow, no LLM call. - SQLite verdict cache (per URL per session) → instant repeat answers. LLM failures (timeout, transport error, unparseable reply) are never cached — an outage cannot seal a page open (or lock one shut).
- LLM call with page title, URL, meta description, and top/middle/bottom text
samples (or YouTube title/channel/description for videos) → JSON verdict
{"allowed": bool, "confidence": 0-1, "reason": str}. Verdicts are rate-limited per session (up to 30 per 5 s) so a tab-storm cannot burn the LLM budget; a burst of navigations simply waits its turn and gets slower answers, it never makes more calls. - Any LLM failure → allow (never block browsing due to API issues) — meaning a slow or stalled evaluator intentionally lets an off-task page through. The API is loopback-only and rejects every non-extension Origin, so no website can read your session or burn your LLM quota.
The "Aegis" page
Off-task pages are redirected to http://127.0.0.1:8123/blocked, which:
- Shows the block reason, session topic, and a live countdown.
- Auto-returns to the original page 5 seconds after the session ends (no input needed).
- Appeals: a text box + "Appeal" button. The appeal is sent to the LLM,
which decides whether your justification warrants access to that page
during this session. The page's link is attached automatically — nothing
to copy.
- Granted → the page opens immediately and the allow verdict is cached for the session, so it is not instantly re-blocked.
- Denied → the reason is shown; you may appeal again (rate-limited to 1 per 5 s to protect the LLM budget).
- LLM outage → denied (fail-closed — an appeal is not an unlock bypass). Appeals are retried up to 6 times before giving up.
- Grants are per-session: a new session judges the page fresh.
- The page also shows an "Aegis daemon unreachable" hint after several failed /status polls, so a dead daemon is visible instead of a silently frozen countdown.
Uninstall
aegis uninstall
Removes the LaunchAgent, ~/Library/Logs/Aegis/, and the entire
~/Library/Application Support/Aegis/ state directory (session lock,
cache, config). The pipx package itself is left installed
(pipx uninstall aegis removes that too). The project directory is left in
place.
Development
python3 -m venv venv
./venv/bin/pip install -e .
python -m aegis.main # run daemon in foreground (menu bar app)
python3 -m tests.test_evaluator # offline daemon+CLI unit tests (no LLM needed)
node aegis/static/test_blocked.mjs # blocked-page UI harness
node extension/test_background.mjs # extension harness
Logs: ~/Library/Logs/Aegis/ — daemon.log (auto-rotating, capped at
~1.5 MB total) plus unbounded daemon.out.log / daemon.err.log (launchd's
stdout/stderr redirects). The daemon is crash-proof by design: corrupt lock
files fall back to the backup copy, a corrupt SQLite cache degrades to an
in-memory cache, and any LLM/cache error always degrades to allow.
License
GNU General Public License v3.0 or later — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aegis_focus-1.1.0.tar.gz.
File metadata
- Download URL: aegis_focus-1.1.0.tar.gz
- Upload date:
- Size: 90.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
790015f98ea4474793cc832afd0848b33b8b26f63ae70e459a59efee55b3e33d
|
|
| MD5 |
3aeb726c710fc48c6428c2ed0899d2a9
|
|
| BLAKE2b-256 |
0c7b5ffa8423590d5986b5358ca7229fe09d9aa0f8a0f821e3f803b2379e022a
|
File details
Details for the file aegis_focus-1.1.0-py3-none-any.whl.
File metadata
- Download URL: aegis_focus-1.1.0-py3-none-any.whl
- Upload date:
- Size: 60.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bfb31b45d1d5ad194c2a7d473d8ca10c6235fd3ac124f3c796ce16500b57c016
|
|
| MD5 |
c3c893183a4960cee1b9aa0d85380d59
|
|
| BLAKE2b-256 |
ba8f99a8cd18acab8e9f330fcae47fa13b8d022efd955e120d7df1d82b27ca38
|