dumbtv
dumbtv takes the ads, screen tracking and update nags out of your Fire TV over ADB. No root, none of your apps touched, every step can be undone. Free and open source.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
A security console in two halves, in one web application:
• the SOC handles what has already happened — an alert is already an incident;
• VulnPipe handles what is about to happen — a flaw in the code, before it ships.
→ menater.vercel.app — what the product is, what
it refuses to do, and what it does not do yet, with screenshots of a running
install. Read that first if you are here to find out what this is.
This project is built to run in Docker
That is the normal way to install it, not a deployment option. Three
commands, on any host:
Then.檢視完整 README ↗A security console in two halves, in one web application:
→ menater.vercel.app — what the product is, what it refuses to do, and what it does not do yet, with screenshots of a running install. Read that first if you are here to find out what this is.
That is the normal way to install it, not a deployment option. Three commands, on any host:
cp .env.example .env # fill in the two passwords
docker compose up -d --build
Then http://localhost:4400.
Nothing else is required. No Node to install, no Postgres to configure, no SQL schema to apply by hand, no workflow engine to install, no third machine. The stack is self-contained and behaves the same on a Mac, on Linux, or on a Raspberry Pi.
| Service | Role | Port |
|---|---|---|
console | Interface, API, and the workflow engine | 4400 |
postgres | Run log, audit, deduplication, variables, tuning rules | internal |
vulnpipe | Code vulnerability analysis | internal |
One port is published. The database and the analyser are reachable only
from the compose network: the console relays /api/vulnpipe/* to the second,
and nobody talks to the database directly. One origin, one lock — a second
entry point would be a lock protecting half the application.
up: the four things to setThe stack comes up in three minutes and triages nothing. That is not a fault — each of these is a choice the console refuses to make for you. The checklist at the top of Settings tracks all four, and disappears when they are done.
| # | What | Where | If you skip it |
|---|---|---|---|
| 1 | Shared secret | Settings → Ingestion | The endpoint stays closed whatever mode you pick |
| 2 | Open the door — mode Primary | Settings → Ingestion | Every alert is refused |
| 3 | Model key (OpenRouter) | Settings → Credentials | Every alert takes the fail-safe verdict: needs_human, confidence 0. Nothing is lost, but nothing is triaged |
| 4 | A log source | Settings → Ingestion → Sources | No alerts arrive |
Slack and the enrichment keys (Shodan, AbuseIPDB, VirusTotal) are optional. A missing enrichment source is shown as unavailable rather than filled in, and the model has to name it in its reasoning.
On the ingestion mode. There are two:
Closed, the default, which refuses everything, andOpen, where the built-in engine handles alerts. A shared secret is required either way — an empty one keeps the door closed rather than opening it to everyone.
Alerts arrive at POST /api/ingest/<source>. The route decides which mapping
applies — never the payload, so a sender cannot claim to be something else.
Settings → Ingestion generates the endpoint, the install commands and the
config block, using the address you reached the console on.
One file to copy, one block to paste — see integrations/wazuh/README.md.
scp integrations/wazuh/custom-menater root@wazuh-manager:/var/ossec/integrations/
chmod 750 /var/ossec/integrations/custom-menater
chown root:wazuh /var/ossec/integrations/custom-menater
systemctl restart wazuh-manager
The script maps nothing: it forwards the alert exactly as Wazuh wrote it, and the console does the translation. An integrator that maps fields turns "add a log source" into "maintain a parser on a production security appliance", unversioned and invisible from the console.
| Tier | Fields | Rule |
|---|---|---|
| Identity | alert_id, rule_name, severity, timestamp, raw_log | Required. Missing means rejected, and the rejection names them |
| Observables | source_ip, dest_ip, user, host, process, file_path, url | Optional. Absent stays absent — never invented, never an empty string |
| Extensions | everything else | Kept whole. Nothing your tool sends is discarded |
Most real detections have no destination address. That is why dest_ip is
optional: rejecting the alert over it would replace it with nothing.
The Rules tab is where a team teaches the console what is normal here — the authorised scanner, the backup window, the deployment account. Without it an honest console alerts on everything, and one that alerts on everything stops being read.
A rule can close an alert as known, suppress it temporarily, change its severity, or demand a human. It can never trigger an action: no rule effect reaches the action catalogue.
Three things the console refuses, each matching an exception shape that is known to go wrong:
allow keyed on one identity field alone — attackers use
legitimate addresses and accounts, so that is a hole shaped like an intrusion;suppress with no expiry — it is temporary by definition;Eight templates ship disabled and unowned, all written as conjunctions. The dry-run panel answers which rule would match this alert without touching the pipeline.
Settings → Health → inject a test alert offers nine scenarios, one per path rather than per story: no destination, with a hash, low severity, malformed, duplicate, and so on. Each creates a real case; the malformed one is rejected on purpose, and the duplicate one exercises deduplication.
docker compose --profile tunnel up -d # exposure to the Internet
tunnel opens a Cloudflare tunnel to the alert endpoint. Only enable it
after setting a shared secret in Settings → Ingestion: without one the
endpoint stays closed, but the tunnel would already be open.
They are off by default because a service you do not know you started is a service you are not watching — and one of the two opens a door to the Internet.
sql/ in order — audit with hash chaining, error log,
engine run log, deduplication, metrics function, tuning rules;Data lives in named volumes: it survives docker compose down and
up --build. To start over — including the database: docker compose down -v.
Docker is still the normal mode. To work on the code with hot reload:
cd dashboard && npm install
npm run dev # API (4400) + code analysis (4319) + Vite interface (5174)
npm test # 1219 tests, 1 skipped (the one needing a database)
See dashboard/README.md.
The Postgres store tests are skipped without a database — and say so. To run them against the Docker stack:
MENATER_TEST_PG=postgres://menater:<password>@localhost:5432/menater_test npm test
site/ is a static page describing the project, deployed on Vercel at
menater.vercel.app. It is not part of the
console and the Docker stack does not serve it: nothing in docker-compose.yml
knows it exists.
| File | Holds |
|---|---|
site/page.src.html | The front page. The short version — plain words, no jargon, ~670 rendered words |
site/docs.src.html | The documentation. Sixteen sections behind a sticky sidebar, ~4 800 words |
site/shared.css | One stylesheet, inlined into every output |
site/partials/ | Header, footer and script, shared by both pages |
site/i18n/fr.json | The French, against the same keys |
site/shots/ | Seven screenshots of a running install, and captions.json beside them |
site/build.sh | Writes the six outputs from those sources |
site/check.cjs | Measures the built site in a browser: contrast, overflow, anchors, script errors |
cd site && ./build.sh # after editing a source, a caption or a catalogue
Two pages, and they answer different questions. / is for someone deciding
in ten seconds whether this is for them; /docs is for someone who already
decided and wants the specifics. The split is why the front page could drop from
3 147 words to 670 without deleting anything: what used to be folded on it now
has a page of its own, with a sidebar and its own vocabulary section.
| Built | Served at |
|---|---|
site/index.html | / |
site/docs/index.html | /docs |
site/fr/index.html | /fr |
site/fr/docs/index.html | /fr/docs |
site/page.html · site/page.fr.html | nowhere — the front page with its screenshots inlined, for a host that blocks external images |
Two languages, and the URL is the whole state. Each page is a static file
with its own lang attribute and hreflang pair; the switcher is two links
that carry your anchor across and keep you on the page you were reading. No
cookie, no header sniffing, no redirect — a link somebody shares opens in the
language they shared it in, and the page never arrives in one language and
repaints in another. A key present on one side only fails the build, naming
it: the typed-catalogue rule the console applies to console.ts, applied to a
page.
Contrast is measured, not looked at. check.cjs drives a real browser over
both pages, both languages and all six palettes — 648 checks — plus horizontal
overflow from 320 px, every in-page anchor, and script errors. It needs
Playwright; it exits non-zero on a failure, so it can gate a change.
Root Directory is site, and that is the only setting. Vercel's importer
scans the repository for a framework, finds dashboard/ — a Vite app — and
offers to build the console instead, pre-filling seventeen environment
variables from the root .env.example along the way. Pointing it at site/
removes the question: that folder has no package.json and no .env.example,
so there is nothing to detect and nothing to ask for.
| Setting | Value |
|---|---|
| Root Directory | site |
| Framework Preset | Other |
| Build / Install Command | leave empty |
| Environment Variables | none — the page is static and reads nothing |
site/vercel.json carries the rest: cleanUrls — which is what makes
docs/index.html answer at /docs — long-lived caching on the screenshots,
which never change under their own name, a no-cache rule on everything else so
a deploy is visible immediately, and the three headers a public page should not
be without. site/.vercelignore keeps the generator, the browser check and the
inlined variants off the public host.
Every screenshot is a real screen, taken against docker compose up with
alerts injected through the real pipeline — no mockups, and none of them is a
console with nothing in it. The captions say what state the install was in,
including that no model key was configured, which is why every verdict on them
reads fallback verdict. A page about a product that refuses to fill a gap with
a default cannot itself show an invented one.
| File | Contents |
|---|---|
| CLAUDE.md | Architecture, design rules, and the traps that cost time |
| ROADMAP.md | What is delivered, what is coming, and why — one list for both halves |
| dashboard/README.md | The console in detail |
| integrations/wazuh/README.md | Connecting Wazuh, end to end |
| VulnPipe/README.md | The code analysis engine |
The Guide tab documents every feature from inside the application, which is where the question usually gets asked.
No irreversible action without explicit human approval. The action
catalogue is closed: isolate_host_temporary (with a TTL and automatic
revert), ticket, escalate, auto_close. Nothing else is implementable,
not even behind an approval — and no tuning rule can reach it.
Silence is never consent. An approval that expires executes nothing: it
escalates. And missing configuration cannot enable execution — shadow mode is
on by default, and only the exact string false turns it off.