Skip to content

feat: add browser-scoped session client#95

Open
rgarcia wants to merge 5 commits intonextfrom
raf/browser-scoped-client
Open

feat: add browser-scoped session client#95
rgarcia wants to merge 5 commits intonextfrom
raf/browser-scoped-client

Conversation

@rgarcia
Copy link
Copy Markdown
Contributor

@rgarcia rgarcia commented Apr 13, 2026

Summary

  • add client.ForBrowser(...) to create a browser-scoped client from a browser session response
  • route browser subresource methods through the session base_url so callers stop repeating the browser id
  • expose raw HTTP through browser.HTTPClient() while keeping the internal /curl/raw transport path hidden
  • add focused tests for metro path rewriting, JWT extraction, and raw HTTP behavior

Test plan

  • go test ./...

Made with Cursor


Note

Medium Risk
Adds new request-scoping/middleware that rewrites paths, injects jwt, and strips Authorization, plus introduces a /curl/raw tunneling transport; mistakes here could break browser subresource routing or inadvertently leak/omit auth. Also bumps the module to Go 1.23 and changes how WithHTTPClient is applied, which may affect client behavior in edge cases.

Overview
Adds Client.ForBrowser(...) which builds a BrowserSessionClient scoped to a browser session base_url, so subresource calls (Process, Fs, Replays, etc.) no longer require repeatedly passing the browser id.

Introduces browserscope.BrowserSessionMiddleware to rewrite /browsers/{session_id}/... paths against the session base URL, attach jwt as a query param, and strip Authorization to avoid forwarding API keys to the browser.

Adds browser-egress HTTP support via BrowserSessionClient.HTTPClient() backed by a RawCURLRoundTripper that tunnels absolute http(s) requests through {base_url}/curl/raw while keeping that internal path out of the public API.

Includes a new code generator (internal/genbrowsersessionservices) and lint script checks to keep browser_session_services_gen.go in sync, plus unit/integration tests for path rewriting, JWT extraction, and raw-curl behavior. Updates tooling by bumping go to 1.23.0 and adding golang.org/x/tools, and switches option.WithHTTPClient to a PreRequestOptionFunc so scoped clients can reliably reuse the underlying *http.Client.

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

Bind browser subresource calls to a browser session's base_url and expose raw HTTP through a standard http.Client so metro-routed access feels like normal Go networking.

Made-with: Cursor
@firetiger-agent
Copy link
Copy Markdown

Firetiger deploy monitoring skipped

This PR didn't match the auto-monitor filter configured on your GitHub connection:

Any PR that changes the kernel API. Monitor changes to API endpoints (packages/api/cmd/api/) and Temporal workflows (packages/api/lib/temporal) in the kernel repo

Reason: PR modifies client libraries and session handling, not API endpoints (packages/api/cmd/api/) or Temporal workflows (packages/api/lib/temporal) as specified in the filter.

To monitor this PR anyway, reply with @firetiger monitor this.

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 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: HTTPClient() never extracts user's custom HTTP client
    • Changed option.WithHTTPClient to return requestconfig.PreRequestOptionFunc so PreRequestOptions now applies it and BrowserSessionClient.HTTPClient() receives the caller’s custom client.

Create PR

Or push these changes by commenting:

@cursor push 3b2a924f02
Preview (3b2a924f02)
diff --git a/option/requestoption.go b/option/requestoption.go
--- a/option/requestoption.go
+++ b/option/requestoption.go
@@ -56,7 +56,7 @@
 // For custom uses cases, it is recommended to provide an [*http.Client] with a custom
 // [http.RoundTripper] as its transport, rather than directly implementing [HTTPClient].
 func WithHTTPClient(client HTTPClient) RequestOption {
-	return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error {
+	return requestconfig.PreRequestOptionFunc(func(r *requestconfig.RequestConfig) error {
 		if client == nil {
 			return fmt.Errorf("requestoption: custom http client cannot be nil")
 		}

You can send follow-ups to the cloud agent here.

Comment thread browser_session.go
rgarcia added 4 commits April 13, 2026 14:09
Use the browser session base_url directly for path rewriting, preserve custom HTTP clients in HTTPClient(), and add an env-gated integration test for browser-scoped routing.

Made-with: Cursor
Avoid depending on base_url path details in the integration test, keep the JWT helper package-private, and make round-tripper conformance explicit while preserving browser-scoped routing behavior.

Made-with: Cursor
Keep the raw round-tripper constructor package-private, remove defensive middleware branches that imply unsupported empty inputs, and retain the browser-scoped integration coverage without baking in base_url path details.

Made-with: Cursor
Replace the handwritten browser-scoped Go service façade with deterministic generated bindings derived from the generated browser service graph, and enforce regeneration in lint.

Made-with: Cursor
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 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: SDK-level dependency on code-gen-only golang.org/x/tools forces Go version bump
    • I moved internal/genbrowsersessionservices to its own Go module and updated generation entrypoints so golang.org/x/tools no longer affects the SDK module, allowing the root go.mod to stay at Go 1.22 without tool-only deps.

Create PR

Or push these changes by commenting:

@cursor push bac079904b
Preview (bac079904b)
diff --git a/browser_session.go b/browser_session.go
--- a/browser_session.go
+++ b/browser_session.go
@@ -1,6 +1,6 @@
 package kernel
 
-//go:generate go run ./internal/genbrowsersessionservices -output browser_session_services_gen.go
+//go:generate sh -c "cd internal/genbrowsersessionservices && go run . -dir ../.. -output browser_session_services_gen.go"
 
 import (
 	"fmt"

diff --git a/go.mod b/go.mod
--- a/go.mod
+++ b/go.mod
@@ -1,16 +1,13 @@
 module github.com/kernel/kernel-go-sdk
 
-go 1.23.0
+go 1.22
 
 require (
 	github.com/tidwall/gjson v1.18.0
 	github.com/tidwall/sjson v1.2.5
-	golang.org/x/tools v0.31.0
 )
 
 require (
 	github.com/tidwall/match v1.1.1 // indirect
-	github.com/tidwall/pretty v1.2.1 // indirect
-	golang.org/x/mod v0.24.0 // indirect
-	golang.org/x/sync v0.12.0 // indirect
+	github.com/tidwall/pretty v1.2.0 // indirect
 )

diff --git a/go.sum b/go.sum
--- a/go.sum
+++ b/go.sum
@@ -1,18 +1,9 @@
-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
-github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
 github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
 github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
 github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
 github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
+github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
 github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
-github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
-github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
 github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
 github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
-golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
-golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
-golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
-golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
-golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
-golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=

diff --git a/internal/genbrowsersessionservices/go.mod b/internal/genbrowsersessionservices/go.mod
new file mode 100644
--- /dev/null
+++ b/internal/genbrowsersessionservices/go.mod
@@ -1,0 +1,10 @@
+module github.com/kernel/kernel-go-sdk/internal/genbrowsersessionservices
+
+go 1.23.0
+
+require golang.org/x/tools v0.31.0
+
+require (
+	golang.org/x/mod v0.24.0 // indirect
+	golang.org/x/sync v0.12.0 // indirect
+)

diff --git a/internal/genbrowsersessionservices/go.sum b/internal/genbrowsersessionservices/go.sum
new file mode 100644
--- /dev/null
+++ b/internal/genbrowsersessionservices/go.sum
@@ -1,0 +1,8 @@
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
+golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
+golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
+golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
+golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=

diff --git a/scripts/generate-browser-session b/scripts/generate-browser-session
--- a/scripts/generate-browser-session
+++ b/scripts/generate-browser-session
@@ -4,4 +4,7 @@
 
 cd "$(dirname "$0")/.."
 
-go run ./internal/genbrowsersessionservices -output browser_session_services_gen.go
+(
+  cd internal/genbrowsersessionservices
+  go run . -dir ../.. -output browser_session_services_gen.go
+)

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 1720a28. Configure here.

Comment thread go.mod
require (
github.com/tidwall/gjson v1.18.0
github.com/tidwall/sjson v1.2.5
golang.org/x/tools v0.31.0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SDK-level dependency on code-gen-only golang.org/x/tools forces Go version bump

Medium Severity

golang.org/x/tools is added as a direct dependency of the SDK module, but it's only imported by the internal code generator (internal/genbrowsersessionservices). This heavyweight dependency and its transitive deps (golang.org/x/mod, golang.org/x/sync) inflate the module graph for all SDK consumers. More critically, it forces the minimum Go version from go 1.22 to go 1.23.0, which is a breaking change for any consumer still on Go 1.22. Moving the generator into its own module (with a separate go.mod) would keep the tool dependency isolated from the SDK's public dependency footprint.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1720a28. Configure here.

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