Skip to content

Add batched POST /chromium/configure#244

Merged
hiroTamada merged 13 commits into
mainfrom
feat/chromium-configure-multipart
May 22, 2026
Merged

Add batched POST /chromium/configure#244
hiroTamada merged 13 commits into
mainfrom
feat/chromium-configure-multipart

Conversation

@hiroTamada
Copy link
Copy Markdown
Contributor

@hiroTamada hiroTamada commented May 19, 2026

Summary

  • Adds POST /chromium/configure: one multipart request to batch display sizing, Chrome policies, Chromium flags, profile archive, extensions, and optional start_url navigation after a single Chromium restart.
  • Introduces cdpclient.NavigateFirstPage for post-ready Page.navigate (flattened CDP session, waits for load / domcontentloaded). Fixes concurrent WebSocket reads that caused flaky navigate failures.
  • Refactors extension upload logic into shared applyExtensionZipItems used by configure and existing upload-and-restart.
  • E2e: 31-way powerset over optional configure parts plus bare/JSON start_url tests; local runner script and DETACHED=1 headless run-docker.sh for manual API use.
  • make test: e2e package timeout raised to 120m (full suite exceeds default 10m).

Test plan

  • go vet ./... and unit tests (go test excluding /e2e)
  • ./scripts/run-local-chromium-configure-powerset.sh (31 permutations + start_url variants) against locally built headless image
  • Full go test -race -timeout 120m ./e2e/ with E2E_CHROMIUM_HEADLESS_IMAGE / E2E_CHROMIUM_HEADFUL_IMAGE pointing at images built from this branch

Made with Cursor


Note

Medium Risk
Moderate risk: introduces a new multipart configure endpoint that performs filesystem/policy/flag/profile mutations and changes the Chromium restart/DevTools-readiness flow, which could affect instance startup stability and extension handling.

Overview
Adds a new multipart POST /configure endpoint to apply batched Chromium/session changes (policies, flags, extensions, profile archive, and display sizing) with a single stop/start cycle, plus optional best-effort start_url navigation via CDP.

Refactors extension installation into shared applyExtensionZipItems (with partial-install cleanup) and reworks Chromium restart logic to explicitly stop then start via supervisorctl, waiting for DevTools readiness by actively dialing the upstream.

Extends cdpclient with DispatchStartURL, updates generated OpenAPI client/server types (including structured ChromiumConfigureError), adds unit + e2e coverage for multipart combinations, and increases e2e test timeout to 120m.

Reviewed by Cursor Bugbot for commit 0f63e8a. Bugbot is set up for automated code reviews on this repo. Configure here.

hiroTamada and others added 2 commits May 19, 2026 09:27
Single multipart request can apply display, policies, flags, extensions, and profile changes with one Chromium restart, then navigate via CDP after DevTools is ready.

Co-authored-by: Cursor <cursoragent@cursor.com>
Drop DETACHED run-docker mode, the powerset runner script, and related AGENTS.md notes.

Co-authored-by: Cursor <cursoragent@cursor.com>
@hiroTamada hiroTamada marked this pull request as ready for review May 20, 2026 15:22
Comment thread server/cmd/api/api/chromium_configure.go
@firetiger-agent
Copy link
Copy Markdown

Monitoring Plan: POST /chromium/configure

This PR introduces a new batched multipart endpoint (POST /chromium/configure) that consolidates display resizing, Chromium flag merging, enterprise policy application, extension installation, user-data profile loading, and optional CDP navigation into a single API call. It also refactors UploadExtensionsAndRestart to share logic with the new endpoint via applyExtensionZipItems(), and adds new stopChromium()/startChromiumAndWait() helpers backed by supervisorctl with 2-minute timeouts and a 15-second DevTools readiness wait.

Key risks to watch post-deploy: (1) the new endpoint is not yet exercised in production so a spike in 400/500 responses would indicate client or validation issues; (2) the refactored stop/start cycle now shares code paths with existing extension uploads — any regression in the supervisorctl integration would surface as stop chromium failed or CDP ready wait failed errors, which today run at ~1–14/hr per signal; (3) startChromiumAndWait() now uses a 15-second readiness timeout (previously indefinite via subscription) which could produce devtools not ready in time errors if Chromium is slow to start under load. Status updates will be posted automatically on this PR as monitoring progresses.

View agent

Batching keeps a single Chromium restart while preserving Kernel's configure order, propagating required-step failures, and keeping start_url best-effort.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread server/cmd/api/api/chromium_configure.go
Comment thread server/cmd/api/api/chromium_configure.go Outdated
Avoid unnecessary stop/start cycles for empty flag updates and remove stale CDP/configure helpers flagged during review.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread server/cmd/api/api/chromium_configure.go
Match stop/start gating to policy apply semantics so empty policy objects remain no-op configuration.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread server/cmd/api/api/chromium_configure.go
Keep the display configure branches consistent if display planning ever becomes a no-op.

Co-authored-by: Cursor <cursoragent@cursor.com>
@hiroTamada hiroTamada changed the title Add batched POST /chromium/configure with optional start_url Add batched POST /chromium/configure May 21, 2026
Comment thread server/openapi.yaml Outdated
Comment thread server/openapi.yaml
Keep shared API errors generic while documenting the configure-specific step field on ChromiumConfigureError.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread server/cmd/api/api/chromium_configure.go
Comment thread server/cmd/api/api/chromium_configure.go
Keep flag validation consistent with actionable detection and reject duplicate extension zip parts before copying uploads.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread server/cmd/api/api/chromium_configure.go Outdated
Comment thread server/cmd/api/api/chromium_configure.go Outdated
Comment thread server/cmd/api/api/chromium_configure.go
Classify multipart parse failures by response type, keep policy validation errors as bad requests, and clear Xvfb viewport overrides under the resize lock.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread server/cmd/api/api/chromium_configure.go
Copy link
Copy Markdown
Contributor

@rgarcia rgarcia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviewed — implementation shape looks good overall. a few API/schema things worth tightening before this lands:

  • server/openapi.yaml:1173 — nit: consider /configure instead of /chromium/configure; display config is not Chromium-specific, and this endpoint may grow to cover more non-Chromium session configuration over time.
  • server/openapi.yaml:1182 — the “control-plane sync path” wording feels too inside-baseball for public API docs. maybe reword this to just describe the execution order directly.
  • server/openapi.yaml:1214strip_components feels like archive layout mechanics leaking into a high-level profile/configure API. even though this exists on the generic zstd upload endpoint, for profile archives it may be better to require/document one expected archive structure instead.
  • server/openapi.yaml:2586ChromiumConfigureError does not define step, but the handler returns it and generated oapi.go currently includes it. since this schema has additionalProperties: false, the source schema and generated code should be reconciled so clients/validators agree on the 500 response shape.

hiroTamada and others added 2 commits May 21, 2026 13:36
Confirm stopped states after supervisor errors and verify DevTools readiness by dialing the current upstream instead of relying only on log notifications.

Co-authored-by: Cursor <cursoragent@cursor.com>
Expose batched session configuration at /configure and clean up public OpenAPI wording around execution order and profile archives.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread server/cmd/api/api/chromium_configure.go
return "https://" + rawURL
}
return rawURL
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No scheme validation allows file:// and javascript: navigation

Medium Severity

normalizeStartURL only adds https:// for bare hosts. URLs with explicit schemes like file:///etc/passwd or javascript:... pass through unvalidated and are dispatched via CDP Page.navigate. The test at line 69 confirms file:///etc/passwd is accepted. While Chrome may block some schemes, file:// URIs are navigable in Chromium when launched without --disable-file-url-scheme, potentially exposing local filesystem contents through the browser.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 182f0a8. Configure here.

Comment thread server/cmd/api/api/chromium_configure.go
Only mark Chromium as restarted after DevTools readiness succeeds so deferred recovery can retry failed starts.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread server/openapi.yaml
$ref: "#/components/responses/InternalError"

/configure:
post:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Endpoint path /configure doesn't name the Chromium subsystem

Low Severity

The new endpoint is registered at /configure, but all existing related endpoints live under /chromium/* (/chromium/policies, /chromium/flags, /chromium/upload-extensions-and-restart). The generic /configure path doesn't identify the subsystem being configured. The PR title itself references POST /chromium/configure, and the OpenAPI description even says "Prefer this over separate /chromium/* and /display calls," confirming this is Chromium-scoped. The path violates the rule requiring paths to name the specific subsystem.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by learned rule: OpenAPI descriptions must be caller-focused; paths must name the specific subsystem

Reviewed by Cursor Bugbot for commit 569d99f. Configure here.

len(st.extItems) > 0 ||
policiesContentNonEmpty(st.chromePoliciesJSON) ||
flagsContentNonEmpty(st.chromiumFlagsJSON)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Display-only configure path skips display when needsStop true

Medium Severity

chromiumNeedsStopCycle does not consider displayJSON as a stop-cycle trigger, but within the needsStop=true branch, the display section is guarded by st.displayJSON != nil && strings.TrimSpace(*st.displayJSON) != "". If a user sends only display with a RestartChromium: true field alongside, say, flags that trigger the stop cycle, the stopped-path display logic runs. However, in the stopped path, chromiumDisplayApplyWhileStopped ignores the RestartChromium field from the display body entirely. This is inconsistent with the standalone PatchDisplay behavior where RestartChromium controls whether Chromium is restarted for the resize. The batched handler always restarts regardless because the stop cycle forces it.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 569d99f. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 5 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0f63e8a. Configure here.

if cur != nil && cur.gotZip && cur.name != "" {
st.extItems = append(st.extItems, extensionZipItem{zipTemp: cur.zipTmp, name: cur.name})
cur = nil
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate extension names across pairs silently overwrite each other

Medium Severity

The new chromiumCfgParseMultipart validates duplicate extensions.name within a single pair but not across pairs. Two pairs with the same name (e.g. both named "foo") pass the parser and reach applyExtensionZipItems, where the batch stat-check loop passes for both (neither exists on disk yet), then the second pair's zip silently overwrites the first's extracted files. This is inconsistent with the per-pair duplicate rejection the PR explicitly added.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0f63e8a. Configure here.

status, statusOut, _ := chromiumSupervisorStatus(ctx)
log.Info("devtools not ready in time", "operation", operation, "elapsed", time.Since(start).String(), "supervisor_status", statusOut)
return fmt.Errorf("devtools not ready in time (chromium status: %s)", status)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Start error may be missed due to select race

Low Severity

In startChromiumAndWait, when the start goroutine fails it sends on errCh then closes doneCh (via defer). Both channels become ready nearly simultaneously. If select picks doneCh first and tryReady happens to succeed (e.g. DevTools is reachable from a stale upstream with allowCurrent=true), the function returns nil (success) despite the supervisorctl start command having failed. The error on errCh is never consumed.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0f63e8a. Configure here.

@hiroTamada hiroTamada merged commit e39965c into main May 22, 2026
14 of 16 checks passed
@hiroTamada hiroTamada deleted the feat/chromium-configure-multipart branch May 22, 2026 03:08
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.

2 participants