browsertools is OpenUdon's tooling project for turning real website UI
evidence into reviewed UWS browser capability profiles.
It exists for the gap where a task is exposed through a web UI and no suitable
API source document is available. The output is not a browser command trace and
not an OpenAPI substitute. The output is a portable, reviewed
browser-profile document that a UWS workflow can bind to.
real website UI
-> browsertools using Playwright / llm-scraper / Crawl4AI / Firecrawl
-> reviewed browser-profile
-> UWS operation binds to browser-profile action
OpenUdon's iCoT is
the primary end-user authoring entry point across API, browser, and
runtime-handoff sources. iCoT retains goal interviewing, LLM/human interaction,
source selection, and package staging. For UI-only acquisition, the distributed
icot executable re-executes a private copy of itself as a separate process
using Browsertools' importable worker; an external browsertools CLI remains
available for experts and maintainers.
Browsertools owns Playwright-based acquisition, browser safety policy, profile synthesis, and the shared validation library for browser capability and authentication profiles, plus offline validation/draft/review tooling for the separate UWS browser-registration profile. Its CLI is primarily a machine- facing protocol plus maintainer and offline tooling, not a parallel end-user authoring product. Browsertools is not the production runtime; runtime replay belongs to Udon and Browserdriver. See the canonical OpenUdon integration reference for the two integration paths and full ownership split.
For UWS 1.9.1 advisory integrity analysis, package
github.com/OpenUdon/browsertools/contenttrust builds a resolver from reviewed
profiles keyed by sourceDescription name. Browser-derived outputs default to
untrusted while retaining their declared value shape. Navigation and option
selection parameters are authority-bearing, confirmation prompts are
instructions, and typed text remains data. The resolver performs no browser
work and never changes validation or execution.
Authenticated live authoring uses the strict
browsertools.author-session.v2 NDJSON boundary. Human-reviewed MFA kinds and
up to 16 final accessibility outputs produce one private
browsertools.authenticated-authoring.v2 envelope; the protocol carries no
credential, page value, cookie, or browser state. See Authenticated
goal-directed browser authoring.
Registration authoring has separate browser-independent contracts. V1
(browsertools.registration-author-session.v1 and private
browsertools.registration-authoring.v1) remains query-free. Additive v2
(browsertools.registration-author-session.v2 and private
browsertools.registration-authoring.v2) admits only bounded canonical
literal structural queries and validates them on all session and retained BRP
navigations. Both continue only approved-origin GET/HEAD traffic and bind one
reviewed inert BRP without claiming a submit, account attempt, session, or
supported runtime. Canonical unapproved non-navigation GET/HEAD subresources
are counted and aborted before contact without expanding the allowlist or
poisoning an otherwise clean observation; unsafe navigation, methods, and
persistent channels remain fatal.
A guarded typed Chromium backend and deterministic explicit candidate builder
implement those contracts. The importable registrationauthorworker and
browsertools registration-author-session chromium expose that backend through
the same closed no-submit wire and finalize the result only under an explicit
owner-only private root.
See Browser registration profiles.
Page-controlled frame names cross the same canonical reduction boundary as candidate labels. Exact backend-reported MFA subsets are preserved, and the finite total timeout charges browser work rather than human credential/MFA wait time.
The authorworker package is the supported process-entry adapter for
browsertools author-session chromium and the hidden iCoT worker. It accepts
context, private root, optional driver directory, interruptible owned stdin,
and stdout. SIGINT/SIGTERM or parent cancellation closes protocol input, then
waits for browser teardown; failed close negotiation or teardown exits
nonzero. Embedding it does not move Playwright into the iCoT engine or HTTP
server process.
The separate registrationauthorworker is the supported process entry for
no-submit BRP production. It performs read-only pinned-driver preflight, owns
the closeable NDJSON input and headed browser lifetime, and creates an
independently reconstructed result only after clean teardown. Its API and CLI
return no result path or digest. A re-executing parent owns executable hashing,
minimal child environment, and process-group termination.
On Linux, an administrator-provisioned CHROME_DEVEL_SANDBOX is forwarded to
Chromium only after exact mode, root ownership, link count, setuid-filesystem,
resolved-path, and complete ancestor-control checks; sandbox disabling is never
supported.
Accessibility-label reduction is a useful heuristic, not data loss prevention. Ordinary names, identifiers, and order numbers can remain in reduced observations and reviewed traces. Operators must review those records before retaining or sharing them.
go get github.com/OpenUdon/browsertools
The full pipeline from evidence to a reviewable bundle:
import (
"time""github.com/OpenUdon/browsertools/adapter""github.com/OpenUdon/browsertools/adapter/playwright""github.com/OpenUdon/browsertools/bundle""github.com/OpenUdon/browsertools/draft""github.com/OpenUdon/browsertools/evidence""github.com/OpenUdon/browsertools/profile""github.com/OpenUdon/browsertools/review"
)
// 1. Import saved Playwright snapshot as normalized evidence.a:=&playwright.Adapter{}
records, err:=a.Import(snapshotJSON, adapter.Options{
Origin: "https://example.test",
ActionHint: "read_status",
RedactionStatus: evidence.RedactionNotRequired,
})
// 2. Add explicit action intent. Evidence never invents a click or assumes// that an action is read-only.result, err:=draft.Build(records, draft.Spec{
Info: profile.Info{Title: "Example", Origin: profile.Origins{"https://example.test"}},
ObservationKind: profile.ObservationAccessibilitySnapshot,
Confidence: profile.ConfidenceMedium,
ExpiresAfter: "P30D",
Actions: map[string]draft.ActionSpec{
"read_status": {
Sequence: []profile.Step{
{Kind: profile.StepNavigate, Navigate: "/status"},
{Kind: profile.StepWaitFor, WaitFor: &profile.WaitForCondition{
Locator: &profile.Locator{Role: profile.RoleStatus, Name: "OK"},
}},
},
SideEffects: []profile.SideEffect{profile.SideEffectReadOnly},
ConfirmationPolicy: profile.ConfirmationPolicy{Required: false},
},
},
})
// 3. Build a digest-bound bundle at an explicit assessment time.assessedAt:=time.Now().UTC()
reviewed, err:=review.Build(result.Profile, records, result.Decisions, assessedAt)
iferr!=nil||!reviewed.Promotable() {
// fix gaps before promoting
}
// 4. Wrap the exact reviewed inputs in an inert publication bundle.published, err:=bundle.Build(bundle.BuildOptions{
ID: "example/status", Release: "1.0.0", Source: "reviewed_fixture",
License: "CC0-1.0", Profile: result.Profile, Review: reviewed,
Evidence: records, PublishedAt: assessedAt,
})The CLI exposes the same offline pipeline:
go run ./cmd/browsertools profile validate --input ./profiles/example.yaml
go run ./cmd/browsertools evidence import \
--adapter playwright --input ./capture.json \
--origin https://example.test --redaction-status not_required \
--out ./evidence.json
go run ./cmd/browsertools draft build \
--evidence ./evidence.json --spec ./draft-spec.yaml \
--out ./profile.yaml
go run ./cmd/browsertools review bundle \
--profile ./profile.yaml --evidence ./evidence.json \
--at 2026-08-14T00:00:00Z --out ./review-bundle.json
go run ./cmd/browsertools revalidate check \
--profile ./profile.yaml --evidence ./evidence.json \
--at 2026-08-14T00:00:00Z --out ./revalidation.json
go run ./cmd/browsertools bundle build \
--id example/status --release 1.0.0 \
--profile ./profile.yaml --review ./review-bundle.json \
--evidence ./evidence.json --source reviewed_fixture --license CC0-1.0 \
--published-at 2026-08-14T00:00:00Z --out ./capability-bundle.json
go run ./cmd/browsertools bundle verify \
--input ./capability-bundle.json --at 2026-08-14T00:00:00Z
go run ./cmd/browsertools registry publish \
--root ./public-registry --bundle ./capability-bundle.json \
--at 2026-08-14T00:00:00Z
go run ./cmd/browsertools registry search \
--location ./public-registry --query status \
--at 2026-08-14T00:00:00ZBrowser acquisition is an explicit, separately installed authoring feature.
Browsertools pins Playwright-Go v0.6201.0 (Playwright 1.62.1). Install the
matching driver and Chromium deliberately, then verify the local installation:
go run github.com/mxschmitt/playwright-go/cmd/playwright@v0.6201.0 install chromium
go run ./cmd/browsertools playwright doctor --engine chromiumBefore playwright doctor or an author session starts, Browsertools verifies
the installed Node executable, CLI, and exact Playwright 1.62.1 package using
read-only filesystem checks. It creates no cache directory, invokes no
installer, and contacts no network. The doctor then starts and stops only that
installed driver; its full local CLI report may include the browser executable
path, while the separate UI-safe report omits executable and private paths. It
does not contact a site or launch a browser. The other CLI
commands remain file-first unless they are explicitly named live acquisition,
check, or assisted-authentication commands.
Safe live capture is Chromium-only, headless, non-interactive, and private. It
requires every exact origin and writes page material only to a finite-retention
private_raw cache entry:
go run ./cmd/browsertools capture chromium \
--url https://example.test/member \
--allow-origin https://example.test \
--cache-root ./.browsertools-cache \
--action-hint read_dashboard \
--retain-for 24hThe command blocks non-GET/HEAD requests, unapproved origins, child frames, service workers, WebSockets, popups, downloads, dialogs, file choosers, and non-essential resources. It uses finite navigation, total, request, response, ARIA-depth, evidence-size, and retention limits. Only cache metadata is printed; raw ARIA/JSON-LD content is never written to stdout. See Safe live capture for the review/redaction handoff.
Explicit private screenshots, traces, and minimal-content HAR can be captured as one short-lived, non-publishable ZIP:
go run ./cmd/browsertools rich-capture chromium \
--url https://example.test/member \
--allow-origin https://example.test \
--cache-root ./.browsertools-cache \
--artifact screenshot --artifact trace --artifact har \
--retain-for 1hThe command prints only cache metadata. Export requires a new 0600 file;
review for secrets is mandatory, and cache delete requires the exact digest
twice. Rich artifacts have no publication or normalized-evidence path.
After normalization, the terminal guide makes every capability and safety decision explicit and emits one deterministic envelope containing the accepted spec, generated profile, action-bound evidence, ambiguity decisions, and promotable review:
go run ./cmd/browsertools guide author \
--evidence ./evidence.json \
--at 2026-08-16T12:00:00Z \
--out ./guided-authoring.jsonA separately selected live check can compare declared locators, waits, and output shapes with the current page without executing any profile macro or emitting page values:
go run ./cmd/browsertools live-check chromium \
--profile ./profile.yaml \
--url https://example.test/member \
--allow-origin https://example.test \
--action read_dashboard \
--at 2026-08-16T12:00:00Z \
--out ./live-check.jsonBoth paths keep sequence intent, side effects, confirmation, expiry, and ambiguity decisions human-authored. The live check reuses the exact-origin ephemeral capture policy, accepts plain CSS outputs but no Playwright selector language, and writes only profile-bound match/type facts. See Guided capability authoring and live checks.
The same profile-derived read-only checks can be compared without locator rewrites across explicitly installed engines:
go run ./cmd/browsertools portability check \
--profile ./profile.yaml \
--url https://example.test/member \
--allow-origin https://example.test \
--action read_dashboard \
--engine chromium --engine firefox --engine webkit \
--out ./portability.jsonChromium is the required baseline. Missing engines and shape differences are fixed value-free diagnostics, not silent fallbacks. See Private rich evidence and cross-engine portability.
Portable sign-in recipes use a separate additive contract and remain local to the workflow package:
go run ./cmd/browsertools auth-draft build \
--spec ./authentication-spec.yaml \
--out ./browser-authentication/member.yaml
go run ./cmd/browsertools auth-profile validate \
--input ./browser-authentication/member.yaml \
--at 2026-08-16T00:00:00Z
go run ./cmd/browsertools auth-review bundle \
--profile ./browser-authentication/member.yaml \
--at 2026-08-16T00:00:00Z \
--out ./browser-authentication/member.review.jsonAn explicit headed authoring command can then observe the exact recipe while the operator enters credentials and completes MFA directly in the browser:
go run ./cmd/browsertools auth-assist chromium \
--profile ./browser-authentication/member.yaml \
--flow member_login_push \
--approve-origin https://members.example.test \
--approve-origin https://login.example.test \
--post-budget member_login_push:3=2 \
--out ./browser-authentication/member.assisted.jsonThe profile supplies every locator, challenge alternative, submit step, and
success condition; Browsertools does not infer them. Each selected flow runs in
a separate visible ephemeral context. Browsertools navigates declared URLs and
counts declared accessibility locators, but the operator performs every
credential, click, and challenge step and signals completion with an empty
terminal line. A POST is blocked unless its exact zero-based flow step has an
explicit bounded --post-budget; all other mutating methods are blocked.
The 0600 output is created only after every context has closed and contains a
selected uws.browser-authentication.1.0 profile, its digest-bound review, and
value-free origin/count/request evidence. It cannot use stdin for a profile,
stdout for the artifact, or overwrite a path. Authentication recipes are also
excluded from static registry publication. Actual credentials, MFA responses,
OAuth state, cookies, storage, and sessions stay in the browser or downstream
private runtime. See Browser authentication profiles.
Account registration uses a separate explicitly mutating contract. Browsertools can validate, deterministically build, and digest-review only the inert recipe:
go run ./cmd/browsertools registration-draft build \
--spec ./registration-spec.yaml \
--out ./browser-registration/dedicated-test-user.yaml
go run ./cmd/browsertools registration-profile validate \
--input ./browser-registration/dedicated-test-user.yaml \
--at 2026-08-25T00:00:00Z
go run ./cmd/browsertools registration-review bundle \
--profile ./browser-registration/dedicated-test-user.yaml \
--at 2026-08-25T00:00:00Z \
--out ./browser-registration/dedicated-test-user.review.jsonThese commands are file-only and never launch a browser, contact a target, resolve a symbolic credential, submit a registration, handle CAPTCHA/MFA/email verification, approve a run, or perform cleanup. Registration profiles and reviews remain package-local and are excluded from the browser capability registry. See Browser registration profiles.
The typed registrationauthor.Build path combines one current reduced
registration observation with a complete registrationdraft.Spec, exact
reviewed candidate IDs, selected submit candidate and flow, approved origins,
and explicit fixed call controls. It reconstructs the current-generation
candidate IDs, binds the one accessibility-name submit, and emits the exact M26
review message without inferring any profile or cleanup field.
After clean session teardown,
registrationauthorresult.FinalizePrivate independently reconstructs and
strict-decodes the M26 result before and after an anchored owner-only,
create-once write; its path remains process-private.
The standalone producer uses that complete path:
browsertools registration-author-session chromium \
--private-root ./private-registration-results \
--driver-dir "$PLAYWRIGHT_DRIVER_PATH"Stdin and stdout are reserved for registration author-session NDJSON. The
worker defaults to v1; --protocol v2 deliberately selects the additive
retained-query contract and guarded Chromium routing. The private root must
already be a mode-0700 directory. The command never prints the resulting file
name or digest, and it provides no registration runtime or submit command.
Caller-supplied raw captures and derived artifacts can be kept in an explicit private local cache. Raw entries can never be publication eligible:
go run ./cmd/browsertools cache put \
--root ./.browsertools-cache --input ./capture.json \
--kind private_raw --media-type application/json \
--created-at 2026-08-14T00:00:00Z
go run ./cmd/browsertools cache list \
--root ./.browsertools-cache --at 2026-08-14T12:00:00Z
go run ./cmd/browsertools cache prune \
--root ./.browsertools-cache --at 2026-09-14T00:00:00Z
go run ./cmd/browsertools cache delete \
--root ./.browsertools-cache --id sha256:EXACT_ID --confirm-id sha256:EXACT_ID- A complete typed model and validation helpers for UWS
browser-profiledocuments. - Typed validation, deterministic drafting, digest-bound review, freshness,
and local discovery for package-local
uws.browser-authentication.1.0recipes. - Typed offline validation, deterministic explicit drafting, digest-bound
review, and freshness for package-local
uws.browser-registration.1.0recipes. - Secret-free evidence records from browser and scraper tooling.
- Draft profile generation from reviewed evidence.
- Review bundles with validation, confidence, expiry, side-effect, and revalidation notes.
- Deterministic fixture-only revalidation and digest-bound promotion gates.
- A bounded, content-addressed private cache for caller-supplied experiences, normalized evidence, profiles, and review bundles.
- Canonical, digest-bound, lifecycle-assessed publication bundles for reviewed profiles, safe evidence, and optional inert UWS companions.
- A service-free static registry layout, atomic local publisher, bounded local/HTTPS reader, and local browser-source discovery report.
- Browser-profile, scraper/crawler, and browser-backed wrapper examples.
- Optional adapters for Playwright, llm-scraper, Crawl4AI, and Firecrawl outputs.
- An isolated Playwright-Go acquisition boundary, pinned capability policy, and offline installation doctor for authoring-only browser tooling.
- Explicit headless Chromium acquisition into the private raw cache with exact origins, ephemeral context destruction, and closed activity/resource bounds.
- Deterministic terminal-guided authoring that binds explicit intent, reviewed evidence, decisions, a valid profile, and a promotable review.
- Value-free Chromium live checks for declared locators, waits, and output shapes without macro execution.
- Explicit short-lived screenshot/trace/HAR bundles with mandatory secret review, no publication path, and exact-ID deletion.
- Fresh Chromium-baseline Firefox/WebKit comparisons of the same value-free profile probes, plus documented browser.1.6 contract pressure.
- Headed manual authentication observation with separate ephemeral contexts, exact preapproved origins, step-scoped POST ceilings, and local value-free draft/review bundles.
- UWS schema and workflow semantics. Those live in
github.com/OpenUdon/uws. - OpenAPI/API-source discovery and provider catalog metadata. Those belong in
github.com/OpenUdon/apitools. - Production browser execution, runtime credential resolution, retained cookies/sessions, retries, account selection, or production side effects.
- A general Playwright, WebDriver, Puppeteer, or scraping DSL.
- Implicit browser launch or uploading cached content. Acquisition commands are separately selected; cache commands are local and offline, and publication has a separate verification boundary.
- Accounts, membership, a registry database, remote writes, or deployment credentials. Static catalogs are reviewed and deployed by existing repository/hosting workflows.
- Publication of authentication recipes through the static browser capability registry.
- Publication of registration recipes through the static browser capability registry, or live registration/account cleanup of any kind.
OpenAPI should describe a stable HTTP service. If you build a browser-backed wrapper service, OpenAPI should describe that wrapper. Browsertools can also emit an advisory overlay sidecar for the wrapper, linking OpenAPI operations to reviewed browser-profile actions and review bundles.
browser-profile describes the UI binding behind the wrapper or behind a UWS
browser operation:
website UI
-> browsertools-reviewed profile
-> browser runtime executes profile
or:
website UI
-> browser-backed wrapper service
-> OpenAPI describes wrapper
-> UWS binds to wrapper API
- Project description
- Authenticated goal-directed browser authoring
- OpenUdon integration
- Wrapper-service guidance
- Typed-profile migration
- Publishable capability bundles
- Static registry and contribution workflow
- Browser authentication profiles
- Browser registration profiles
- Safe live capture
- Private rich evidence and cross-engine portability
- Guided capability authoring and live checks
- Examples
go test ./...
go vet ./...
GOWORK=off go run golang.org/x/vuln/cmd/govulncheck@v1.6.0 ./...
git diff --check
(cd ../uws && go test ./...)Default tests use fakes and synthetic fixtures; they do not install or launch browsers and do not contact the network.
An installed-browser loopback integration test is opt-in:
BROWSERTOOLS_LIVE_TEST=1 go test ./capture -run PlaywrightLiveCaptureLoopbackHeaded authentication behavior is covered by browser-free policy/state-machine tests by default. A real site is never contacted by the test suite. An installed-browser headed smoke test is separately opt-in and loopback-only:
BROWSERTOOLS_AUTH_LIVE_TEST=1 go test ./capture -run PlaywrightAuthHeadedLoopbackRich evidence and cross-engine smoke tests are independently gated and remain loopback-only:
BROWSERTOOLS_RICH_LIVE_TEST=1 go test ./capture -run PlaywrightRichCaptureLoopback
BROWSERTOOLS_PORTABILITY_LIVE_TEST=1 go test ./capture -run PlaywrightPortabilityLoopback