Skip to content

feat(browser)!: stealth Chromium 154 with coherent personas - #124

Merged
tenequm merged 168 commits into
mainfrom
feat/chromium-154-rebase
Sep 30, 2026
Merged

tenequm merged 168 commits into
mainfrom
feat/chromium-154-rebase

Conversation

@tenequm

@tenequm tenequm commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Stealth Chromium moves from 151.0.7922.137 to 154.0.8037.57 (ungoogled-chromium 154.0.8037.57-1), and the same build carries the fingerprint-coherence work from the improvements plan plus two rounds of fixes from the final gates and review. Nothing ships as a pure rebase: both targets are built from one commit, 69789c0, published as browser-v154.0.8037.57-1, and one pin commit here moves versions.env, the Dockerfile, chromiumVersion, the golden and posture.json to it.

Addresses #24: the binary now sends real Chrome's Sec-CH-UA headers itself, so cuttle no longer passes --disable-features. Also addresses #45, #50 and #80, and part of #53.

Chromium 151 -> 154 rebase

  • 0037 rebased: upstream now enables kIncognitoStaticStorageQuota itself.
  • 0010 and 0011 regenerated against the 154 tree with real context.
  • 0006 hardcoded Chrome/151.0.0.0 in navigator.userAgent; the major now comes from the validated --fingerprint-brand-version, so page, worker, header and brands agree on 154.
  • Removed 0002 and 0045, and 0007's headless_browser_impl hunk: they patched headless/lib, which the chrome target never compiles.
  • Removed 0036 (storage-estimate override): the daemon never passed its switch, stock 154 already reports a static quota, and the override itself was a tell (synchronous, no usageDetails, before the opaque-origin TypeError).
  • Removed the unread fingerprint-location, audio-sample-rate and fingerprint-fonts-dir switches.

The series goes from 27 to 37 patches: 0055-0067 are new; 0002, 0036 and 0045 are gone.

Stealth improvements, by what a page now sees

  • Canvas (0055, Canvas readback noise is re-rolled on every call and every reload instead of keyed to the seed #80): readback noise is keyed on the seed and the pixels read; toDataURL, toBlob and convertToBlob agree, flat or cleared canvases read back exact, and --fingerprint-noise=false turns it off. macOS readbacks unpremultiply on the CPU as real Mac Chrome does, so the first read no longer rounds apart from later ones. measureText noise is off (it moved widths off Chrome's fixed-point grid) and the client-rects noise switch is dropped.
  • WebGL and WebGPU (0016, 0049, 0058-0060): caps, shader precision and the extension list come from the persona GPU's table (real Apple M1 Max, and D3D11 rows from fingerprint-chromium, BSD-3); WEBGL_debug_shaders answers known detector shaders in MSL or HLSL. readPixels noise now covers only the RGBA bytes validation accepted, is keyed on seed and framebuffer coordinates, and never touches alpha. A software WebGPU adapter resolves null; adapter info keeps device and description empty as stock does, with Dawn architecture names; the Intel-Mac GPU is out of the macOS pools.
  • WebRTC (0057): with --fingerprint-webrtc-ip, one .local host candidate and one srflx at the seed's exit IP; no UDP leaves the real interface. TURN over TCP or TLS connects through the proxy; every WebRTC DNS lookup fails in forced mode, so a TURN or candidate hostname never reaches the system resolver. A proxied seed without a forced IP gets the same through the new opt-in --fingerprint-webrtc-no-local-dns, which cuttle passes next to the disable_non_proxied_udp policy; stock Chrome and 151 resolved a page's TURN hostname through the system resolver there. Direct seeds get their egress IP, cached 10 minutes (failures 1 minute).
  • CDP (0056, 0066, Runtime.enable console-preview leak is detectable through any attached driver #50): console and exception previews no longer run page getters under a driver's Runtime.enable (error stack, RegExp.prototype.flags, NodeList.length, console.table columns), and each realm's own Error is checked before a stack is formatted. 0066 keeps Runtime.enable from raising the stack capture depth to 200 frames, which are_you_a_bot's hasInconsistentTimingResolution timed.
  • Audio (0026, 0027, 0061): the offline-render gain is seeded and purely multiplicative; AudioContext reports 48 kHz with the persona's device buffer, and Windows playback rounds to 960 frames as real Windows does. Analyser noise off now means off, and byte data derives from the noised floats.
  • Display, colours, window (0011, 0013, 0054, 0062): CSS system colours match Chrome on Windows and macOS; macOS reports colorDepth 30, color-gamut: p3, dynamic-range: high and availTop/screenY at the menu bar. outerWidth/outerHeight are the real window's again, so popups and resized windows keep a real frame. Screen, device-memory and core-count switches are bounded to values Chrome can report.
  • Identity coherence (000-shared, 0006, 0007, 0051, 0052, 0064, 0065): an empty --fingerprint seed is drawn once in the browser and passed to every renderer, so OOPIFs and workers match their page; --fingerprint-brand-version is validated once for every UA-CH surface; hardwareConcurrency shares one range between 0006 and 0065; navigator.cpuPerformance and jsHeapSizeLimit follow the persona, not the host; navigator.connection rounds rtt and downlink like stock Chrome; macOS UA-CH platformVersion defaults to a real release instead of 10.15.7; persona voice names decode as UTF-8.
  • Permissions (0010, 0048): prompt normalisation applies only in secure, same-origin contexts, so insecure pages and cross-origin iframes read denied as stock Chrome does, and the notification getter agrees with permissions.query.
  • Network, Referer, client hints (0019, 0040, clark strips Sec-CH-UA request headers, contradicting the spoofed Windows Chrome persona #24): stock strict-origin-when-cross-origin referrers and Sec-CH-UA ship in the binary; stock profile prefs ungoogled re-defaults are written only when absent.
  • Fonts (0063, image font packs): macOS system-ui and BlinkMacSystemFont resolve to Inter re-widthed to SF Pro Text, hidden by name; Windows system-ui resolves to a Selawik-based "Segoe UI" with real Segoe UI advances and kerning. Both packs add stock families with real metrics and kerning (macOS: Avenir, Avenir Next, Futura, Verdana, Tahoma, Georgia, Trebuchet MS; Windows: Verdana, Tahoma, Trebuchet MS, Georgia, Consolas, Lucida Console, Impact, Comic Sans MS, Segoe UI Symbol), and macOS monospace is Menlo.
  • Persona fonts (0067 and the macOS pack): generic families default to the persona platform's fonts, macOS Times/Helvetica/Courier get Mac Chrome's ascent, quoted generic names are no longer used as families, and the macOS pack adds real-metric Bold faces, Times, Courier and the faces fingerprinting scripts probe for. Every face on both personas answers local() by its real full and PostScript name (Arial Bold, Arial-BoldMT, Segoe UI Bold).
  • Machine pools: the macOS persona picks chip, cores and screen as one MacBook Air model (1440x900, 1470x956, 1710x1107); the Windows pool is integrated GPUs only (Intel 9A49, 46A6, A7A0, 9B41, 3EA0 and AMD 1638), 16 GB or more, without 1536x864. An existing seed can therefore report a different GPU, core count or screen, and a pinned screen that is no longer offered is refused.
  • Launch and image: X runs with -noreset so the persona keymap sticks (all 48 getLayoutMap() keys match); --test-type on every launch removes the --no-sandbox infobar; the system window frame on both personas reads chromeWidth 0 / chromeHeight 87 like real maximized Chrome; --msaa_is_slow on Windows reproduces its canvas antialiasing; VNC mode renders on the same GL path as plain mode and maximizes the session window. Geo-derived locales map only to English variants Chrome offers.
  • cli: host verbs dial the address a container actually publishes CDP and the viewer on, not always loopback.

Build pipeline

  • git apply with applied records: stage 4 keeps each applied patch in .browser-applied/ and reverses only changed or removed patches, so a warm prep rewrites only what it must and out/ stays warm. regen-patch.sh proves a patch round-trips before replacing it.
  • patch-lint: git apply lands a zero-context insertion at end of file and reports success, so stage 4, just patch-lint (part of just check) and CI reject @@ -N,0 hunks.
  • BROWSER_STAGE=prep|build: prep does every write to the shared tree once and refuses to run while a build is running; both targets then build concurrently. Non-git CIPD/GCS deps are fetched from DEPS and stamped only after a complete install, and ninja runs -k 0 with one retry for upstream ordering races.
  • Deps image: keyed on install-build-deps.sh and baked once per version.
  • Artifacts: written to a temp name, and the .sha256 file lists the tarball by basename so sha256sum -c works on a downloaded asset.

Validation

Gates run on the exact published artifacts, each persona on its own arch, inside a runtime image with the new binary and font pack.

Gate Result
Smoke (validate/smoke.py), per persona macOS 77/77, Windows 81/81
Parity vs browser-v151.0.7922.137-1 (validate/parity.py) 0 unexplained besides the intended navigator.connection rounding (151 reported raw rtt/downlink, 154 rounds like stock)
detect.py, macOS persona 0 failed
detect.py, Windows persona 0 failed
CreepJS (headless / stealth / likeHeadless / lies) macOS 0/3, 0/5, rating 25 (real 25), no lies; Windows 0/3, 0/5, rating 19 (real 19), no lies
are_you_a_bot (5 runs, driver attached) isBot false; 0/5 flagged with Runtime.enable on, both personas
botstop HUMAN, score 0, both personas
Daemon (cuttle serve): session, pool, VNC pool mode, default and VNC: 0 assertions failed, both personas; session mode, default and VNC: are_you_a_bot 0/5 flagged, canvas readback, window geometry, keymap and notification checks pass, both personas
Go image smoke (go -C packages/cuttle run ./test/smoke) 8/8, both personas
CDP getter probe (validate/cdp-getter-probe.sh) pass, both personas

Every posture move has to head toward the real Chrome 154 values; the numbers land in packages/browser/benches/posture.json with the pin commit.

Known limits

  • Selawik covers 348 codepoints, so Cyrillic in "Segoe UI" falls back and measures about 6% narrow.
  • Segoe UI italics are Carlito re-widthed and keep its liga/calt/dlig; Light, Semibold and Semilight are not shipped.
  • Canvas noise is keyed on the requested rect: a crop, 1x1 or float16 read disagrees with the full read.
  • WebGL caps and shader dialect are tables, not a backend; validation and execution follow ANGLE on llvmpipe.
  • macOS WebGL1/2 extensions 35/31 (real 39/36); an unmapped shader translates to GLSL.
  • Some probed fonts are missing (Windows: Franklin Gothic, MS UI Gothic, Marlett, Segoe UI Light, Cambria Math and several newer system fonts; macOS: PingFang HK Light); Apple Chancery is about 1% wide.
  • WebGL2 PIXEL_PACK readPixels is not noised, and WebGL readPixels noise skips a read's last row and column, so a crop disagrees with the full read.
  • WebGPU returns no adapter, and its limits and features come from the host adapter.
  • Proxied seeds carry a STUN tell: a page's STUN server sees no binding request, yet a srflx appears.
  • Two peer connections in one page cannot connect in forced-IP mode (.local names are never registered).
  • 0056 treats any builtin accessor as inert without proving it cannot re-enter page script.
  • Persona devicePixelRatio ignores browser zoom.
  • speechSynthesis.speak() with a persona voice fails with synthesis-failed; BarcodeDetector.detect() returns [].
  • Binary and daemon ship together: without --disable-features, cuttle on an older self-built binary (CUTTLE_BROWSER_BINARY) would lose the referrer and client-hint fix.

Follow-ups

  • Rebuild-only fixes from the final review, canvas-noise overflow first: Rebuild-only fixes from the 154 review (canvas noise overflow first) #126.
  • Delete the unused Hetzner volume scripts (packages/browser/hetzner/ provision.sh, teardown.sh, cloud-init.yaml) in a separate PR; the README documents the snapshot flow.
  • Measure on real hardware: Windows iGPU caps, Windows dark-scheme Highlight, MacBook Air menu-bar height and HDR, the dabi flags on both personas, and the TLS client hello against real 154.

Release notes

Stealth Chromium moves to 154 with more coherent personas: seed-stable canvas, per-GPU WebGL, WebRTC at the exit IP, persona fonts, colours and window geometry, and stock client hints. Existing seeds may report a different GPU or screen; a pinned screen no longer offered is refused.

…the build

Rebase 0037 (upstream now enables kIncognitoStaticStorageQuota) and regenerate
0010/0011 with real context.

build-linux.sh: split into BROWSER_STAGE=prep|build so both targets build
concurrently from one tree; fetch every non_git_source CIPD/GCS dep from DEPS
and its recursedeps generically; fail fast on missing source inputs; ninja -k 0
with one retry for upstream ordering races; drop the gclient --jobs=2 throttle;
match depot_tools' reformatted GSUTIL_DEFAULT_PATH. run-build.sh: bake
install-build-deps into a per-version image.
…e persona's audio device

Rewrite 0026 to scale OfflineAudioContext output once, at the render-thread copy,
instead of offsetting every AudioBuffer read: silence stays zero, both read paths
agree and script-built buffers are untouched. New 0061 reports 48 kHz with the
persona's measured device buffer (Windows 480, macOS 256 frames) when a persona
is active. Smoke gains an audio section covering both.
Chrome builds navigator.keyboard.getLayoutMap() from the server's XKB
keymap. Set it explicitly after the X-ready poll in both the Xvfb and the
Xvnc branch, so the layout is a deliberate choice lane M can tune.
… IDs

Drop the RTX 3060 and RX 7600 rows and move every Intel and AMD row to a
device ID with captured WebGL capability tables (9A49, 46A6, A7A0, 9B41,
3EA0, AMD 1638), re-pairing thread counts with parts that carry each die.
No row claims less than 16 GB, which the host-derived jsHeapSizeLimit
would contradict.
Write payments.can_make_payment_enabled, credentials_enable_service,
credentials_enable_autosignin, autofill.credit_card_enabled (true) and
bookmark_bar.show_on_all_tabs (false) when absent, on fresh and existing
profiles. A value already set in the browser is left alone.
CreepJS catches --fingerprinting-client-rects-noise with a known-geometry
check however it is seeded (Element.getClientRects "unknown rotate
dimensions"). Rects still differ per seed through each seed's own screen
and window size, and getClientRects and getBBox now agree.
With the referrer hunks gone from 0040 and the kRemoveClientHints flip gone
from 0019, the binary ships real Chrome's referrer and client-hint defaults
itself. The flag was also appended after the caller's args, so it silently
overrode any --disable-features a caller passed. Ships with the binary pin.
Drop the MinimalReferrers/NoCrossOriginReferrers flips from 0040 (keeping
SetIpv6ProbeFalse) and the RemoveClientHints flip from 0019, so the binary
matches stock Chrome without --disable-features. Smoke asserts the Referer
and Sec-CH-UA headers on the wire from a two-origin local server.
…ample-rate switches

Nothing in the series, the 154 tree or the Go daemon reads either switch, so
drop them from the shared header and from 0050's renderer forwarding list.
0050 is regenerated against 154 and loses two comment-only hunks that
rewrote upstream URLs to their domain-substituted form.
Both patched headless/lib/{renderer,browser}, which only the headless_shell
executable links; the chrome target never compiles them. The README records
the drop, the new patch count and the current Widevine state.
Bromite's canvas readback noise and measureText factor were re-rolled per call and per Document, so an unchanged canvas read back differently every time (#80). Patch 0055 keys the image-data noise on the seed plus the pixels read, skips transparent and flat pixels, noises a private copy on both the one-shot and streaming encoders so toDataURL, toBlob and convertToBlob agree, takes the measureText factor from the seed and scales only the glyph-dependent metrics. smoke.py gains the (a)-(e) canvas assertions.
…hanged patches from records

Stage 4 records each applied patch in .browser-applied/<name>.<hash>.patch and
reverses a changed or removed patch from that copy, so a warm prep rewrites only
the files it must and out/ stays warm. A one-time BROWSER_APPLIED_SEED converts
the legacy .done markers, checked against their hashes. Zero-context insertions,
which git apply lands at end of file, are rejected by stage 4 and by the new
patch-lint recipe (just check and CI). regen-patch.sh regenerates a patch and
proves the round trip plus the identifier diff. 000-shared is copied only on a
content change. Also corrects the args.gn Widevine comment: nothing fetches a CDM.
…gamut/availTop, heap limit

0062 maps the Windows persona's CSS system colours to Chrome on Windows' light-mode values (ActiveText no longer the stock red that fires CreepJS hasKnownBgColor). 0011 reports colorDepth 30 and the menu bar as availTop on the macOS persona; 0054 adds color-gamut p3, dynamic-range high and (color: 10) there. 0064 pins jsHeapSizeLimit to V8 154's 8 GiB+ value when a persona is active. smoke.py gains a display-persona section.
probes.py holds every probe from the 154 improvements plan's validation
matrix as one dict entry of JS, served from a two-origin 127.0.0.1 page, and
both scripts emit it as metrics["probes"]. detect.py's worker check now reads
the shared worker probe, which adds languages, deviceMemory, userAgentData and
timezone to the comparison. realref.py pins the window to the primary display.
…d version

Patch 0006 hardcoded Chrome/151.0.0.0, so on 154 navigator.userAgent (page and
workers) contradicted the HTTP header, userAgentData.brands and Sec-CH-UA. The
major now comes from --fingerprint-brand-version, falling back to the version
the browser fills into the UA metadata from version_info. smoke.py asserts the
page, a worker, the header and the brands agree on one major.
Chrome 154 ships navigator.cpuPerformance, a tier computed from the host's core
count and CPU model, so the container leaked the build host's CPU beside a
persona describing another machine. New patch 0065 tiers the persona instead:
its hardwareConcurrency, plus the Apple chip its Metal WebGL renderer names,
through upstream's own GetTierFromCpuInfo.
… driver (#50)

New patch 0056 in v8/src/inspector: getErrorProperty walks the chain by
descriptor and never runs page getters, error stacks are formatted only when
formatting is inert, the property walk skips RegExp.prototype.flags, and the
NodeList length and console.table column reads go through descriptors.
The default us/pc105 keymap already matches real Chrome's getLayoutMap in
47 of 48 entries on both personas. The outlier is IntlBackslash (keycode 94,
<LSGT>), which X maps to '<'. Override that key alone via xkbcomp: the
section sign on the arm64 macOS persona, backslash on the amd64 Windows one.
…drivers run

smoke.py and detect.py gain a driver-shaped section: the issue #50 probe and
its siblings with Runtime off, then on for the page and an auto-attached
worker; the counts must not move. cdp-getter-probe.sh runs the same probe
through the bundled playwright-cli for release step 9.
Ungoogled registers enable_a_ping false, so <a ping> is never sent. Write
Chrome's stock true when absent, like the other stock prefs.
…, Windows system fonts

0013 pins window.screenX/screenY to the maximized work-area origin (availTop is the macOS menu bar) and shifts mouse/pointer event screen coordinates by the same offset. MenuBarHeight() moves into cuttle_seed so 0011 and 0013 share it; the notchless value is 30 (macOS 26, measured). 0062 adds the macOS Highlight/HighlightText/SelectedItem values, the unblended Windows dark Highlight, and the Windows menu/small-caption/status-bar fonts. smoke.py checks all of it.
…WebGPU adapters

Patch 0058 adds a header-only capability table keyed on --fingerprint-gpu-renderer (D3D11 rows ported from fingerprint-chromium, BSD-3; Metal rows measured on a real Apple M1 Max under Chrome 154). Patch 0016 now serves numeric getParameter caps, getShaderPrecisionFormat and a hide-only extension filter from it, and drops the WebGL1-only allowlist that also hid WebGL2 extensions. Patch 0060 resolves requestAdapter to null for a CPU adapter under a persona and reports bgra8unorm as the preferred canvas format.
…lect

Patch 0059 returns real Chrome 154's MSL or HLSL translation for the shaders detectors are known to compile (deviceandbrowserinfo's hasInconsistentWebGLShaderLang vertex shader and our posture probe pair), so a Metal or D3D11 renderer string no longer sits over SwiftShader's SPIR-V notes. MSL is captured on a real Apple M1 Max; HLSL is derived from the 154 ANGLE translator and matches a real Windows capture's head and length. Other shaders keep the backend translation.
@tenequm
tenequm marked this pull request as ready for review September 29, 2026 21:43
…ed IP

A proxied seed with no forced WebRTC IP runs under disable_non_proxied_udp,
which still resolves a page's TURN hostname through the system resolver,
outside the proxy. The new --fingerprint-webrtc-no-local-dns switch (forwarded
to renderers by 0050) makes 0057's lookup failure apply without a forced IP,
and cuttle passes it alongside the policy flags. Without the switch the
binary behaves as before.
…trics

Windows sizes a font without USE_TYPO_METRICS from OS/2 winAscent/winDescent,
while the Linux build reads hhea. The stamped Consolas faces kept Consolas's
hhea (1521/-527/350), so monospace fontBoundingBoxAscent read 36 at 48px and
7 at 10px against real Windows Chrome's 44 and 9. The Windows metrics
extractor now writes the win metrics into hhea (line height kept through the
gap), and the table carries 1884/-514/0 for both Consolas faces. Every other
stamped Windows face already had hhea equal to its win metrics.
@tenequm tenequm changed the title feat(browser): stealth Chromium 154 with coherent personas feat(browser)!: stealth Chromium 154 with coherent personas Sep 29, 2026
A direct seed with a pinned timezone ran the full geo lookup on the launch path, which may download GeoLite2 under the shared lock, and a lookup that found the IP but no timezone was cached for the full 10 minutes, leaving unpinned direct seeds on UTC. A pinned timezone now resolves only the exit IP, that IP-only entry never answers a seed that needs the timezone, and an IP without a timezone is retried after the short failure TTL.
An explicit --fingerprint-webrtc-ip=auto on a direct seed with a pinned timezone skipped the egress lookup and was then dropped, so the seed got real ICE where the same seed without a timezone got the forced egress IP. And a zoned IPv6 value (fe80::1%eth0) passed netip.ParseAddr, so a proxied seed skipped the fail-closed policy although the binary's IPFromString rejects it and forces nothing.
A connection that pins --fingerprint-screen-* skips ScreenArgs, but AppleSiliconArgs still drew the Mac against the operator screen, so a 1440x900 pin could land on an M2/M3 15" GPU, breaking the one-machine invariant. The Mac is now drawn among the models with the pinned screen.
…the refusals

BROWSER_STAGE=all also runs prep_tree, which reverses and re-applies patches under any running STAGE=build container, but only STAGE=prep was refused. The install-build-deps bake also ran before both refusals, so a rejected invocation still paid 1-2 minutes and read the script from under a running prep.
…o diff --git line

Patches 0026, 0055, 0057 and 0061 start their diffs at '--- a/', so the header cut kept the whole file and a regeneration put the old diff in front of the new one.
…ical

smoke.py and benches/probes.py each carry CDP_GETTER_PROBE, and neither can load the other: the build container mounts only validate/, and probes.py is copied alone to the reference box. Assert the two copies match, alongside the other smoke.py cross-file checks.
cuttle_webgl_caps.h, which carries the BSD-3 D3D11 rows, is created by 0016-webgl-vendor-renderer-from-cli; 0058 only extends it with the WebGL2 int64 caps. THIRD-PARTY.md and the browser README both named 0058.
Calibri, Segoe UI Italic/Bold Italic and Cambria are built only into the Windows (amd64) font pack, so listing them as shared overstated what the macOS image ships.
…ding

The series is 37 patches and 0036-storage-estimate-from-cli was dropped. Only image-data noise stays on, so the section no longer says to keep 'the flags'. The sizing paragraph described the retired 100 GB cache volume rather than the snapshot of the server's root disk.
The image no longer ships ungoogled-chromium 148 with clark's stealth5 series as-is, and Segoe UI Regular and Bold now come from Selawik, not Carlito.
…stScript names

The Windows metrics table was last regenerated before the extractor learned to
record each face's names, so rename-fonts.py gave every Windows stand-in the
bare family as its full name: local("Segoe UI Bold"), local("Tahoma-Bold") and
every other Bold/Italic face failed to load where real Windows Chrome finds
them. The table is regenerated from a Windows 11 install; the only change is
the added names for all 19 entries - advances, vertical metrics and kerning
are byte-identical, so the Consolas win-metrics sizing is kept.
@tenequm
tenequm merged commit 6eacdd7 into main Sep 30, 2026
9 checks passed
@tenequm
tenequm deleted the feat/chromium-154-rebase branch September 30, 2026 00:38
tenequm added a commit that referenced this pull request Sep 30, 2026
🤖 I have created a release *beep* *boop*
---


## [0.16.0](v0.15.1...v0.16.0)
(2026-09-30)


### ⚠ BREAKING CHANGES

* **browser:** stealth Chromium 154 with coherent personas
([#124](#124))

### Features

* **browser:** stealth Chromium 154 with coherent personas
([#124](#124))
([6eacdd7](6eacdd7))


### Bug Fixes

* **serve:** keep proxy credentials out of the ignored-params warning
([#125](#125))
([2cf0023](2cf0023))
* **skill:** ship the skill as a real file under skills/cuttle
([#122](#122))
([e04bd29](e04bd29))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant