Axol molts every so often. Here's what changed, and when.
📜v3.0April 27, 2026
she reads the rulebook
Axol now reads the same permissions.allow / deny / ask rules Claude Code already evaluates, so calls she'd normally bubble for get auto-resolved when your settings.json has an answer. Only the genuinely undecided ones surface — no more clicking through allows for tools you'd already approved.
rules-driven permission prompts
Reads your existing CC rules. User settings (~/.claude/settings.json) and every project-level .claude/settings.json + settings.local.json on the way up from cwd all merge in. Same shape Claude Code already evaluates — Axol just evaluates them too, so the answer arrives without a bubble.
Deny beats ask beats allow. First match in each bucket wins, deny short-circuits everything. Matches CC's documented precedence so the two evaluators stay in agreement.
Bash compound commands split correctly.git status && git diff evaluates each subcommand independently — any deny match denies the whole call, and every subcommand has to be allow-matched for a silent pass.
Session-mode aware. Honors all six values CC emits in permission_mode: auto / bypassPermissions auto-allow undecided cases, acceptEdits waves through Edit / Write / NotebookEdit only, plan and dontAsk deny any unresolved call (the latter is the inverse of auto — block anything you didn't pre-approve), default bubbles. Explicit allow / deny rules always pass through — mode never downgrades them.
upgrading from v2.x
Rename the hook event. Open ~/.claude/settings.json and change "PermissionRequest" to "PreToolUse". CC's PermissionRequest hook had a ~1.5s race that swallowed human-paced clicks; PreToolUse doesn't, but it fires for every tool call — which is what the rules engine above is for.
Add a matcher. Restrict the hook to tools that actually need a decision; without one you'll get a flicker for every Read and Glob too. "matcher": "Bash|Edit|Write|WebFetch|NotebookEdit|mcp__.*" covers the common set.
Your existing rules just work. Anything already in permissions.allow / deny / ask is honored automatically — no migration on the rules side.
"If you've already told Claude it's fine, I won't ask twice."
🪶v2.2April 22, 2026
she stretches her gills
Axol can now reach out to services she can't webhook. Gills are small local scripts that poll an API on a launchd schedule, read their token from Keychain, and drop each new item straight into her bubble — no neuromast hop, no cloud credential. First one in the box: github-notifications, so @mentions and review requests from every repo you're subscribed to show up as bubbles automatically.
pull-style alerts via gills
A new companion pattern to lateral-line. Where lateral-line drains the neuromast queue for push-style webhooks, gills go out and fetch. Same loopback target, same 30-second cadence, fully local — the credential never leaves the Mac.
github-notifications gill bundled. Polls GET /notifications every 30s. review_requested / mention / team_mention / assign / security_alert render urgent (pinned until clicked); everything else rides the normal lane. API URLs are rewritten to human-facing github.com links so the View button opens the PR in the browser.
Secrets in Keychain, not env vars.security add-generic-password -s axol-github-pat -w <token> is the only install-time secret step. Rotation is a one-liner; no redeploy.
Six-helper mini-framework.lib.sh exposes gill_init, gill_axol_up, gill_keychain_secret, gill_cursor_read/write, gill_post_envelope, and gill_log — enough to write a new gill in ~20 lines of bash. ./install.sh <name> scaffolds the launchd plist automatically; no registry file.
Graceful failure everywhere. Axol closed? Skip the API call before spending rate-limit budget. Token missing? Log once and exit 0. Mid-batch POST failure? Hold the cursor, retry next sample. Stale launchd-double-fire? File lock silently drops the second run.
also in this release
github-actions adapter now renders with the GitHub mark icon for both success and failure cases — the pass/fail distinction reads from the body copy and bubble priority instead of a separate glyph.
Scheme inference for signed webhooks. Setting HOOK_SECRET_GITHUB is now enough on its own — HOOK_SCHEME_GITHUB defaults to the source name when it matches a known scheme (github / stripe / generic). Closes a papercut where forgetting the scheme var silently fell through to the shared-key tier.
"Not every alert comes knocking. Sometimes I go looking."
🫧v2.1.1April 20, 2026
bubble polish
Three small fixes to the v2.1 permission flow — click-through to focus, history stays out of the way, and Axol dismisses her own bubble when you answer in Claude Code itself.
permission-bubble fixes
Click the title or body to focus the terminal. Same affordance as any other actionable bubble — the prompt stays open until you Allow or Deny.
Recent Alerts stays out of the way while a permission prompt is up. Double-clicking Axol no longer buries Allow / Deny behind the history panel.
Auto-dismiss when CC resolves in-terminal. Claude Code still shows its own prompt alongside the hook; answer there and Axol notices curl's socket close, so the bubble goes away on its own.
"If you've already decided, I won't keep nagging."
🪸v2.1April 18, 2026
she holds the door
Axol can now be asked for a decision — from Claude Code locally, or from any cloud caller via neuromast. The loop is the same shape in both cases: bubble comes up, user clicks, decision flows back to whoever was waiting.
interactive permission bubbles
Allow / Deny buttons — permission requests pop a modal bubble with a green Allow and outlined Deny. No auto-dismiss; she waits for you.
Attention choreography — big anticipation hop on arrival, then alternating wave / wiggle every five seconds until answered, so you notice even if you looked away.
Claude Code integration — a PreToolUse hook bridge routes CC's tool-permission prompts through Axol. Rules from your user and project settings.json auto-allow or auto-deny silently; only unresolved calls surface a bubble.
Answered history — every decision is logged to the Recent Alerts panel with a compact ✓ or ✕ chip alongside the source label.
Cloud approval endpoint — POST /app/api/permission/{source} parks a pending request and 202-accepts in under a second, so callers with tight webhook SLAs (Slack 3 s, Stripe 10 s) never block on a human.
Optional inline hold — add "hold": 20 and the endpoint waits up to 25 s for the user's click before falling back to async; callers who can wait skip a polling round-trip.
Decision polling — GET /app/api/decision/{id} returns allow / deny / pending / expired. Opaque id is its own auth — no secret needed on the hot read path.
Per-service defaults — SERVICES_CONFIG env sets hold window + expiry per source; per-request fields still win.
Lateral-line threading — the forwarder now detects kind: "permission" envelopes, posts them to Axol's local /permission (held open), and relays the decision back to neuromast. Drops cleanly through all five deploy targets; no Cloudflare-only primitives.
movement, mood, and little things
Option-click to improvise — hold ⌥ and click Axol to play any idle on demand.
Worry bubbles that breathe — density scales with the unseen backlog; tempo doubles when anything urgent is unanswered.
Proper drag — absolute cursor-to-window tracking, so she stays under your cursor instead of drifting off.
Load at startup in the right-click menu — writes a LaunchAgent, no manual launchctl dance.
docs
New approval-flow section on remote alerts with a worked GitHub Actions deploy-gate example.
"You decide. I'll hold the bubble open."
✨v2.0April 18, 2026
she grew up
Axol made friends with the wider internet, learned to shrink when she's not needed, and found homes on five different hosting platforms. The corner of the screen has never been this well-connected.
remote alerts
webhookneuromastlateral-lineaxol
neuromast — a tiny serverless webhook intake that parks inbound payloads in a queue behind tiered auth (per-source HMAC for GitHub / Stripe / generic, with a shared-key fallback).
lateral-line — a 60-line bash forwarder on launchd that drains the queue into Axol's loopback port. At-least-once delivery; items stay queued while she's offline.
Runtime-agnostic storage — neuromast runs on Cloudflare KV or Redis. A factory sniffs the env and picks the right backend.
One-command installer at lateral-line/install.sh writes the launchd plist, wires env vars, and starts polling.
Axol didn't change — remote alerts feed her existing adapter system, so a new webhook source is still just a JSON file.
three size modes
mainCI passed
full
~300×360
mainCI passed2
mini
62×56 · 290×80
3
micro
48×48
Mini is new — a smaller character with a side bubble and a count badge on her gill.
Micro (formerly compact) — a static character with a count badge. Out of the way when you need the screen real estate.
⌘-click cycles full → mini → micro → full. The current mode persists across launches.
five deploy guides
Neuromast runs on whichever platform you already pay for (or don't). Each guide is a one-command bootstrap.
Remote alerts — an overview of the lateral-line architecture, with links out to each deploy guide.
Plugin reference — a walkthrough of adapters, a catalog of the five built-ins (Claude Code, GitHub Actions, Sentry, Stripe, and the generic fallback), and a PR flow for contributing your own.
This changelog. Hi.
repo reorg
The Mac app lives in axol/ now — symmetric with neuromast/ and lateral-line/. Three deployable components side-by-side.
Swift sources split out of the mega-file into Adapters.swift, AlertStore.swift, Envelope.swift, Server.swift, plus a tests/ suite.
"I can hear GitHub now. And Stripe. The gills are getting busy."
🥚v1.0April 17, 2026 · first sighting
she hatched!
A tiny pink axolotl wriggled free of her egg and wandered up onto the desktop. She has opinions about notifications.
meet Axol
A cheerful pink axolotl lives in the corner of your screen. Drag her anywhere; she stays put across launches.
Naps: she nods off every few minutes and wakes on a click, drag, or inbound alert. A z z z rises from her head while she dreams.
Right-click menu for animation toggles, about, quit. Left-click for a cheerful quip. She hydrates.
loopback alerts
POST JSON to 127.0.0.1:47329 and she pops a speech bubble. Remote connections are rejected at the socket.
Envelope format:title, body, priority, icon, actions, context. Only title is required.
Priorities:low / normal / high (worry bubbles rise) / urgent (bubble pins open until handled).
Click-through actions:focus-pid, open-url, reveal-file, noop. Closed set — the validator quietly drops anything else.
17 named icons (SF Symbols plus the Claude and GitHub marks), or pass any emoji as a literal prefix.
History panel: double-click for the recent list; click a row to re-run its action.
adapter framework
Drop a JSON file into ~/Library/Application Support/Axol/adapters/ to translate any webhook shape into an envelope.
Tiny template language:{{field}}, dot-paths, {{x | default 'y'}}, plus basename and trim filters.
Predicate matching with match and skip_if; switch + cases for per-branch templates.
Bundled Claude Code adapter out of the box — SessionStart, Notification (pins open for permission prompts), and Stop. Include X-Claude-PID to focus the right terminal.
mood
A single quiet scalar reacts to alert volume and decays slowly.
When she's wound up, she refuses to nap, picks agitated idles ("peek", "wiggle"), and swaps to a frazzled quip pool ("That's a lot.", "Gills are flaring.").
Calm mood → gentle idles, hydration quips. You never see the number. It just shows up.
landing page
A live hero demo on the homepage runs Axol through a hello → alerts → history script so you can see her in motion before downloading.
Compact mode (⌘-click) introduced as a minimal footprint option. (Grew into the three-mode system in v2.)
"Hi! I live here now. If you need me, I'll be in the corner. Please hydrate."
more molts soon. If you want to see what's cooking, the commit log is the freshest view.