156 Commits

Author SHA1 Message Date
Jake Writer 15c513296d Merge branch 'main' of https://github.com/daijro/camoufox into main
Conflict: pythonlib/camoufox/utils.py — both sides fixed the fontconfig
cache dir independently (#654 here, #712 upstream). The two spellings
resolve to the same path, since pkgman's INSTALL_DIR is
platformdirs.user_cache_dir("camoufox"). Kept INSTALL_DIR so the module
has one name for that directory, dropped the now-unused platformdirs
import, and kept the comment explaining why the dir must sit outside the
read-only browser bundle.
2026-08-20 10:58:28 -06:00
daijro cd83f7fd2f Bump python library to 0.5.5 2026-08-12 06:26:10 -05:00
D Yiapanis 658d00e0c7 fix: use platformdirs for fontconfig cache directory
_generate_fontconfig hardcoded ~/.cache/camoufox/fontconfig instead
of respecting XDG_CACHE_HOME. On systems where ~/.cache is read-only
(e.g. containerized environments), this causes OSError on browser launch.

Replaced os.path.join(os.path.expanduser('~'), '.cache', 'camoufox',
'fontconfig') with os.path.join(platformdirs.user_cache_dir('camoufox'),
'fontconfig'). platformdirs is already a declared dependency and
respects XDG_CACHE_HOME on Linux, ~/Library/Caches on macOS, and
%LOCALAPPDATA% on Windows.

Closes #654
2026-08-04 17:51:15 +10:00
Jake Writer 75ee7eb267 fix(virtdisplay): keep 1x1x24 as the headless="virtual" default
Reverts the default half of 4b20b77. That commit raised Xvfb's root window
from 1x1x24 to 1920x1080x24 for #458, on the reasoning that a 1x1 root
"breaks anything that measures the screen".

That reasoning does not hold here:

- screen.* never comes from the root window. It comes from the generated
  fingerprint, applied per context in the browser, and
  clamp_screen_to_display() is skipped outright for virtual displays (the
  `not virtual_display` guard in utils.py), so a 1x1 root cannot clamp a
  generated screen down to 1x1.
- #458's actual symptom -- blank/dark screenshots -- does not reproduce on
  152.0.4-beta.28. Measured at both geometries on the same build, same page:

    Xvfb 1x1x24        493 distinct colours, 57.5% dominant  -> renders
    Xvfb 1920x1080x24  493 distinct colours, 55.8% dominant  -> renders

  Identical. Firefox composites offscreen, so the root window size does not
  gate rendering. A full-page screenshot of example.com under 1x1x24 is
  pixel-correct.

1x1x24 is Camoufox's long-standing default and has run that way for years.
CAMOUFOX_VIRTUAL_DISPLAY_SIZE is kept as an escape hatch for anyone who does
want a real framebuffer, and still validates its input.

The Composite half of 4b20b77 was already reverted separately in 75d09a3.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 00:19:20 -06:00
Jake Writer 1e838e86d5 ensure window === self, restore test 2026-07-30 15:50:10 -06:00
Jake Writer fa8a93577e fix(juggler): record video headful and under a virtual display (#93)
After the screencastFrameAck/timestamp fix, recording worked headless but still
produced nothing usable anywhere else: `headless="virtual"` and plain headful
both emitted a valid .webm containing 24 pure-white frames -- Playwright's
filler for a screencast that never delivered a frame.

nsScreencastService only has a working source when the browser is headless
(HeadlessWindowCapturer). Outside headless, CreateWindowCapturer falls through
to libwebrtc's X11 window capturer, which fails three different ways:

  * no XComposite -> startVideoRecording() succeeds and then never delivers a
    frame. This is Camoufox's own Xvfb configuration, which passes
    `-extension COMPOSITE`;
  * XComposite enabled -> the browser segfaults during capture (reproduced on
    the shipped 152.0.4-beta.28 as well, so it is not specific to this branch);
  * Wayland -> nsWindow::GetNativeData(NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID) is
    documented as unhandled and returns null, so the service throws
    NS_ERROR_FAILURE ("Failed to get native window id") and no capture starts.

Capture from the compositor instead when not headless, via
WindowGlobalParent.drawSnapshot() -- the same call Page.screenshot already
uses, which is why screenshots have always worked in every mode. It renders
page content directly and does not care about the windowing system.

The tick is ack-driven, mirroring nsScreencastService's kMaxFramesInFlight = 1,
so a slow consumer throttles capture rather than queueing JPEGs. Headless keeps
the native C++ capturer, which is cheaper and already correct.

Measured on the packaged Linux build, 3s recording of an animated page, frames
decoded to PNG and inspected rather than trusting file existence:

                        before                  after
  headless              100 frames, real        unchanged, real
  headless="virtual"    24 frames, all white    100 frames, real
  headful (Xvfb, X11)   24 frames, all white    99 frames, real
  headful (Wayland env) no capture at all       99 frames, real

tests/async/test_video.py passes 5/5 both headless and headful. Enabling
Composite no longer crashes either, since X11 window capture is now unused.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 14:32:42 -06:00
Jake Writer 75d09a3dad fix(virtdisplay): stop enabling Composite by default -- it segfaults recording
9654452 enabled Xvfb's Composite extension on the theory that #93 (no video
under headless="virtual") was caused by disabling it. Measurement disproves it:

  composite off + record_video_dir -> valid .webm, 24 pure-white frames
  composite ON  + record_video_dir -> browser dies with SIGSEGV, no video
  composite ON  + no recording     -> fine

So compositing does not fix #93, and defaulting it on turns a blank recording
into a crash for anyone recording under a virtual display. The segfault
reproduces on the shipped 152.0.4-beta.28 too, so it is a pre-existing fault in
the screencast capture path rather than something this branch introduced -- but
that is exactly why it should not be reached by default.

Kept as an opt-in (CAMOUFOX_VIRTUAL_DISPLAY_COMPOSITE=1) for hosts with real
GL, where it may behave differently. The real-screen-size half of 9654452 is
unaffected and stays.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 14:32:42 -06:00
Jake Writer 4b20b771e2 fix(virtdisplay): give headless="virtual" a real screen and Composite (#458, #93)
Two hardcoded Xvfb arguments, both verified against a live Xvfb with xdpyinfo.

#458 -- `-screen 0 1x1x24`. A 1x1 root window is not a plausible desktop: it
breaks anything that measures the screen, and it is the reason
clamp_screen_to_display() has to special-case virtual displays (a generated
fingerprint would otherwise be clamped to 1x1). Default to 1920x1080x24;
the framebuffer cost is ~8MB. Overridable per-run with
CAMOUFOX_VIRTUAL_DISPLAY_SIZE="1920x1080[x24]", which is validated and rejects
malformed values rather than passing them to Xvfb.

#93 -- `-extension COMPOSITE`. Offscreen rendering needs Composite, which is
what Playwright's video recording uses, so disabling it silently broke
record_video_dir under headless="virtual". A real X server has the extension,
so enabling it is also the more faithful default. Set
CAMOUFOX_VIRTUAL_DISPLAY_COMPOSITE=0 to restore the old behaviour.

Verified with xdpyinfo against real Xvfb instances:
  default            -> dimensions 1920x1080, Composite present
  screen="800x600x24", composite=False -> dimensions 800x600, Composite absent
  CAMOUFOX_VIRTUAL_DISPLAY_SIZE=2560x1440 -> resolves to 2560x1440x24
  CAMOUFOX_VIRTUAL_DISPLAY_SIZE=bogus     -> VirtualDisplayNotSupported

xvfb_args becomes a property so the two settings can vary per instance; the
existing VirtualDisplay(debug=...) call sites are unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 14:32:42 -06:00
Aleksandr d5d7713684 fix: add allow_addon_new_tab launch option 2026-07-30 14:32:42 -06:00
Jake Writer 2834a463d1 test(virtdisplay): assert the real post-condition of kill()
`VirtualDisplay.kill()` reaps the Xvfb child and then clears `self.proc`, so
asserting `vd.proc.poll() is not None` afterwards raises AttributeError on
None. Two tests failed this way on main, unrelated to any of the merged PRs.

Assert `proc is None or proc.poll() is not None` -- reaped-and-cleared is the
success path, and a surviving handle must still report an exit code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 00:33:55 -06:00
Jake Writer c61c55f108 fix(server): reject unservable persistent-context options (#161)
PR #398 added `persistent_context` / `user_data_dir` to `launch_options()` and
emitted `_user_data_dir` in the result, on the assumption that Playwright's
`browserServerImpl` consumes it. It does not.

`launchServer()` spreads its options into `BrowserType.launch()`, which passes
`undefined` as the userDataDir and never reads `options._userDataDir` (only
`browser._userDataDirForTest` is ever assigned, after the fact). Verified
against the bundled playwright-core 1.53.1: launching a server with
`user_data_dir=/tmp/...` starts cleanly and leaves the directory empty.

Serving a persistent context is not merely unimplemented, it is outside
Playwright's server model: `launchPersistentContext` returns a BrowserContext
while `PlaywrightServer` only accepts a `preLaunchedBrowser`.

So keep #398's genuinely-correct `camel_case` fix -- it lets any underscore-
prefixed private option reach the driver -- and drop the two options that would
otherwise be accepted, validated, and silently ignored. `launch_server()` now
fails loudly and points at the in-process API instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 00:31:59 -06:00
Piero Rolando c11b920524 Fix launch_server, to be able to use persistent_conext and user_data_dir 2026-07-28 00:29:18 -06:00
Cloudymap1e e61642aaf5 fix(server): close browser when launcher exits 2026-07-28 00:29:11 -06:00
Pratyush Sharma a5afa46cfa Apply screen constraints on Windows and macOS
get_screen_cons() was gated on DISPLAY being set, which only ever happens on
Linux, so headful runs on Windows and macOS generated fingerprints with no
monitor bound at all.

Fixes #425
2026-07-28 00:29:03 -06:00
Pratyush Sharma 22c6ffbdda Probe the host monitor in CSS pixels
screeninfo makes the process per-monitor DPI aware, so it reports physical
pixels, while Firefox lays windows out in CSS pixels. At 150% Windows scaling a
1920x1080 panel is 1280x720 CSS px, so bounding the fingerprint by the physical
size lets the window open 1.5x larger than the screen.

Refs #425
2026-07-28 00:29:03 -06:00
Pratyush Sharma fbafbcf9f0 Exclude virtual displays from the display clamp
headless='virtual' reaches launch_options as headless=False with
virtual_display set (async_api rewrites it), so the headful gate fired and
clamped the fingerprint to Xvfb's 1x1 stub. fix_screen_no_taskbar then drove
availHeight to -39 and validate_config rejected the launch outright.
2026-07-28 00:29:03 -06:00
Pratyush Sharma 2266f27501 Clamp headful window geometry to the real display
get_screen_cons() bounds the generated fingerprint to the monitor, but
BrowserForge honours a Screen constraint only when its pool has a match:
FingerprintGenerator.partial_csp catches the filtering failure and deletes the
constraint unless strict=True. So a 1366x768 laptop routinely gets a 2560x1440
fingerprint with window.outerWidth 1920, and browser-init resizes the real
chrome window to it -- rendering past the edge of the monitor.

Re-apply the bound after generation instead of trusting BrowserForge with it,
and pull screenX/screenY back inside the shrunken screen.

Headful only. headless has no window to overflow, and headless='virtual' runs a
1x1 Xvfb whose "monitor" would otherwise shrink the fingerprint to 1x1.

Fixes #499
2026-07-28 00:29:03 -06:00
Jake Writer 63860c9cab fix(python): keep the runtime fontconfig outside the browser bundle (#654)
PR #678 made the fontconfig cache XDG-aware, but `get_path('fontconfig')`
resolves inside the versioned browser install directory
(.../browsers/official/<version>-<hash>/fontconfig/), which already holds the
bundled linux/ macos/ windows/ trees and is read-only in the common
"bake the browser into the image as root, run as non-root" deployment.

Use INSTALL_DIR / 'fontconfig' instead: still XDG-aware, but outside the
bundle. This is byte-identical to the pre-#678 path when XDG_CACHE_HOME is
unset, so existing caches are reused and no migration is needed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 00:28:52 -06:00
Andrei Zhemaituk 150328cc01 Re-using get_path for correct placement of the file(s) when XDG_CACHE_HOME is set 2026-07-28 00:27:33 -06:00
Cloudymap1e a8ad6285d6 fix(linux): prepare required runtime directory 2026-07-28 00:26:37 -06:00
Cloudymap1e 9e74311572 fix(python): preserve humanize duration types 2026-07-28 00:26:37 -06:00
Cloudymap1e 3efa5e2182 fix(python): isolate per-launch environments 2026-07-28 00:26:36 -06:00
daijro 479453d4e1 Bump pythonlib to 0.5.4 2026-07-16 17:01:21 -05:00
Jake Writer 0e4151f820 fix: page-recycle hang under spoofed window dims, and the unmerged halves of #637-#647
Fixes the new_page() hang from #666, and restores the pythonlib/ + settings/
halves of #637-#647 that were dropped when those PRs were consolidated into #666
(that PR only carried patches/ + additions/, so these never actually landed).

## new_page() hangs when window.outer* is spoofed (#666)

The outer-size hijack in browser-init.patch pinned the chrome documentElement to
the spoofed size. That caps .browserStack, which caps the content viewport, so
the content window can never reach the size Juggler asks for in
updateViewportSize() -- and awaitViewportDimensions awaits exact equality with
no timeout, so it deadlocks rather than erroring. The second new_page() hung
forever and took the context with it.

The pin was never load-bearing: GetOuterWidth/GetOuterHeight already consult
MaskConfig unconditionally (fingerprint-injection.patch), so window.outerWidth is
spoofed in C++ regardless of the real chrome window size. Resizing is enough.

Measured on the official v152.0.4-beta.26 build (headless):

    config      before        after
    none        pass          pass
    inner       pass          pass
    outer       HANG          pass
    both        HANG          pass  (iw:360 ih:740 ow:360 oh:800 -- exact)

This corrects the diagnosis in #666, which blamed the inner+outer combination and
the `!(outerWidth || outerHeight)` guard. outer* ALONE is sufficient to hang, and
dropping inner* does not help, so that guard is not the culprit.

Also fixed driver-side: Playwright's implicit 1280x720 viewport is what asks for
the impossible size, so the driver now defaults to no_viewport when the config
spoofs any window dimension. That fixes the hang on already-released builds
without a rebuild. An explicit viewport=/no_viewport= from the caller wins.

## WebRTC ICE prefs (#538)

#666 merged the C++ half of the WebRTC fix but not the prefs, so the shipped
build still has no_host=true and none of the proxy_only prefs.
proxy_only_if_behind_proxy is the pref that actually stops the real-IP leak: it
prevents a UDP STUN request routing around a TCP proxy. no_host=false keeps the
stock two-candidate shape, which obfuscate_host_addresses makes leak-free.

## Also restored from the consolidation

- fix(proxy): dom.security.https_first rewrote http:// before the launch-arg
  proxy filter saw it, breaking CONNECT-only proxies (#638).
- fix(stealth): speech-voice spoofing + stop leaking host voices (#646).
- fix(stealth): clamp inner <= outer <= avail <= screen; BrowserForge can emit
  impossible geometries that leak as tells (#647).

Refs: https://github.com/daijro/camoufox/pull/666
Refs: https://github.com/daijro/camoufox/issues/538
2026-07-16 14:38:39 -06:00
daijro 6871b114f3 Merge pull request #670 from pratyush618/fix/656-playwright-160-server-launch
Fix server launch on Playwright 1.60
2026-07-15 13:38:45 -05:00
daijro 9aebec17e5 Fixes for version pinning in GUI
- Replace icons with pins rather than checkboxes
- Add a tooltip that says "Pin version"
- Fix "Active" status bubble UX issues
2026-07-15 09:41:41 -05:00
Pratyush Sharma ab20eca72d Add regression tests for camoufox server
Cover both failure modes from #656 and pin the driver entrypoint
contract, so a future Playwright reshuffle fails in CI rather than in a
user's terminal. No browser download or launch, so they run anywhere.

Refs #656
2026-07-15 18:32:05 +05:30
Pratyush Sharma 84fadb7481 Report server exit code instead of pipe error
When the node server exits early, writing its config to the dead stdin
raised BrokenPipeError (EINVAL on Windows), burying the real cause.
communicate() ignores both, so the underlying failure stays visible.

Refs #656
2026-07-15 18:31:52 +05:30
Pratyush Sharma 5bf8081aec Fix server launch on Playwright 1.60
Playwright 1.60 bundled its internals and removed the private
lib/browserServerImpl.js that launchServer.js required, so
`python -m camoufox server` died with MODULE_NOT_FOUND. Load the
driver's package entrypoint instead, which is a bundled playwright-core
and exposes launchServer as public API.

The driver path is now passed explicitly rather than inferred from
process.cwd().

Fixes #656
2026-07-15 18:31:36 +05:30
daijro 022b7f1f8e Bump pythonlib to 0.5.3 2026-07-15 07:59:40 -05:00
daijro a9118939e2 Merge pull request #652 from Praveensenpai/fix/xvfb-zombie-cleanup
fix: kill Xvfb with SIGKILL and clean up X11 lock/socket files
2026-07-11 09:59:37 -05:00
daijro 195298d004 Temporarily cap Playwright dependency #653
Restrict playwright version to less than 1.61
2026-07-03 09:01:34 -05:00
Hunter Boyd 0d5440117b Fix playwright session leak when launch or close raises (#82)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 11:00:02 -04:00
daijro 3911d11dbd Support for releases with the same version and build v0.5.2
Fix for JWriter20 repo having several releases with the same version and build name, which broke the manager
- Installs browsers as version-build-sha256/ rather than version-build/ since assets can share a version-build combo
- Doesn't break existing downloads
- Show date column in gui and selector tui
2026-06-23 07:24:40 -05:00
daijro e723fb29ae Add JWriter20/camoufox as a binary source 2026-06-23 04:37:40 -05:00
Praveen Senpai 71fe02899f fix: kill Xvfb with SIGKILL and clean up X11 lock/socket files
Replace the terminate→wait→kill fallback in VirtualDisplay.kill() with a
direct SIGKILL to prevent zombie Xvfb processes. After the process exits,
remove the stale /tmp/.X{n}-lock and /tmp/.X11-unix/X{n} files so future
display allocations are not blocked. Also set self.proc = None to mark the
display as fully cleaned up.
2026-06-22 20:55:48 +05:30
daijro fdaffc03e9 Bump pythonlib to 0.5.1 2026-06-08 07:18:05 -05:00
daijro e8b86fa5c6 fontconfig path compatibility with older releases
Merged from cloverlabs repo
2026-06-08 07:16:54 -05:00
daijro 53e10d3d3b Make alpha versions available through the Camoufox manager
Adds "alpha" releases to the official/prerelease channel, even if the asset isnt marked as a prerelease
2026-06-08 05:19:06 -05:00
daijro 8899b42784 Merge pull request #618 from Young-Lord/wait-xvfb
fix(virtdisplay): wait() Xvfb child process to prevent zombie process
2026-06-06 15:25:38 -05:00
oyaah fafd35a775 Fix fontconfig path typo to fix FileNotFoundError on Linux 2026-06-07 01:49:50 +05:30
LY ce5edf6f5c fix(virtdisplay): wait() Xvfb child process to prevent zombie process 2026-05-20 09:19:24 +08:00
icepaq 0ac611c4ad v150 with Windows Support - Python Package Being Merged Separately (#611)
* fix v150 patches

* screen related patch fixes

* fix juggler issues with 150

* Update grading.py

improved build tester scoring

* fix windows build for v150

- scripts/_mixin.py: switch moz_target from x86_64-pc-mingw32 (no longer
  supported in FF150) to x86_64-pc-windows-msvc
- additions/juggler/screencast/HeadlessWindowCapturer.h: typedef pid_t
  on XP_WIN; libwebrtc headers (video_capture.h, desktop_capturer.h)
  reference pid_t which is POSIX-only
- patches/anti-font-fingerprinting.patch: include mozilla/dom/Document.h
  in gfxTextRun.cpp; on Windows it is not transitively included so
  doc->GetInnerWindow() failed with "incomplete type 'Document'"

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* make service test use local binary

* updated ff fingerprint versions

* Update README.md

---------

Co-authored-by: Ubuntu <ubuntu@ip-172-31-15-96.us-east-2.compute.internal>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:34:05 -04:00
Jake Writer 8f9ff07b48 fix(virtdisplay): atomically claim X11 display via Xvfb -displayfd (#597)
Replace the userspace lock-file scan + random-jitter retry loop with
Xvfb's own -displayfd mechanism. Xvfb scans up from :0 and atomically
binds the first free X11 socket (kernel-mediated, no userspace race),
then writes the chosen display number back through an inherited pipe.

This eliminates the duplicate-display race that occurred when many
camoufox processes started concurrently and all observed the same set
of free display numbers before any of them bound.

Adds a 10s read timeout on the displayfd pipe so a hung Xvfb fails
fast instead of blocking forever, and adds tests covering single
launch, idempotent get(), 50 concurrent reservations with uniqueness,
and post-kill display reuse.
2026-05-08 19:12:09 -04:00
Ruben Vereecken 0830d36e47 Add config_overrides to generate_context_fingerprint(), fix font spacing disable (#601)
* Add system-ui font spoofing patch

GetSystemUIFontFamilies() now reads navigator.platform from MaskConfig
and returns the appropriate system font (Helvetica for macOS, Segoe UI
for Windows) before falling through to LookAndFeel::GetFont. Fixes the
CreepJS headless.systemFonts "Sans:Linux" leak when spoofing macOS from
Linux.

* Add patch verification test for system-ui font spoofing

Tests macOS and Windows presets: launches Camoufox with a fingerprint,
measures canvas text width with unquoted system-ui, asserts it matches
the expected system font (Helvetica / Segoe UI).

* Rename test to match patch naming convention

tests/patches/ files should mirror patch names, not use dated prefixes.

* Add config_overrides param to generate_context_fingerprint()

There was no clean way to override config values (like disabling font
spacing perturbation with fonts:spacing_seed=0) because init_script is
rendered inside generate_context_fingerprint() — by the time the caller
gets the config dict back, the init_script string is already baked.

config_overrides is applied after all config building (preset/seeds/
timezone/locale) but before init_script rendering, giving callers a
clean override point without touching the preset (which represents
real device data, not perturbation config).

The longer-term fix is separating config building from init_script
rendering so callers can modify config and re-render. config_overrides
is the minimal API addition that unblocks the use case without that
refactor.

* Rename test to match patch naming convention
2026-05-08 19:04:14 -04:00
LY 65f3454f48 Fix virtual display under Wayland by forcing X11 (#576)
When using Xvfb-backed virtual display, override Wayland env vars
(GDK_BACKEND, WAYLAND_DISPLAY, MOZ_ENABLE_WAYLAND) so Firefox/GTK
honors DISPLAY and reliably uses the X11 virtual screen.

closes #575
2026-04-29 15:51:44 -04:00
Ruben Vereecken 0cd6c782d0 Set FONTCONFIG_FILE to isolate bundled fonts on Linux (#561)
Without FONTCONFIG_FILE, fontconfig loads the system's /etc/fonts/fonts.conf
which includes system font directories. This means system fonts leak into the
rendering pipeline for fallback glyphs (emoji, CJK, etc.), even though the
font enumeration whitelist hides them from JavaScript. The result is
environment-dependent font metrics that differ between machines with different
system font packages installed.

Generate a runtime fontconfig at ~/.cache/camoufox/fontconfig/ that resolves
the bundled font directory absolutely (the bundled fonts.conf uses
prefix="cwd" relative paths which break under Playwright), and point
FONTCONFIG_FILE at it.
2026-04-14 20:21:42 -04:00
Ruben Vereecken 40fbce61e4 Remove init_script system timezone fallback that poisons storage (#560)
The else branch in _build_init_script() reads the system timezone via
Intl.DateTimeFormat().resolvedOptions().timeZone and stores it via
setTimezone(). When geoip resolves a different timezone (set later via
CAMOU_CONFIG/launch_options), the storage value from the init_script
takes precedence — workers read the wrong timezone and the C++ MaskConfig
fallback (PR #546) is never reached.

Fix: only call setTimezone() when an explicit timezone value is provided.
When omitted, timezone propagation is handled entirely by the C++ side
(SetNewDocument + TimezoneManager::GetTimezone MaskConfig fallback).

Companion to #546 (Fix worker timezone leak when using geoip/proxy).

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:24:24 -04:00
Ruben Vereecken 2d62ef0a6d Add timezone and locale parameters to generate_context_fingerprint() (#563)
Allow callers to inject pre-resolved timezone/locale into the
fingerprint before init_script generation. When provided, these
take priority over preset data.

In launch_options(), use setdefault for geoip timezone/locale keys
so that values pre-set via generate_context_fingerprint() aren't
overwritten by geoip resolution.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:22:43 -04:00
icepaq d6540b52ce Service Test and Contributing Guides (#521)
* example files

* contributing guides

* simple service test

* run tests in sync

* update pr template

* pip updates

* Update README.md

* typo fixes

* undo pip package update lol

* upgraded service test

* undo injections

* test with proxies

* auto set timezone and proxy url

* delete checks bundle

* split up service tests

* split up build tests

* rename service tests to service tester

* Update CONTRIBUTING.md

* fix entry vs exit ip

* allow alpha versions

* fix patch issues on macos

* bidirectional patch

* Add note on experimental pip package
2026-03-15 21:31:49 -04:00