dumbtv
dumbtv가 ADB로 Fire TV에서 광고·추적·업데이트 알림을 제거, 루트·앱 변경 없이, 무료 오픈소스
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기🦊 Anti-detect browser
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗[!NOTE] All of the latest documentation is available at camoufox.com.
Processing tens of billions of requests per month for thousands of customers, Byteful powers browser-based AI agents, automation systems, and data workflows. It is a member of the Internet Watch Foundation and the Ethical Web Data Collection Initiative.
Get 10% off Byteful Residential Bandwidth with the code: CAMOUFOX10
Get 15% off your first 3 months with code CAMOUFOX → crawlbase.com
Camoufox is intended to be used with rotating proxies (preferably residential IPs). Check out these providers:
Camoufox is a Firefox build for web scraping and AI agents. Every launch gets a fresh, internally consistent device identity drawn from the real-world distribution of devices, and the browser reports it from C++, so a page sees native values rather than JavaScript overrides. Playwright drives it, through an automation layer the page cannot see.
| Package | Install | Docs |
|---|---|---|
| Python | pip install -U "camoufox[geoip]" | python/README.md |
| TypeScript / Node.js | npm install camoufox playwright-core | typescript/README.md |
| AI coding agents | AGENTS.md |
pip install -U "camoufox[geoip]"
camoufox fetch
from camoufox.sync_api import Camoufox
with Camoufox(headless=True) as browser:
page = browser.new_page()
page.goto("https://example.com")
import asyncio
from camoufox.async_api import AsyncCamoufox
async def main():
async with AsyncCamoufox(headless=True) as browser:
page = await browser.new_page()
await page.goto("https://example.com")
asyncio.run(main())
// npm install camoufox playwright-core && npx camoufox fetch
import { Camoufox } from "camoufox";
const browser = await Camoufox({ headless: true });
const page = await browser.newPage();
await page.goto("https://example.com");
await browser.close();
What you get back is a normal Playwright Browser. Behind a proxy, add
proxy=... and geoip=True so timezone, locale, geolocation and the WebRTC IP
match the exit IP. For many identities in one browser, use NewContext().
flowchart TB
YOU(["Your Playwright code"])
subgraph L["Launcher: python/ or typescript/"]
FP["fpgen<br/>draws a real device"] --> CO["coherence.py<br/>rejects impossible combinations"]
CO --> ID[["Identity"]]
GEO["geoip<br/>timezone, locale, WebRTC IP"] --> ID
end
subgraph B["Patched Firefox: browser/"]
MC["MaskConfig<br/>reads CAMOU_CONFIG"] --> API["Web APIs answered in C++<br/>navigator, screen, WebGL,<br/>fonts, audio, WebRTC"]
SET["Per-context setters<br/>sealed before page script"] --> API
JG["Juggler<br/>isolated world"]
end
PAGE(["The web page"])
YOU -- "1 launch options" --> L
ID == "2 CAMOU_CONFIG + prefs" ==> MC
ID -- "3 NewContext()" --> SET
YOU <-- "4 Juggler protocol" --> JG
API -- "native values" --> PAGE
JG -. "trusted input, DOM reads;<br/>no code in the page" .-> PAGE
classDef you fill:#eef2f6,stroke:#57606a,color:#1f2328
classDef launcher fill:#ddf4ff,stroke:#0969da,color:#0a3069
classDef browser fill:#fff1e5,stroke:#bc4c00,color:#4a1c00
classDef page fill:#dafbe1,stroke:#1a7f37,color:#0f3d1c
class YOU you
class FP,CO,ID,GEO launcher
class MC,API,SET,JG browser
class PAGE page
style L fill:none,stroke:#0969da,stroke-dasharray:4 3,color:#0969da
style B fill:none,stroke:#bc4c00,stroke-dasharray:4 3,color:#bc4c00
coherence.py rejects combinations no real machine
produces.CAMOU_CONFIG environment variable
(keys declared in browser/settings/properties.json).
The patches read it through MaskConfig and answer from C++.NewContext() gives one context its own identity through setters that the
per-context patches add to window. They are sealed before page script runs.Each row says where it is implemented. A patch is a file in
browser/patches/; Juggler is
browser/additions/juggler/; the launcher is
the Python and TypeScript packages.
The goal is a browser that behaves exactly like stock Firefox, except that it reports a different machine. A value is spoofed when it identifies the host machine or the network. Anything else stays as Firefox has it, because something stock Firefox never does, such as noise, is a fingerprint in its own right.
| Trait | What a page sees | Why it is spoofed | Where |
|---|---|---|---|
| Navigator: User-Agent, platform, oscpu, hardware concurrency, language, appVersion | The drawn device's values, per launch or per context, in workers too | They name the OS and hardware directly, and every other value is checked against them | navigator-spoofing, fingerprint-injection patches |
| HTTP headers: User-Agent, Accept-Language | The navigator values | Servers compare the headers with what script reports | network-patches patch |
| Screen and window sizes, color depth, CSS media features | The drawn device's display, the same in JS and CSS | Screen size is among the most identifying values, and CSS media queries read it without script | screen-spoofing, fingerprint-injection patches |
| WebGL and WebGL2: vendor, renderer, parameters, extensions, shader precision, context attributes | One GPU fpgen recorded for Firefox on that OS. Values Firefox decides rather than the GPU follow the shipped Firefox (webgl-browser-owned-values-follow-firefox) | The renderer string names the host's GPU, and its limits must agree with it | webgl-spoofing patch, webgl.py |
WebGPU: navigator.gpu and its adapter | navigator.gpu only where Firefox on the claimed device has it: Windows and Apple Silicon Macs. The adapter is the host's only when the identity claims the host's GPU; otherwise requestAdapter() returns null, as for a blocked or software GPU | The adapter's limits and features come from the real GPU | launcher, host_rendering.py |
| Audio: sample rate, output latency, max channels | The identity's values | They follow the claimed device's audio hardware | audio-context-spoofing patch |
Fonts: installed list, system-ui, CSS2 system fonts | The claimed OS's fonts, from a bundled set; see docs/FONTS.md | Installed fonts reveal the OS and the software on it |
Left as stock Firefox has it, on purpose:
| Trait | What a page sees | Why it is not spoofed |
|---|---|---|
| Canvas pixels | What the GPU and fonts produce. As in stock Firefox, each context's PNG exports (toDataURL, toBlob) carry a per-session deBG chunk; the pixels themselves are untouched. On the host's OS the identity claims the host's GPU, so the pixels agree with it; on another OS readback returns random data, as privacy.resistFingerprinting does in LibreWolf (the canvas on another OS) | Stock Firefox does not perturb pixels in its default configuration, so noise would only mark the browser as a spoofer (canvas-is-not-noised) |
| Text widths | What the font really measures | The same font on the same OS measures the same everywhere; widths no real machine produces are a fingerprint (no-glyph-spacing-noise) |
Audio render (OfflineAudioContext, AnalyserNode) | What the host CPU computes, the same in every context and every launch | Stock Firefox renders one value per CPU architecture, whatever the OS, sound card or sample rate, so a noised value is one no real machine produces (audio-is-not-noised) |
Each context of one browser can carry its own identity: docs/per-context-patches.md.
Canvas and WebGL pixels come from the real OS, driver and GPU, whatever the identity claims. The launcher chooses the canvas behaviour from two facts: does the identity claim the host's OS, and does the host render on a GPU?
| Identity on the host's OS | Identity on another OS | |
|---|---|---|
| GPU rendering | 🟢 Most stealthy ✅ No canvas noise: looks like stock Firefox ✅ The claimed GPU is the host's, so the pixels agree with it ⛔ Every identity on this machine claims the same GPU, so there is less fingerprint diversity | 🟡 Less stealthy ✅ Readback is random, so the real OS does not show ✅ A GPU is drawn per identity, so identities differ ⛔ Canvas noise: looks like LibreWolf, Tor Browser or Mullvad Browser, which are rarer than stock Firefox |
| Software rendering(no GPU driver, as on most servers) | 🟢 Stealthy ✅ No canvas noise: looks like stock Firefox ✅ A GPU is drawn per identity, so identities differ ⛔ Looks like a GPU falling back to software rendering, which is rarer than a working GPU | 🟡 Less stealthy ✅ Readback is random, so the real OS does not show ✅ A GPU is drawn per identity, so identities differ ⛔ Canvas noise: looks like LibreWolf, Tor Browser or Mullvad Browser, which are rarer than stock Firefox |
That is the default. These options override it:
| Override | Result |
|---|---|
canvas_noise=False on another OS | 🔴 Stock Firefox, but the pixels show the real OS |
canvas_noise=True on the host's OS | 🟡 Looks like LibreWolf with no need to |
webgl_config naming a GPU other than the host's | 🟡 Canvas noise is turned on, as on another OS; with canvas_noise=False, 🔴 WebGL reports one GPU while the pixels come from another |
Noise is set per browser, not per context. Without it, every NewContext()
claims the host's OS. Details are in
the canvas on another OS.
| What | Where |
|---|---|
Playwright's evaluate, selectors and init scripts run in an isolated world the page cannot see | Juggler |
navigator.webdriver is false | playwright/1-leak-fixes patch |
select_option, fill on date and colour inputs, and set_input_files fire trusted events | trusted-automation-events patch |
| Headless reports a fine pointer, like a desktop | force-default-pointer patch |
| Wheel events carry native ticks, like a physical wheel | wheel-native-ticks patch |
| Juggler's debugger leaves no side effects content can observe | debugger-invisible-to-content patch |
| Per-context fingerprint setters are gone before page script runs | window-setter-seal patch |
| Popup blocker on, as in stock Firefox | popup-blocker-parity patch |
| On X11, a pointer-locked window partly off the top or left of the screen keeps running (stock Firefox can crash: the X server refuses the cursor barrier and GDK aborts) | pointer-lock-offscreen patch |
| Content-accessible chrome files match stock Firefox | contentaccessible-parity patch |
| Viewports never enter Responsive Design Mode; containers show no automation label | Juggler |
| Feature | How to use it | Where |
|---|---|---|
| Human cursor movement, replayed from 2356 recordings of real people (Cursory) | humanize=True, or a number of seconds to cap each move | Juggler input/ |
| Run code in the page's own world | main_world_eval=True, then page.evaluate("mw:...") | Juggler |
| Finish animations at once so Playwright never waits | config={"instantAnimations": True} (detectable; warns) | no-css-animations patch |
| Read closed shadow roots | config={"forceScopeAccess": True} | shadow-root-bypass patch |
| Many identities in one browser | NewContext(browser, os=..., proxy=...) | per-context patches, launcher |
| Feature | Where |
|---|---|
Load extracted addons without a debug server: addons=["/path/to/addon"] | launcher, camoufox-window-module patch |
uBlock Origin installed by default: each browser release bundles the newest uBO release on AMO when it is built (browser/scripts/fetch-ubo.py), blocking with the default filter lists packaged in it, never downloaded at launch (exclude_addons=[DefaultAddons.UBO] to drop it) | packaging, launcher, policies.json |
Addons cannot open tabs unless allow_addon_new_tab=True | disable-extension-newtab patch |
| Addons run in private browsing | all-addons-private-mode patch |
| Feature | Option or command |
|---|---|
| Identity drawn from fpgen's real-world distribution, checked for coherence | default |
| Recorded real-device presets instead of fpgen | fingerprint_preset=True |
| Timezone, locale, geolocation and WebRTC IP from the proxy's exit IP | proxy=..., geoip=True |
| Locale chosen by the languages spoken in the IP's region | geoip=True without locale |
| Headed browser on a private Xvfb display (Linux) | headless="virtual" |
| Headed browser on a private Xorg display that renders on a named GPU (Linux) | headless="virtual-gpu" |
| Remote Playwright server for other languages | launch_server(), camoufox server |
| Install, pin and switch browser builds | camoufox fetch, set, list, remove |
| Core count that a page can measure | pin_cpu_cores=True |
| Block images, WebRTC or WebGL | block_images, block_webrtc, block_webgl |
| The host's GPU on the host's OS; random canvas readback, as in LibreWolf, on another OS | default; canvas_noise=True or False to choose |
The full option list is in the Python README.
| What | Where |
|---|---|
| Telemetry and data reporting removed at compile time | librewolf/ patches |
| Onboarding messages disabled | ghostery/ patch |
| No bundled search engines | no-search-engines patch |
| Mozilla services and background traffic turned off | browser/settings/camoufox.cfg |
| Firefox's bundled certificate revocations (OneCRL) enforced in every profile, with no network fetch | remote-security-settings patch |
| Stock browser UI: no theme, toolbar or dialog changes |
[!NOTE] Camoufox presents Firefox identities only. Some WAFs test SpiderMonkey engine behaviour, which no amount of spoofing turns into Chromium.
| JavaScript injection | CDP-based (Chromium) | Camoufox | |
|---|---|---|---|
| Where values are spoofed | Page-world overrides | Page-world overrides or flags | Browser C++ |
toString(), property descriptors | Show the override | Show the override | Native |
| Workers and iframes | Often missed | Often missed | Same value everywhere |
| Headers agree with navigator | Only if set separately | Only if set separately | Always |
| Automation code in the page | Yes | Yes (bindings, navigator.webdriver) | No: isolated world |
| Identity source | Hand-picked or random | Hand-picked or random | fpgen's real-world distribution, coherence-checked |
Playwright drives Chromium over CDP but Firefox over Juggler, a protocol that is a separate module inside Firefox. That makes it possible to give Juggler its own copy of the page: Playwright reads and edits that copy, and the real page never sees a getter fire or a listener appear. Input goes through Firefox's own input handlers, so it is handled as a user's would be.
Camoufox can still be detected. Thousands of values must agree with each other, and anti-bot vendors look for the one that does not. File a detection report when you find one.
Each dot is a mousemove event the page received from six page.mouse.move()
calls with humanize=True, shown at the speed it arrived; close dots mean the
hand slowed down. browser/scripts/cursor-demo.py regenerates the figure.
Camoufox does not compute its cursor paths. It picks one of Cursory's 2356
recorded human movements that suits the move, morphs it onto the start and end
points, and replays it with the recording's own timing, including pauses and
overshoots. It ships cursory-js, a
TypeScript port that reproduces the Python original exactly, at
browser/additions/juggler/input/cursory/. Cursory is LGPLv3-or-later, not
MPL-2.0 like the rest of the browser; see
NOTICE.
This repository is not the Firefox source. It fetches Firefox, copies in
browser/additions/, applies
browser/patches/ and builds. The build runs on Linux; the
Windows and macOS binaries are cross-compiled. WSL does not work.
flowchart TB
subgraph IN["From this repository"]
direction LR
UP["upstream.sh<br/>Firefox version"]
ADD["additions/ + settings/<br/>files copied in"]
PAT["patches/<br/>diffs applied"]
end
S1["<b>1. make dir</b><br/>download Firefox, copy additions,<br/>apply every patch"]
S2["<b>2. make bootstrap</b><br/>toolchains, once per machine"]
S3["<b>3. make build</b><br/>./mach build"]
S4["<b>4. make package-linux</b><br/>or package-macos, package-windows"]
FONTS["font bundle + fontconfig"]
OUT[("Release archive")]
IN --> S1 --> S2 --> S3 --> S4 --> OUT
FONTS --> S4
classDef input fill:#ddf4ff,stroke:#0969da,color:#0a3069
classDef step fill:#fff1e5,stroke:#bc4c00,color:#4a1c00
classDef out fill:#dafbe1,stroke:#1a7f37,color:#0f3d1c
class UP,ADD,PAT,FONTS input
class S1,S2,S3,S4 step
class OUT out
style IN fill:none,stroke:#0969da,stroke-dasharray:4 3,color:#0969da
git clone --depth 1 https://github.com/daijro/camoufox
cd camoufox/browser
make dir # fetch Firefox, copy additions, apply every patch
make bootstrap # one time: host packages and mach bootstrap
make build # ./mach build
make run # run the build
make package-linux arch=x86_64 # or package-macos / package-windows
make help lists every target. Install ccache: a cold build takes about 40
minutes and an incremental one about 5.
To build and package several targets in one go:
python3 multibuild.py --target linux windows macos --arch x86_64 arm64
multibuild.py flag | Values |
|---|---|
--target | linux, windows, macos (one or more) |
--arch | x86_64, arm64, i686 (one or more; i686 builds Windows only) |
--bootstrap | Bootstrap the build system first |
--clean | Clean the build directory first |
Artifacts are written to browser/dist/.
cd browser
docker build -t camoufox-builder .
docker run -v "$(pwd)/dist:/app/dist" camoufox-builder --target linux --arch x86_64
docker run -v "$(pwd)/dist:/app/dist" camoufox-builder --target windows --arch x86_64
The container takes the multibuild.py flags. Add
-v "$HOME/.mozbuild":/root/.mozbuild:rw,z to reuse the host's toolchains.
| Task | Where |
|---|---|
| Write or edit a patch | AGENTS.md |
| Port patches to a new Firefox | docs/patch-upgrading-guide.md |
| Which suite to run for a change | AGENTS.md |
| The CI pipeline and running any job locally | ci/README.md |
| Contributing | CONTRIBUTING.md |
A way to find what a site detects without deobfuscating its JavaScript: start from stock Firefox and add Camoufox's changes back until the site flags.
flowchart TD
START(["A site flags Camoufox"]) --> Q1{"Does it flag<br/>stock Firefox?"}
Q1 -- yes --> NB["Not the browser:<br/>a bad IP or rate limiting"]
Q1 -- no --> BASE["Build the debug baseline<br/>make ff-dbg (1), make build (2)"]
BASE --> Q2{"Does it flag the<br/>baseline headless (4)?"}
Q2 -- yes --> Q3{"Headed (3)<br/>as well?"}
Q3 -- yes --> NB
Q3 -- no --> Q4{"Still flagged with<br/>privacy.resistFingerprinting (6)?"}
Q4 -- no --> FPP["Enable FPP and drop overrides<br/>until the leak returns"]
Q4 -- yes --> DEOB["Deobfuscate the site's script<br/>to see what it tests"]
Q2 -- no --> CFG["Apply config.patch (5)<br/>and rebuild (2)"]
CFG --> Q5{"Still passes (3)?"}
Q5 -- no --> PREFS["Remove prefs from camoufox.cfg (6)<br/>until it passes"]
Q5 -- yes --> PW["Apply playwright/0-playwright.patch (5)<br/>and rebuild (2)"]
PW --> Q6{"Still passes (3)?"}
Q6 -- no --> JUG["Debug Juggler"]
Q6 -- yes --> REST["Apply the other patches one at a time<br/>until it flags"]
JUG -. "if Juggler looks clean" .-> DEOB
classDef start fill:#eef2f6,stroke:#57606a,color:#1f2328
classDef ask fill:#ddf4ff,stroke:#0969da,color:#0a3069
classDef step fill:#fff1e5,stroke:#bc4c00,color:#4a1c00
classDef found fill:#dafbe1,stroke:#1a7f37,color:#0f3d1c
class START start
class Q1,Q2,Q3,Q4,Q5,Q6 ask
class BASE,CFG,PW step
class NB,FPP,DEOB,PREFS,JUG,REST found
| # | Command | What it does |
|---|---|---|
| 1 | make ff-dbg | Stock Firefox with the minimum Camoufox needs |
| 2 | make build | Build |
| 3 | make run | Run the build |
| 4 | make run args="--headless https://example.com" | Run headless |
| 5 | make patch ./patches/<name>.patch | Apply one patch (make unpatch reverses it) |
| 6 | make edit-cfg | Edit camoufox.cfg |
| Part | Licence |
|---|---|
The browser (browser/: patches, additions, settings, build system) | MPL-2.0, the licence of the Firefox source it modifies |
| Vendored Cursory trajectories | LGPLv3-or-later (NOTICE) |
| Python package | MIT (python/LICENSE) |
| TypeScript package | MIT (typescript/LICENSE); includes a port of NumPy's pairwise summation (notice) and depends on fpgen-js (Apache-2.0) and python-random |
| Project | Used for |
|---|---|
| LibreWolf | Debloat patches and the original build system |
| BetterFox | Speed and debloat preferences |
| Ghostery | Debloat reference (disable onboarding) |
| Vinyzu/cursory | The recorded human mouse movements behind humanize=True, vendored via cursory-js |
| riflosnake/HumanCursor | The Bézier cursor Camoufox used before Cursory |
| scrapfly/fingerprint-generator (fpgen) | The device distribution identities are drawn from |
| CreepJS, Browserleaks, BrowserScan | Leak testing |
font-list-spoofing, font-hijacker, system-ui-font-spoofing patches |
| Speech voices | The claimed OS's voices, which actually speak | The voice list is specific to each OS, and a voice that cannot speak is a tell | voice-spoofing, speech-voices-spoofing patches |
| Microphones, cameras, speakers | OS-style labels and groups; see docs/MEDIA-DEVICES.md | Device count and labels differ per machine and per OS | media-device-spoofing patch |
Media codecs (canPlayType, isTypeSupported) | Not the host's codec libraries | On Linux the answer depends on the host's installed codec libraries | media-codec-spoofing patch |
Timezone, locale and Intl | The identity's, per realm, workers included | They must agree with the IP's country | timezone-spoofing, locale-spoofing patches |
| Geolocation | Coordinates from GeoIP, permission granted | They must agree with the IP | geolocation-spoofing patch, launcher |
| WebRTC IP | ICE candidates, SDP and getStats() rewritten to the proxy IP | WebRTC otherwise reveals the real IP behind a proxy | webrtc-ip-spoofing patch |
| Touchscreen | A touchscreen laptop, not a phone | Touch points and pointer media must agree with the claimed device | touchscreen-fingerprint-spoofing, force-default-pointer patches |