dumbtv
dumbtv가 ADB로 Fire TV에서 광고·추적·업데이트 알림을 제거, 루트·앱 변경 없이, 무료 오픈소스
개인 데이터를 가리고 기기에서 AI 토큰 비용 절감, 무료 Mac·Chrome·Python 앱
Mask personal data and cut AI token costs on your device — before anything reaches Claude, ChatGPT, Gemini or any AI. Free Mac app, Chrome & Python.
보안Python · JavaScriptGitHub @gaurjin온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
Omna — mask secrets and PII before your prompt leaves the machine. One local proxy for Claude Code, aider, Codex CLI and the Anthropic/OpenAI SDKs.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A small local proxy for AI coding tools. Point Claude Code, aider, Codex CLI or any
Anthropic/OpenAI SDK app at http://127.0.0.1:7788 and every request is masked on
the way out, sent to the provider with your own key or login, and un-masked on
the way back. The tool never notices. The model never sees the real values.
you type: "fix the config, [email protected] AWS_ACCESS_KEY_ID=AKIA…"
model sees: "fix the config, SUPPORT_EMAIL=[EMAIL_1] AWS_ACCESS_KEY_ID=[SECRET_GENERIC_SECRET_1]"
you get back: "…set SUPPORT_EMAIL to [email protected]…" ← restored, streaming, inside tool calls too
[SECRET_AWS_KEY_1] that live only in the proxy's memory,
never on disk, and are put back only in what comes back from the model, so when Claude edits the line that
holds a key, the edit still matches your file. --no-restore-secrets redacts them for good instead.[EMAIL_1] is the same address in every request, so
prompt caching keeps working.Same compiled engine as the Omna Mac app, browser extension and the omna Python library.
curl -fsSL https://omna.dev/cli/install.sh | bash
That installs the omna command (via uv), wires Claude Code, and starts the
proxy.
Piping a script from the internet straight into a shell means running it before you have read it. If that bothers you — and for a privacy tool it reasonably might — you never have to do it. Every step the installer takes, you can take yourself:
# 1. Read the script first, if you want to see what the one-liner does.
curl -fsSL https://omna.dev/cli/install.sh -o install.sh && less install.sh
# 2. Or skip it entirely — this is all it really does:
uv tool install --python 3.12 omna-plugin # or: pipx install omna-plugin
omna init # Claude Code: ANTHROPIC_BASE_URL in ~/.claude/settings.json + a SessionStart hook
omna start -d # background proxy on 127.0.0.1:7788
omna status
The installer has no privileged step of its own: it installs uv if you do not have it, runs the
uv tool install above, and calls omna init. omna init is the only part that asks for sudo,
it prints the exact commands before it runs them, and omna uninstall reverses all of it.
brew tap gaurjin/omna https://github.com/gaurjin/omna-plugin
brew install --cask omna
This installs a single self-contained binary — Python, every dependency and the masking engine baked in — signed with our Developer ID and notarized by Apple. Two commands rather than one because the recipe lives in this repo instead of a separate tap; see docs/homebrew.md for why, and docs/signing.md for how to verify the signature yourself.
Run omna uninstall before brew uninstall — Homebrew cannot remove the encryption keys
Omna stores in your Keychain, and omna uninstall does.
omna init also wires aider, Codex CLI, VS Code (Copilot Chat and similar chat
extensions), and Continue automatically if each is already installed/configured — nothing is
added for a tool you don't have. Wire one later, or a tool you didn't have yet: omna enable aider
/ codex / continue; omna disable reverts it. VS Code is Mac-only for now (omna init's Mac
step); see omna.dev/help/plugin-supported-tools for
the full list, including the tools that only offer an in-app setting (Cursor, Cline, Windsurf — one
line, typed once, inside the tool) and the ones not supported yet, with the real reason why.
On a Mac, omna init also does one more thing: it trusts a local certificate ("Omna Local
Certificate Authority") and points the system proxy at Omna via a PAC file that names only AI
hostnames, so apps that don't use ANTHROPIC_BASE_URL/OPENAI_BASE_URL (a desktop chat app, a
browser) are masked too — not just Claude Code. That's one sudo password prompt, printed before
it runs. Pass --no-system to skip it and wire Claude Code only. omna uninstall reverts it.
On a Mac, omna init also starts a menu-bar icon (omna menubar) showing live status — secrets
kept off the wire, personal values tokenised, requests masked, what's covered, and masking
overhead in ms, each on its own line. Its top line is the toggle itself (click, or the native
checkmark, to pause/resume masking), and it's the only thing Omna adds to System Settings → Login
Items: the masking proxy has no login item of its own, the menu-bar process supervises it.
Quitting the icon only stops that one process: relaunch it any time from Spotlight/Launchpad as
Omna Plugin (a small .app at /Applications/Omna Plugin.app, kept distinct from the native
Omna Mac app so the two never collide — though it now shares that app's icon, so look for the name
if you have both installed), or turn on its Launch at Login toggle so it comes back on its own
after every reboot or log-out. Its Keep Local Reports toggle turns the receipt log off entirely
(not just hides it) for anyone who doesn't want even anonymous counts kept on their machine —
omna status/omna report/omna log still run, they just have nothing new to show while it's off.
Cursor and Cline only offer an in-app setting (paste http://127.0.0.1:7788 / /v1 — see the docs
link above for the exact menu path). Any custom SDK app uses the same address, by hand:
export ANTHROPIC_BASE_URL=http://127.0.0.1:7788 # Anthropic SDK
export OPENAI_BASE_URL=http://127.0.0.1:7788/v1 # OpenAI SDK, Cursor BYOK
| Surface | How | Turn it off |
|---|---|---|
| Claude Code, aider, Codex CLI, VS Code, Continue | wired automatically by omna init (each only if already installed) | omna disable claude-code / aider / codex / continue |
| Cursor, Cline, Windsurf | one in-app setting, typed once — full instructions | unset it in the tool |
| Custom SDK apps | ANTHROPIC_BASE_URL/OPENAI_BASE_URL set by hand | unset the env var |
Browsers (claude.ai, chatgpt.com, gemini, … — a named list, not every site; add your own with omna hosts add SITE) | the system proxy (PAC + a local certificate), set up by omna init on a Mac | omna hosts remove SITE for one site, omna init --no-system or omna uninstall for all of them |
| Desktop AI apps that honour the system proxy | same system proxy as browsers | same as above |
| Desktop apps that ignore the system proxy | omna capture app NAME (Stage 3, per app, its own signed network extension) | omna bypass app NAME (stops masking; there's no command yet to release the app from capture itself) |
| Any specific app, at any layer | omna bypass app NAME — tunnelled through untouched, still receipted | omna mask app NAME |
On a chat website, Omna masks what you send (your messages, pastes and files). The site's own
security checks (Cloudflare's robot check, ChatGPT's anti-abuse check) and its own usage and error
reports are forwarded byte-for-byte and receipted as passthrough-security-check /
passthrough-site-report. Editing them got people signed out of claude.ai, so Omna never does.
Attached files are masked inside and stay the same kind of file: text, .env and code files,
Word .docx, Excel .xlsx, LibreOffice .odt / .ods and .rtf (only the text changes;
formatting, styles and images are kept). PDFs, old .doc / .xls, PowerPoint and images cannot be
masked in place by a network proxy, so they go as they are and omna log says
file NOT masked (pdf). (Omna's Chrome extension masks PDFs and screenshots on the AI websites.)
It makes no difference how a file or text gets into the chat box — the + button, drag and drop, or a paste (some sites turn a long paste into a file) — and Perplexity's uploads to its own Amazon storage are covered too. A value Omna finds once is masked everywhere it appears in that message.
omna mask "email [email protected], key AKIAIOSFODNN7EXAMPLE, phone 212-555-0199"
# email [EMAIL_1], key [SECRET_AWS_KEY_1], phone [PHONE_1]
omna mask --realistic "email [email protected], key AKIAIOSFODNN7EXAMPLE, phone 212-555-0199"
# email [email protected], key [SECRET_AWS_KEY_N], phone 415-555-0128
# ...the key stays a label on purpose. See "Two masking styles" below.
omna log # one line per request: time, route, status, ms, what was masked (or "file NOT masked")
omna log --verify
| Command | What it does |
|---|---|
omna start [-d] [--smart] [--no-restore-secrets] | Run the proxy (foreground, or -d in the background). --smart adds the on-device Contextual model for prose names (809 MB download once, slower). |
omna stop / omna ensure | Stop the background proxy / start it if it is not running (the Claude Code hook calls this). |
omna status | Running? Claude Code wired? Receipts today. |
omna menubar | Mac status icon (starts automatically): click the top line to pause/resume, see live counts (secrets kept off the wire, personal values tokenised, requests masked, coverage, masking overhead), toggle Launch at Login, Keep Local Reports, Restore Real Values in Browser or Use Realistic Fake Values in Browser, or uninstall. Both take effect on the next request — no restart. |
omna log [-n 20] [--verify] [--json] | Local receipts. --verify checks the hash chain. |
omna report [--days 7] [--json] [--html FILE] | Weekly summary from the receipts: requests enabled, distinct secrets kept off the wire, PII tokenised, destinations, chain status, masking cost. |
omna report --export FILE | Write a counts-only copy of that report, safe to hand to a company admin — numbers plus your org/department tags. No prompt text, no real values, no usernames, no file paths, no app names. |
omna report --merge FILE... | Add up exported files into one company view plus a per-department view. A machine whose file is collected twice is counted once. |
omna enroll [--org NAME] [--dept NAME] [--forget] | Tag this machine so its exports can be grouped. With no arguments, shows the current tags. Nothing is sent anywhere — see below. |
omna mask [TEXT or -] | Mask a string or stdin. |
omna style [tokens|realistic] | How a masked value is written: a numbered token (the default, every door) or a realistic fake value (your browser only — refused for anything that writes files). Prints what changed and which doors it affects. |
omna allow VALUE | Never mask this exact value again (false positive). |
Measured on a MacBook Air M5 with Claude Code 2.1.274, a 4-request task (read a file, write a file, answer):
| direct | through omna | |
|---|---|---|
| wall time | 8.8 s | 9.8 s |
| fast masking, 68 KB request body | 16 ms |
Fast masking (rules + checksums, the default) is the 16 ms. Smart masking (--smart, which adds the
on-device Contextual model for prose names) is slower and off by default — it is a deliberate choice you
make, not a surprise you discover.
omna status reports the real average for your machine and your traffic
(avg_mask_ms_this_run), so you never have to take this table's word for it.
The proxy relays the stream as it arrives (pings included), forwards anthropic-beta, anthropic-version,
cache_control and the system array untouched, and forwards provider errors unmodified, per Claude Code's
gateway compatibility guide.
File (~/.omna/) | Contents | Permissions |
|---|---|---|
registry.json | token → real value for reversible PII (needed to restore replies). Secrets are never in it; they live in the running proxy's memory only. | 0600 + encrypted |
receipts.jsonl | one hash-chained line per request: route, status, ms, counts per entity. No values. | 0600 |
ruleset.json | your allowlist and custom patterns ({"allowlist": [...], "custom_rules": [{"label": "CUSTOMER_ID", "pattern": "ACME-\\d{6}"}]}). Created with loopback addresses (127.0.0.1, 0.0.0.0, ::1, localhost) allowlisted. | |
proxy.log, omna.pid | background-process housekeeping | |
policy.json | which tools/apps/hosts are covered, and — only if you ran omna enroll — an organisation name, a department name, and a random device id. | 0600 |
crashes.jsonl | the last 50 crashes: error type, message, stack frames (filenames only), versions. Masked by the engine before it is written, so a value that leaked into an error message is a token here too. Never sent. | 0600 |
dashboard.token | the local secret the dashboard asks for. | 0600 |
Nothing, to us. The plugin makes exactly one kind of outbound connection: the request you were already making, to the AI provider you were already using, with your own key.
There is no analytics call, no crash reporter phoning home, no licence check, no update ping.
When something crashes, it is written to ~/.omna/crashes.jsonl on your machine and stays there —
omna crash shows you the exact bytes, and omna crash --send opens a pre-filled GitHub issue
that you submit, or close. That is a deliberate difference from tools that ship an opt-in
crash reporter: an opt-in promise is something you have to trust, and a file on your disk is
something you can read.
registry.json is the only file Omna writes that contains real values, so it is
the only one that needs more than file permissions.
It is sealed with AES-256-GCM. The key is 32 random bytes kept in your macOS Keychain — never on disk next to the file it opens, and never sent anywhere. GCM also authenticates, so a file that someone edited fails to open instead of quietly handing back changed mappings.
omna status always tells you which state you are in, in plain words.0600. Omna says so loudly rather than
refusing to run — and on that kind of machine, turn on full-disk encryption.OMNA_REGISTRY_ENCRYPTION=off opts out entirely and touches the Keychain not
at all.omna forget starts cleanly from scratch.omna forget and omna uninstall remove the key along with the file.A company with 100 laptops does not want 100 separate reports, and Omna does not want their prompts. So the rollup is done with files, not a backend:
omna enroll):
curl -fsSL https://omna.dev/cli/install.sh | sh -s -- --org "Acme Inc" --dept engineeringomna report --export machine.jsonomna report --merge *.json and gets a
company total plus a per-department breakdown.Nothing is uploaded. There is no Omna server in this picture at all — the
files move however that company already moves files. The export is an
allowlist of number fields, so a new field added to the report later cannot
quietly start riding along. It deliberately leaves out your home path (which
carries your username) and which apps you run. The device id is random and
local; it exists so two files can be told apart and a duplicate can be spotted,
and omna enroll --forget deletes it.
Omna can write a masked value in one of two ways. Numbered tokens are the default, everywhere, and most people never need to change it.
| what the AI receives | when it suits | |
|---|---|---|
| tokens (default) | email [EMAIL_N] | anything that writes files, and anywhere you want a mistake to be obvious |
| realistic | email [email protected] | chat and prose, where ordinary-looking text gets you a better answer |
omna style # what is in force, door by door
omna style realistic # switch on realistic values (browser only — see below)
omna style tokens # back to the default
omna mask --realistic "email [email protected], key AKIAIOSFODNN7EXAMPLE"
# email [email protected], key [SECRET_AWS_KEY_N]
A numbered token fails loudly. If a restore ever breaks, a leftover
[EMAIL_N] sitting in your code is visibly wrong and somebody notices
immediately.
A realistic fake value fails silently. A leftover [email protected]
looks like ordinary data. Nobody notices, and it gets committed to a repository
for ever.
So this is a safety trade, not a preference — and Omna decides it for you where the stakes are one-sided:
| Door | What it reaches | Realistic values |
|---|---|---|
| API door | Claude Code, aider, Codex, Continue, VS Code | refused — they all write files |
| System door | your browser and the chat websites | allowed |
| Deep door | a named desktop app, captured individually | refused — that list can hold an editor |
| Omna's Chrome extension | the browser, masked before the request leaves it | allowed, unless the system door is also on — see below |
A refusal is never quiet: omna style realistic prints which doors kept
numbered tokens and why, omna status shows the style per door, and every
receipt records which style that request actually used.
If you also run Omna's Chrome extension, it masks inside the browser before the request ever leaves it, so it has to be told which style to use. It asks the plugin — on the same 60-second check-in it was already making — and does what it is told. It never decides for itself: with the plugin not running, or answering anything other than "realistic", it uses numbered tokens.
There is one case where the plugin answers "tokens" even though you asked for
realistic: while the system door is also on. Both halves would then mask the
same browser request, one after the other, and the second pass cannot tell a
stand-in from real data. A numbered token has brackets, so it is walked straight
past and comes back to you unchanged. A realistic fake value looks exactly like
a real e-mail — that is the whole point of it — so it would be masked a second
time and nothing could put your real value back. omna style prints this, in
those words, on the browser ext line.
Secrets are never given a fake value, under any style. An API key, a token,
a password or a private key always becomes a numbered [SECRET_…] label. A fake
API key that looked real is the worst thing this program could hand you.
example.org domains, phone numbers the reserved 555-01xx range, IP
addresses the documentation ranges, and Social Security numbers an area
number that is never issued. A generated card number is deliberately left
failing its checksum: one that passed might be somebody's real card.omna mappings opens a screen showing every personal value Omna has masked on
this machine — behind the same ~/.omna/dashboard.token as the dashboard,
never a second password to manage. It shows more than "what was masked":
which layer caught it (L1 rules, L2 secrets, L3 the contextual model), whether
a checksum actually validated it, which style it was last written in (tokens
or realistic — a value masked before the two-style feature existed says
"unknown", never a guessed default), how many times it has been sent, when
last, and to which provider and app.
Real values are never on the page until you ask. Each row is hidden by default; click reveal to fetch just that one, or reveal all for every row on the current page. Nothing is embedded in the page source ahead of time, so a screenshot or a browser extension reading the DOM sees nothing until you click. Search, filter by entity type, sort any column, and paginate are all built in.
Secrets never appear here. They are memory-only from the moment they are caught and are never written to the registry — the page says so, rather than silently omitting them.
Delete removes one mapping — its token, its real value, and its
realistic-style fake together, so a "deleted" value can't keep resolving
through the fake path. The same real value gets a brand-new token or fake the
next time it's seen; the deleted one never comes back. Clear all is the
same as omna forget — it also resets your secret counters and your
encryption key.
Use Realistic Fake Values in Browser (menu bar, off by default) is the same
switch as omna style realistic, for people who would rather not open a
terminal. It changes only how a masked value is WRITTEN, in the browser.
Restore Real Values in Browser (menu bar, on by default) decides what you
read back in a browser reply: the real name, or the [EMAIL_1] label that was
actually sent. Masking is not affected by it and is never optional — this
only changes what comes back.
A browser reply only ever gets back the real values that were sent to that same site (0.6.5): a value you typed into claude.ai is never put into a ChatGPT page, and a value only a coding tool sent is never put into any website. The site's own page already saw what you typed into it; another site never did. Values saved before 0.6.5 keep coming back everywhere, as before.
It is scoped to the browser on purpose. The coding tools always get real values
put back, because there a label reaching the tool breaks it: Claude Code would
write [SECRET_AWS_KEY_1] into your file instead of editing the real line.
If you also run the Chrome extension, the plugin steps aside and lets the
extension handle the browser reply. It has to: neither half knows the other's
list, so whoever restores second could swap in a different person's value. That
is true of the [EMAIL_1] labels, which both sides number independently, and
worse of realistic fake values, where two generators draw ordinary names out of
one small pool and the wrong substitution reads as perfectly correct data. The
extension tags the requests it handles so the plugin knows, per request, to
leave that reply alone — masking still happens either way.
A request to an inference path (/v1/messages, /v1/chat/completions, /v1/responses, …) whose body cannot be
parsed as JSON (compressed, malformed, wrong content type) is answered with 400 omna_refused and never forwarded.
Uploads (/v1/files and other non-JSON bodies) are masked like an attached file: text, code, Word and Excel are masked; a picture, PDF or audio goes as it is and the receipt says file-not-masked:<kind> or passthrough. An upload form that cannot be read is refused.
If the proxy is not running, the tool's requests fail to connect: nothing leaves unmasked.
If masking itself fails at the system door (an engine error, a full disk), the request is refused
(refused-error:<kind> in the receipt), never forwarded as it was.
A file sent in pieces is refused when a piece cannot be checked on its own (a value can be cut across
two pieces); pictures and PDFs, which are never masked anyway, go as a whole one would.
The API door answers only requests addressed to 127.0.0.1 or localhost (a page that re-points its
own domain at your machine is refused), and puts back only the real values that request itself carried:
a caller who types [EMAIL_1] gets [EMAIL_1] back, never your email.
omna status shows refused: AppName → host ×N (pinned)
with the exact omna bypass app "AppName" command to let it through untouched instead.launchd restarts the daemon (seconds); one that
already cached the proxy address instead fails closed (a connection error, nothing loads) until then. This
Mac's file:// PAC is not honoured by Safari/Chrome, so the PAC itself is served by the proxy — an honest
limit either way, not a fail-closed guarantee like the coding-CLI door.about:config →
security.enterprise_roots.enabled = true. Node: NODE_EXTRA_CA_CERTS, which omna init
already sets for VS Code.) Safari, Chrome, Edge, Brave, curl and Go need nothing.--smart, which loads a
local model and slows each request; it is off by default.AWS_ACCESS_KEY_ID=[SECRET_GENERIC_SECRET_1], not one anonymous token), and a
secret span no longer swallows the newline after it. One known false positive remains: prose like
"authorization context: something" can be flagged as a generic secret.Everything in this repository is free. The paid layer, for teams, is the proof: a signed weekly evidence pack built from these receipts, per-machine coverage, and org-wide rulesets. See a sample at omna.dev/sample-report.
uv venv .venv && uv pip install -p .venv/bin/python -e '.[dev]'
.venv/bin/pytest -q # 205 tests, ~7 s (fake upstream, no network)
.venv/bin/omna start # foreground, then: ANTHROPIC_BASE_URL=http://127.0.0.1:7788 claude
MIT for this shell. The engine wheel (omna-pii-mask) is binary-only.
omna forget |
| Wipe the token registry and its encryption key (tokens renumber). |
omna crash [--show N] [--send] [--clear] | What broke on this machine. Masked by Omna's own engine before it is written to disk, and never sent anywhere — --send opens a GitHub issue pre-filled with the report you just read, and you submit it or close the tab. |
omna verify-model | Re-hash the on-device Contextual model and compare it to the hash this engine was built against. Does the full check every time, never a cached answer. |
omna dashboard | Open the live dashboard. It is protected by a local token (~/.omna/dashboard.token, owner-only), so another program on your machine cannot read your numbers over HTTP. |
omna mappings | Open the Mappings Review screen — every value Omna has masked, real values hidden until you click Reveal. Same token as the dashboard. |
omna init [--project] [--no-system] / omna uninstall | Wire / un-wire Claude Code, aider, Codex CLI, VS Code and Continue (each only if already installed), and (on a Mac, unless --no-system) the system proxy + certificate. init backs up every settings file it touches first and removes only its own keys on uninstall. |
omna tools / omna enable TOOL / omna disable TOOL | Show, or turn on/off, which tools Omna covers. enable/disable claude-code, aider, codex, or continue also wire/unwire it (same as init/uninstall); other tool names (Cursor) just record the policy today. |
omna apps / omna bypass app NAME / omna mask app NAME | Show, or set, what happens to an app's traffic through the system proxy: mask (default) tokenises it like everything else, bypass tunnels it through untouched (still receipted, so you can see what wasn't masked). |
omna capture app NAME | Stage 3: deep-capture an app that ignores the system proxy, via its own signed network extension. |
omna hosts / omna hosts add HOST / omna hosts remove HOST | Show, or add/remove, the hostnames the system proxy treats as AI traffic. |