browserrot: Node stealth sidecar + AI self-heal (M-B1..M-B3, LIVE-VM)

Extends the Phase A-tier-1 site-side rotation engine with an optional Node
browser-sidecar and a deterministic self-heal tier, all proven LIVE-VM against
the local lab change-password form. go-rod stays the default; the sidecar is a
swap-in Driver behind the same Session/Driver/Rotator interfaces — no spine
change, backup + verify-new-before-revoke-old + proof gates all apply unchanged.

M-B1 sidecar parity: internal/browserrot/sidecar/ (index.js JSON-RPC over stdio,
  patchright real-fingerprint Chromium, playwright-core fallback) + sidecar.go
  Driver/Session. A shared test harness runs go-rod and the sidecar through the
  identical live rotation proof.
M-B2 stealth: patchright removes navigator.webdriver; humanized input (Bezier
  mouse paths + per-key typing jitter, no ML trajectory generation per plan §5).
M-B3 self-heal: Healer interface + heal-retry fill/click/text wrappers; on a
  rotted selector, heal.js relocates the field from DOM structure ONLY (never a
  value) and the engine rewrites + persists the recipe (recipes.go RecipeBook)
  so the next run is Tier-1 again. Deterministic heuristic is the proven Tier-2;
  Stagehand LLM strategy is opt-in + UNPROVEN. Leak-check asserts no secret ever
  reaches a heal request (redacted IPC transcript).

Secret seam documented honestly: fill carries base64 secret bytes across exactly
one local stdio boundary, typed and wiped, never logged/persisted/sent to a
model. MFA/CAPTCHA still degrade to the human worklist (Hard Rule 5).

14 tests green, -race clean; lab/lab-provision-browserrot.sh proves both drivers.
M-B4 (first REAL self-owned site) deliberately NOT started — needs per-credential
authorization (safe-candidate ladder).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
leetcrypt
2026-07-20 08:51:28 -07:00
parent 8dbb2f21fd
commit c2f08d1a21
16 changed files with 1642 additions and 22 deletions
+26 -3
View File
@@ -322,6 +322,29 @@ adapter is pure-exec Go, so it is unit-validated cross-platform via an injected
wiring auto-enables it yet (still gated behind per-credential authorization). Still to do:
build out the curated selector table for real high-frequency no-MFA sites and measure the real
success rate before promising anything.
- **Phase A-tier-2:** Computer-Use / AI-in-browser vision fallback for DOM variance, with
**secrets kept out of the model loop** (the model decides navigation; the Go harness injects
the actual password via CDP, redacted from the screenshot stream). Opt-in, measured.
- **Phase A-tier-1.5 — Node stealth sidecar (built + LIVE-VM):** an optional swap-in
`Driver` (`internal/browserrot/sidecar.go` + `internal/browserrot/sidecar/`) that speaks
newline-delimited JSON-RPC over stdio to a small Node program driving a **patchright**
real-fingerprint Chromium. go-rod stays the default; the sidecar reaches **parity** through
the *identical* LIVE-VM harness (`runLiveRotationProof` runs both drivers —
`TestIntegration_sidecarBrowserRotation`). Stealth posture is **LIVE-VM**: patchright
removes `navigator.webdriver` (`TestIntegration_sidecarStealthSignals`), and humanized input
(cubic-Bézier mouse paths + randomized per-key typing, **no ML trajectory generation**) still
lands a real rotation (`TestIntegration_sidecarStealthRotation`). The secret seam is honest:
`fill` carries the vault bytes **base64** over ONE local stdio boundary, never logged, never
on disk, never to a model (`TestSidecar_secretSeamBase64` + a redacted IPC transcript).
- **Phase A-tier-2 — self-heal + recipe persistence (built + LIVE-VM for the heuristic
strategy):** on a Tier-1 selector miss the engine calls an optional `Healer` (a Session
capability) to relocate the field by **natural-language description**, retries, and rewrites
the recipe; `RecipeBook` (`recipes.go`) persists the healed `Site` so the next run is Tier-1
again ("learn once"). The `Heal` signature takes only a description — a secret **cannot**
reach the healer by construction (`TestHeal_engineRetriesRewritesAndPersists`,
`TestIntegration_sidecarSelfHeal` with a redacted-transcript leak-check). The sidecar's
**deterministic DOM-heuristic** heal (attributes/labels/aria only, never field values) is the
**LIVE-VM-proven** Tier-2; the **Stagehand `observe` (LLM) strategy** is real but **UNPROVEN**
and strictly opt-in (`INCREDIGO_HEAL_STRATEGY=stagehand`) — it needs Stagehand to own the
browser, still to be wired. A specific real-site recipe stays **UNPROVEN** until run against
that site; no production `rotate` wiring auto-enables it (per-credential authorization).
- **Phase A-tier-3:** Computer-Use / AI-in-browser vision fallback for hostile DOM variance,
with **secrets kept out of the model loop** (the model decides navigation; the Go harness
injects the actual password via CDP, redacted from the screenshot stream). Opt-in, measured.
+184
View File
@@ -0,0 +1,184 @@
# browserrot buildout plan — Node stealth sidecar + AI self-heal (Phase A tiers 1-3)
**Status (2026-07-19):** M-B1 ✅ M-B2 ✅ M-B3 ✅ (all LIVE-VM proven against the local lab
form, `internal/browserrot/` suite green + `-race` clean). **M-B4 (first REAL self-owned
site) deliberately NOT started** — it rotates a real credential and needs explicit
per-credential authorization (safe-candidate ladder). M-B5 (vision) later. The Stagehand
(LLM) heal strategy is wired but UNPROVEN/opt-in; the deterministic DOM-heuristic heal is
the LIVE-VM Tier-2. See `docs/BROWSER-ROTATION.md` §9 for the proof-as-data record.
**Extends, does not replace:** [`docs/BROWSER-ROTATION.md`](BROWSER-ROTATION.md) (the design
spec) and the existing engine in [`internal/browserrot`](../internal/browserrot).
**Goal (user's words):** reset passwords for the **most sites without getting blocked**,
"do it once → reproduces forever, until the site ups bot detection."
---
## 0. Decision: Option (b) — a Node browser-sidecar
Chosen over "stay pure-Go with `playwright-go`" because the whole ecosystem that maximizes
*site coverage* and *avoids false-positive bot blocks* is Node-centric and has no Go equivalent:
- **`patchright`** (drop-in Playwright, real-fingerprint Chromium) — beats the fingerprint
checks `playwright-go` cannot touch; more robust than JS-injected stealth plugins.
- **Stagehand** (`act`/`observe`/`extract` + **auto-caching self-heal**) — covers the long
tail of site layouts without hand-coding every selector; this IS the "record once, replay
forever, re-learn on break" paradigm.
- **Playwright codegen** — records a real human run into a starter recipe.
`playwright-go` would give a driver but none of the above. The runtime cost of (b) is one
Node dependency, isolated behind an interface and a local socket.
> **The architecture already anticipated this.** `internal/browserrot/browserrot.go`:
> *"go-rod is the default; playwright-go and a Tier-2 AI driver are swap-ins that satisfy
> the same interface."* The sidecar is just a new `Driver`. **No spine change, no new hard-rule
> surface** — `rotate.Snapshot` (backup), verify-new-before-revoke-old, and the proof gate all
> apply unchanged because a browser `Rotator` already implements `rotate.Rotator`.
---
## 1. Non-negotiable reframe (carry into every task)
incredigo is **not** a bot-bypass/scraping tool. Per CLAUDE.md Hard Rule 5:
- **Self-owned accounts only, with the user's authorization.**
- **Never bypass MFA / CAPTCHA / email-confirm** — when one appears, the success marker
simply doesn't match, `Rotate` fails cleanly, the OLD secret is kept, and the human is
handed the change-URL by the existing worklist. (Already how `Rotate` behaves,
browserrot.go:175-180.)
- "Stealth" here = **do not get false-flagged as a bot while doing a legitimate self-login**,
NOT defeating a challenge. For a user logging into their own account from their own machine,
most of the anti-bot arms race is already moot (real IP, real device, real user).
- **Hard Rule 6:** the self-heal LLM may see **page structure only** to locate fields; it
**never receives a secret**. incredigo still fills fields itself, over CDP, with vault bytes.
---
## 2. Target architecture — three tiers, graceful degrade (mirrors rotate → worklist)
```
Tier 1 deterministic replay of the recorded recipe (Site/Form selectors) ← no LLM, no secret to model
engine driver = go-rod (exists) OR node-sidecar (new)
│ selector miss / Text() or Fill() fails
Tier 2 AI self-heal: Stagehand observe/act re-locates the field by NL ← LLM sees DOM structure only
description → new selector → incredigo fills with vault bytes → (never the secret)
PERSIST the healed recipe so the next run is Tier-1 again
│ hostile/opaque markup, canvas, etc.
Tier 3 vision fallback (Anthropic Computer Use) — secrets injected locally ← later; keep out of default path
│ MFA / CAPTCHA / email-confirm at ANY tier
HUMAN worklist handoff (change-URL + guide TUI) — existing internal/worklist
```
The **recipe** = the existing `Site{Host, ChangeURL, Form, Login, Proof}`. Tier-2 healing
*rewrites the recipe's selectors* and re-persists it; that is the entire "learn once" loop.
---
## 3. The Node sidecar (new component)
- **Process:** a small Node program (`browserrot/sidecar/`), Playwright + `patchright` +
`@browserbasehq/stagehand`. Launches a real headed-ish Chromium with real fingerprint.
- **IPC:** newline-delimited **JSON-RPC over stdio** (Go spawns it as a child; simplest, no
port, dies with the parent). Methods map 1:1 to the Go `Session` interface:
`open`, `goto`, `fill`, `click`, `text`, `close`, plus Tier-2: `heal` (NL field description
→ selector) and (Tier-3 later) `act`.
- **Secret seam (document honestly, like the CDP seam in rod.go):** `fill` carries the secret
bytes over the local stdio pipe to the sidecar, which types them into the field. The secret
crosses ONE local process boundary, is **never logged, never written to disk, never sent to
an LLM**. Heal/act requests carry **only** a field description + sanitized DOM, never a value.
- **Go side:** `internal/browserrot/sidecar.go` = a new `Driver`+`Session` impl that speaks the
JSON-RPC. Selected via a driver flag/env; go-rod stays the default until the sidecar reaches
parity.
- **Lifecycle:** Go owns spawn/teardown; `Session.Close()``close` RPC → kill child. Timeouts
and a hard context deadline on every RPC.
---
## 4. Recording a recipe once ("manual once")
- `incredigo browser record --host <site>` → drives Playwright **codegen** in the sidecar; the
human performs the change-password + re-login flow once; incredigo captures the selectors into
a `Site` recipe (Form + Login), **redacting any typed secret** (store selectors, never values).
- Persist recipes as a curated table in-repo for public sites (no secrets) and/or a local
user file for private ones. This is the browser analog of the rotate driver's ref-map.
---
## 5. Stealth posture (deliberately minimal)
- Use **`patchright`'s real-fingerprint Chromium**; avoid JS-injected stealth plugins (more
fragile, more detectable).
- **Human-paced input:** simple Bézier mouse paths + randomized typing delays. Research shows
GAN/diffusion mouse-trajectory models are often *more* detectable than plain Bézier+jitter
(DMTG discriminators ~80-90%) — so **do NOT build ML trajectory generation**; it is cost with
negative return for a self-owned login.
- No proxies, no residential-IP rotation — the user's own IP is correct and legitimate.
---
## 6. Proof ladder (DATA, mirrors rotate/proofs.go & BROWSER-ROTATION.md §8)
Per-`Site` `Proof`:
- Local lab HTTP form → **LIVE-VM** (real browser + real form proved the mechanism) — exists.
- Each **real self-owned site** stays **UNPROVEN** until actually run against it, then promotes
to **LIVE-REAL** for that host (same discipline as the gitea rung). No blanket "works on all
sites" claim ever.
---
## 7. Milestones (execute in order; each ends green + a lab proof)
- **M-B1 — sidecar parity.** Node sidecar + `sidecar.go` Driver passing the EXISTING
`browserrot_test.go` against the local lab form (`lab/lab-provision-browserrot.sh`), at
parity with go-rod. Deliverable: `go test ./internal/browserrot` green with driver=sidecar.
- **M-B2 — stealth + human input.** patchright fingerprint + Bézier/typing pacing. Re-run lab;
add a detection-signal probe (navigator.webdriver etc. absent).
- **M-B3 — self-heal + persistence.** Tier-2 `heal` RPC (Stagehand observe) triggered on
selector miss → rewrite + persist the recipe → next run is Tier-1. Test: seed a *broken*
selector, assert one run heals it and the second run needs no LLM. Assert **no secret** in any
heal request (leak-check the IPC transcript).
- **M-B4 — record command + first REAL site.** `incredigo browser record`; then point the
engine at ONE real self-owned account (low-stakes, no MFA), rotate + re-login-verify, promote
that host to LIVE-REAL. New `lab/`/runbook entry, per-cred authorization (safe-candidate
ladder discipline).
- **M-B5 (optional/later) — vision fallback.** Tier-3 Computer Use for hostile markup; secrets
injected locally, never to the model.
---
## 8. Files to add / change
- **add** `internal/browserrot/sidecar/` — Node sidecar (package.json, index.js, stealth+heal).
- **add** `internal/browserrot/sidecar.go` — Go Driver/Session over JSON-RPC (+ `sidecar_test.go`).
- **add** recipe persistence (`recipes.go` or extend the curated `Site` table) + record command
in `cmd/incredigo`.
- **add** `lab/lab-provision-browserrot-real.sh` (M-B4) — real-site LIVE target harness.
- **edit** `docs/BROWSER-ROTATION.md` §8 validation table + §9 roadmap as tiers land.
- **edit** driver selection (flag/env) so go-rod stays default until sidecar parity.
- **unchanged:** the rotate spine, hard-rule gates, `Session`/`Driver`/`Rotator` interfaces.
---
## 9. Open decisions / risks to confirm before/while building
1. **Node runtime dependency** — acceptable for the browser layer only? (Go core stays pure;
sidecar is optional, spawned on demand.) → assume yes; keep go-rod as the no-Node fallback.
2. **Stagehand LLM provider + key** — which model, and where the key lives (env, never in a
recipe). LLM sees DOM structure only.
3. **Recipe distribution** — curated public-site table in-repo (no secrets) vs. per-user local
file for private hosts. Likely both.
4. **ToS/legal** — automating even your own-account login can breach a site's ToS; the
self-owned + human-handoff-on-challenge framing is the mitigation, state it in the docs.
5. **Per-site fragility** — accept that recipes rot; the self-heal tier + record command are the
maintenance story, not a promise of permanence ("until the site ups bot detection").
---
## 10. What this plan explicitly does NOT do
- Does not put an LLM in the deterministic destructive path (Tier-1 is pure).
- Does not send any secret to any model, ever.
- Does not bypass MFA/CAPTCHA — those degrade to the human worklist.
- Does not build ML mouse-trajectory generation (negative ROI, see §5).
- Does not change the rotate spine, the backup gate, or any hard rule.
Binary file not shown.

After

Width:  |  Height:  |  Size: 907 KiB

Binary file not shown.