JARVIS scrubs the API key from every child process, and skips prompts by default
JARVIS — a voice assistant for Claude Code. Talk to your Mac and he brainstorms a project with you, builds it, and tells you out loud when a Claude Code session needs you. macOS, on your Claude subscription, no API key.
At a glance
- What is it?
- A macOS voice front end for Claude Code that runs on your existing subscription, guarantees the model never sees an API key by stripping it from the environment, and by default launches build subprocesses with permission prompts switched off.
- Who is it for?
- The environment scrubbing is the part of this design worth copying: it removes the credential rather than trusting the code not to pass it. The part to think about before enabling it is the default.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 25 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The API key is scrubbed, not merely unused
The claim that JARVIS cannot spend money on an API is enforced in code rather than promised. Three lines do the work:
# claude_env.py
SCRUBBED_ENV_PREFIXES = ("CLAUDE_CODE_", "ANTHROPIC_")
SCRUBBED_ENV_KEYS = {"CLAUDECODE"}Every Claude Code process the project spawns, both the brain and every build, is launched through claude_env.child_env(), which removes anything matching those two prefixes first and one further key by name. The reason given is specific rather than theoretical: the CLI silently prefers an inherited ANTHROPIC_API_KEY over your login, and claude auth status goes on reporting loggedIn true while billing quietly moves onto the key. So the key is taken away instead of being left for the code to avoid. You may keep one in your .env, a startup check will warn you that it is there, and the brain still never sees it. Note that the prefixes also cover the CLI's own variables, not just the provider's.
Permission prompts default to off, and another session's prompt can be answered
One optional setting carries more weight than its position in the env file suggests. JARVIS_SKIP_PERMISSIONS defaults to true, with a stated reason: JARVIS is voice-driven and cannot respond to interactive permission prompts, and they would silently hang the subprocess rather than fail it. The file advises setting it to false only when you are running JARVIS in a terminal you can see and can answer. The default therefore means a build session started by voice runs its tool calls without asking. The same capability reaches sideways, too: the session watcher covers every Claude Code session on the machine rather than only JARVIS's own, can post a message into one, and can answer a permission prompt for a session running in Terminal.app by pressing a single key. So the tool can both skip asking and answer on your behalf.
Chrome is a constraint, not a preference
The requirements list rules out two platforms and one browser in plain terms. macOS is required because terminal control, window listing, screenshots and notifications all go through AppleScript, and the page says there is no Linux or Windows path today. Chrome is required for a reason in the code: the microphone uses the Web Speech API, SpeechRecognition and its webkit prefixed form, both handled in frontend/src/voice.ts, which Firefox has never implemented, and there is no server-side transcription to fall back on. The rest is ordinary: Python 3.11 or newer, Node.js 18 or newer, Claude Code installed and logged in at version 2.1.224 or newer via npm install -g @anthropic-ai/claude-code, Chromium for Playwright, and a Fish Audio key with no fallback voice. The cost claim is scoped to that setup: the number that matters, the page says, is how much of your subscription's two rolling windows is gone.
The certificate step is the one that fails quietly
One setup step is marked as not optional, and the failure it prevents is the least obvious in the project. The certificate is a single self-signed pair:
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj '/CN=localhost'server.py serves HTTPS whenever cert.pem and key.pem sit beside it, and frontend/vite.config.ts proxies both /api and /ws to https://localhost:8340. Without the pair, the backend drops to plain HTTP, the dev server's proxy has nothing to reach, and every API and WebSocket call through the front end returns a 500 while the page itself still loads. The page calls that a confusing way to spend an evening, and then advises generating the pair once and forgetting about it. Two details follow from the commands: the certificate is valid for 365 days and so needs regenerating annually, and the proxy target port is fixed at 8340 in the front end, so running the backend on a different port breaks the same calls in a different way. The backend is then started bound to 127.0.0.1, which is what keeps the whole thing local.
Fish Audio is the only bill, and losing the key costs you the voice
The voice is a single paid dependency with a hard failure mode. tts.py returns nothing without FISH_API_KEY, so a missing key does not degrade to a different voice or to a beep: JARVIS goes silent and his replies appear as text in the browser instead. The page points out that this is the one thing you pay for, and that swapping in a different text to speech system is a small, well isolated file to replace. Three optional keys tune the rest: JARVIS_BRAIN_MODEL, which defaults to sonnet and is always passed explicitly rather than left to a CLI default, FISH_VOICE_ID, which defaults to a voice built for the project and whose example value is a 32 character identifier, and USER_NAME, which is what he calls you. The visual side is honest about itself too: the orb on the front page is frontend/src/orb.ts running live, and the page says the audio driving its pulse is synthetic, a speech shaped envelope fitted to a measurement of the real analyser rather than a recording of the voice, regenerable through scripts/make_orb_loop.py.
Weather starts by telling a third party your public IP
Four optional keys pin the weather location and unit, and reading them explains the default. WEATHER_LOCATION_LABEL, WEATHER_LATITUDE, WEATHER_LONGITUDE and WEATHER_UNIT override an auto-detected location, with the example values naming London at 51.5072 and -0.1276 and celsius as the alternative to the default fahrenheit. Left alone, the location comes from your public IP address via the service ipwho.is, which means the lookup discloses the machine's egress address to a third party as part of a voice request. The example block also carries a note that no Anthropic variable set in the env file is needed for anything, and that any such variable is deliberately hidden from the brain so it can only use the login. That is the same scrubbing rule as the Python constants, restated where a user editing the file will actually read it.
Progress lives in checkboxes inside the project being built
A build is a real claude -p session handed a brief with three phases: write a phased plan, review that plan against the spec, then execute it task by task under test driven development, ticking the plan's checkboxes as it goes. The page is explicit about why that shape matters, saying how far it has got is answered by reading the checkboxes rather than guessing. The spec itself is a file, docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md, written inside the project being built and created before a single process is spawned, which you can read back by numbered section and approve by voice or open on the dashboard. The conversational phase is the same discipline in speech: one question at a time, two or three approaches offered, and nothing started until you agree on one. Everything the project starts is recorded as a run, a row in SQLite with its prompt, project, status, token usage and full event stream, viewable live at the dashboard, whose six tabs end with Usage showing what your five hour and seven day windows have left and who spent them.
Forty five top level entries, most of them flat modules
The layout is a wide flat surface rather than a package tree. Forty five entries sit at the root, and around thirty of them are single Python modules with no directory around them: actions.py, brain.py, browser.py, builds.py, claude_env.py, data_paths.py, dialog.py, gh_lookup.py, jarvis_mcp.py, jarvis_memory.py, notifier.py, preflight.py, project_maker.py, projects_view.py, repo_read.py, run_executor.py, run_store.py, screen.py, session_steer.py, session_watch.py, specs.py, speech.py, stream_parser.py, tts.py, usage_scan.py, usage_store.py, web_auth.py and work_mode.py. Alongside them sit data/ and migrations/ for the SQLite store, frontend/, jarvis_home/, skills/, scripts/, tests/ and pytest.ini. The install requirements are eight lines, every one of them an open ended lower bound with no ceiling, and two of those eight are pytest and pytest-asyncio, so the test tooling is installed with the runtime. There is also no dotenv library in that list even though the env file is central to setup, which means the file is parsed by the project's own code.
Editorial conclusion
The environment scrubbing is the part of this design worth copying: it removes the credential rather than trusting the code not to pass it. The part to think about before enabling it is the default. With permission prompts skipped, a voice-driven build can edit your project without asking, and the same tool can approve a prompt in another session, so anyone using JARVIS on real work should decide deliberately which projects it touches and what it is allowed to answer. Chrome, macOS and a Fish Audio key are non-negotiable as written.
Frequently asked questions
Does JARVIS need an Anthropic API key?
No, and the repository works to keep one out of reach. Every Claude Code process goes through claude_env.child_env(), which strips variables beginning ANTHROPIC_ or CLAUDE_CODE_ and the named key CLAUDECODE first, because the CLI otherwise prefers an inherited key over your login while auth status still reports a logged in session.
Can JARVIS run on Windows or Linux?
No. Terminal control, window listing, screenshots and notifications all go through AppleScript, and the page states there is no Linux or Windows path today. Chrome is also required, because the microphone uses the Web Speech API that Firefox has never implemented and there is no server side transcription to fall back on.
What has to be installed to run JARVIS?
Python 3.11 or newer, Node.js 18 or newer, Claude Code 2.1.224 or newer installed and logged in, Chromium for Playwright, and a Fish Audio API key with no fallback voice. A self-signed certificate pair is also required, because the front end proxies to the backend over https://localhost:8340.
Are permission prompts enabled in JARVIS?
Not by default. JARVIS_SKIP_PERMISSIONS defaults to true because a voice driven assistant cannot answer interactive prompts and they would silently hang the subprocess. The file says to set it to false only when running JARVIS in a terminal you can see and respond to.
Where does JARVIS keep its memory and its run history?
Long term memory is a folder of plain Markdown files, one fact per file, with an index the brain always sees, and it can be edited in any text editor. Every Claude Code process it starts becomes a run row in SQLite with its prompt, project, status, token usage and the full event stream, on a dashboard with six tabs ending in Usage.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ethanplusai-jarvis)