Skip to content

Add local HTTP/OpenAPI server and JS SDK#623

Closed
GautamBytes wants to merge 5 commits into
Gitlawb:mainfrom
GautamBytes:feature/http-openapi-sdk
Closed

Add local HTTP/OpenAPI server and JS SDK#623
GautamBytes wants to merge 5 commits into
Gitlawb:mainfrom
GautamBytes:feature/http-openapi-sdk

Conversation

@GautamBytes

@GautamBytes GautamBytes commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Closes #622

Summary

  • Adds zero serve --http with local bearer auth, loopback-only no-auth, CORS, OpenAPI, SSE, session, run, approval, abort, file, and search endpoints.
  • Adds internal/httpapi with stdlib net/http, run manager, permission broker, ask-user broker, JSON error shape, and path confinement.
  • Runs HTTP prompts in-process through agent.Run so permission and ask-user callbacks can block and resume over HTTP.
  • Adds a dependency-free JS SDK package at sdk/js as @gitlawb/zero-sdk.
  • Keeps zero serve --mcp behavior unchanged.

Security and scope

  • This is a v1 local trusted automation server, not a hosted multi-user server.
  • Auth fails closed unless --no-auth is explicitly used on loopback.
  • CORS rejects untrusted origins, JSON bodies are capped and content-type checked, file access is confined to the workspace, and HTTP errors are redacted.

Notes

  • Includes one small TUI test fixture hardening line so go test ./... passes from this long worktree path.
  • Does not include generated local review/threat-model report files.

Summary by CodeRabbit

  • New Features
    • Added zero serve --http mode with HTTP/OpenAPI docs, auth (env token or auto-generated), CORS, session management, and streamed run execution/events.
    • Added an internal HTTP API layer (including SSE event streaming) and workspace file browsing/search endpoints.
    • Introduced a JavaScript SDK with typed models and SSE subscription.
  • Bug Fixes
    • Strengthened request validation, authentication/CORS behavior, workspace confinement (including symlink escape checks), and domain error responses.
  • Tests
    • Expanded integration coverage for HTTP mode, streaming/brokers, permission/ask synchronization, async runs, and file routing.
  • Documentation
    • Updated JS SDK README and TypeScript typings.

@GautamBytes GautamBytes marked this pull request as ready for review July 9, 2026 15:26
Copilot AI review requested due to automatic review settings July 9, 2026 15:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9be9d47b-12eb-47a5-ba16-82776fedf1b5

📥 Commits

Reviewing files that changed from the base of the PR and between a5f9098 and 75741a4.

📒 Files selected for processing (2)
  • internal/httpapi/broker.go
  • internal/httpapi/server_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/httpapi/broker.go

Walkthrough

Adds a local HTTP/OpenAPI server with session, file, event, and approval endpoints, CLI HTTP-mode startup and agent execution, plus a typed JavaScript SDK with SSE support.

Changes

HTTP Server and CLI Integration

Layer / File(s) Summary
HTTP contracts and error model
internal/httpapi/types.go, internal/httpapi/errors.go
Defines runner, request/result, runtime option, snapshot, and structured error types.
Authentication, CORS, and file confinement
internal/httpapi/middleware.go, internal/httpapi/files.go
Adds bearer auth, CORS and loopback checks, workspace-confined file access, and file search.
Event and approval streaming
internal/httpapi/broker.go
Adds session-filtered event delivery, permission/ask coordination, SSE output, and opaque IDs.
Routing and run lifecycle
internal/httpapi/server.go
Adds routing, prompt execution, active-run management, approval endpoints, and JSON validation.
Session APIs and OpenAPI documentation
internal/httpapi/sessions.go, internal/httpapi/openapi.go
Adds session lifecycle handlers and generated OpenAPI/documentation responses.
Agent runner and HTTP serve startup
internal/cli/http_runner.go, internal/cli/serve.go, internal/cli/serve_http.go
Connects HTTP prompts to agent execution and adds HTTP serve parsing, validation, authentication, snapshots, startup, and shutdown.
HTTP integration validation
internal/httpapi/server_test.go, internal/cli/serve_test.go
Tests authentication, CORS, validation, sessions, runs, brokers, permissions, file confinement, and CLI flags.

JavaScript SDK Package

Layer / File(s) Summary
SDK declarations and package metadata
sdk/js/index.d.ts, sdk/js/package.json, sdk/js/README.md
Defines the typed client surface, package metadata, and usage example.
SDK requests, errors, and SSE
sdk/js/index.js
Implements authenticated requests, API error mapping, endpoint methods, and incremental SSE parsing.
SDK behavior tests
sdk/js/test/client.test.mjs
Tests request construction, error mapping, SSE parsing across chunks, and 204 handling.

TUI Test Fixture

Layer / File(s) Summary
TUI scroll test setup
internal/tui/scroll_test.go
Sets the workspace path used by the AltScreen transcript test.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • Gitlawb/zero#82: The HTTP runner translates permission events into streamjson events using related permission contracts.

Suggested reviewers: Vasanthdev2004, anandh8x, jatmn

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a local HTTP/OpenAPI server and JS SDK.
Linked Issues check ✅ Passed The changes cover the requested HTTP server, auth/CORS/OpenAPI, SSE, session/prompt/approval/abort/file APIs, and the JS SDK for #622.
Out of Scope Changes check ✅ Passed No clearly unrelated code changes stand out; the test fixture hardening and README/package additions support the new HTTP/SDK scope.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (3)
sdk/js/index.d.ts (1)

65-113: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Consider defining response types instead of Promise<unknown>.

Nearly every method returns Promise<unknown>, which provides no type safety for SDK consumers. For a v1 this is acceptable, but defining at least the key response shapes (e.g., Session, RunResult is already defined, HealthStatus, FileMeta) would significantly improve the SDK's value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdk/js/index.d.ts` around lines 65 - 113, The ZeroClient declaration is using
Promise<unknown> for most API methods, which leaves SDK consumers without usable
type safety. Update the method signatures in ZeroClient to return concrete
response types where possible, starting with the key shapes already represented
in the file such as RunResult and adding named response interfaces for endpoints
like global.health, file.get/content/status, session.get/list/create/update, and
the various list/get methods. Keep the interface definitions organized near
ZeroClient so the return types are easy to find and reuse.
internal/cli/serve_http.go (1)

57-57: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Set server timeouts to prevent resource exhaustion.

The http.Server is constructed without ReadTimeout, ReadHeaderTimeout, WriteTimeout, or IdleTimeout. Even on loopback, a misbehaving client or stuck connection can hold resources indefinitely. Set at least ReadHeaderTimeout (e.g., 10s) and IdleTimeout to bound connection lifetime.

⏱️ Proposed fix: add server timeouts
-	server := &http.Server{Handler: api}
+	server := &http.Server{
+		Handler:           api,
+		ReadHeaderTimeout: 10 * time.Second,
+		IdleTimeout:       120 * time.Second,
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/cli/serve_http.go` at line 57, The http.Server setup in
serve_http.go is missing connection timeouts, which can leave resources tied up
by slow or stuck clients. Update the server initialization around the
http.Server literal to set appropriate timeout fields, at minimum
ReadHeaderTimeout and IdleTimeout, and consider also adding ReadTimeout and
WriteTimeout if they fit the API behavior. Keep the change localized to the
server construction used by the serve_http path so the API handler continues to
work with bounded connection lifetime.

Source: Linters/SAST tools

sdk/js/test/client.test.mjs (1)

41-66: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a chunk-boundary SSE test.

The SDK implements its own streaming parser; add one test where a data: JSON line is split across chunks, with a heartbeat/comment interleaved, to prevent regressions in real network streaming.

Test coverage idea
+test('event.subscribe handles SSE chunks split across line boundaries', async () => {
+  const encoder = new TextEncoder()
+  const stream = new ReadableStream({
+    start(controller) {
+      controller.enqueue(encoder.encode(': ping\n\n'))
+      controller.enqueue(encoder.encode('event: text\n'))
+      controller.enqueue(encoder.encode('data: {"type":"text",'))
+      controller.enqueue(encoder.encode('"delta":"hi"}\n\n'))
+      controller.close()
+    },
+  })
+  const client = createZeroClient({
+    baseUrl: 'http://zero.local',
+    fetch: async () => new Response(stream, { headers: { 'content-type': 'text/event-stream' } }),
+  })
+
+  const events = []
+  for await (const event of client.event.subscribe()) {
+    events.push(event)
+  }
+
+  assert.deepEqual(events, [{ type: 'text', delta: 'hi' }])
+})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdk/js/test/client.test.mjs` around lines 41 - 66, Add a new SSE coverage
test in client.test.mjs alongside event.subscribe to exercise the custom
streaming parser with real chunk boundaries. Build a ReadableStream for
createZeroClient whose chunks split a single data: JSON line across boundaries
and include an interleaved heartbeat/comment event, then verify
client.event.subscribe({ sessionId: 's1' }) still yields the parsed text event
correctly. Use the existing event.subscribe test structure and identifiers
(createZeroClient, client.event.subscribe) to place the new regression case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/httpapi/broker.go`:
- Around line 47-50: The non-blocking send in the event dispatch path is
dropping control events, which can cause permission/ask requests to disappear
before the client sees them. Update the broker’s event publishing logic in the
affected send path so permission/ask events are never silently lost: either make
these control events durable/replayable or switch slow subscribers to
backpressure/disconnect behavior instead of using the default drop branch.
Reference the event fanout code around the channel send in the broker
implementation so the fix applies to the correct control flow.

In `@internal/httpapi/files.go`:
- Around line 116-137: The file handling in the WalkDir callback keeps
descriptors open too long because `defer file.Close()` is used for every opened
file. Update the `os.Open`/scanner logic in `internal/httpapi/files.go` to close
the file explicitly right after scanning completes (including the early-break
path), so each iteration releases the descriptor before the next `WalkDir`
callback continues.

In `@internal/httpapi/middleware.go`:
- Around line 63-69: The LoopbackHost helper in middleware.go is treating an
empty hostname as loopback, which allows --hostname= to bypass the auth gate.
Update LoopbackHost so only explicit loopback values such as "localhost" or
loopback IPs return true, and ensure an empty trimmed host returns false. Keep
the behavior centered on the LoopbackHost function since it is used by the
--no-auth check.

In `@internal/httpapi/server.go`:
- Around line 320-324: The async path in server.executeRun is launching a
goroutine without any panic protection, so a panic there can escape net/http
recovery and skip terminal run events. Wrap the go server.executeRun(...) call
in a recover guard inside the HTTP handler flow, and on panic publish a
failure/terminal event before the goroutine exits; use server.executeRun and the
surrounding async handler branch to locate the fix.

In `@sdk/js/index.d.ts`:
- Line 115: The type declaration for createZeroClient currently makes options
required even though the implementation defaults it to an empty object. Update
the createZeroClient signature in the declaration so callers can omit the
argument, keeping it aligned with the runtime behavior; check the
createZeroClient export and ZeroClientOptions type usage in the declaration
file.

---

Nitpick comments:
In `@internal/cli/serve_http.go`:
- Line 57: The http.Server setup in serve_http.go is missing connection
timeouts, which can leave resources tied up by slow or stuck clients. Update the
server initialization around the http.Server literal to set appropriate timeout
fields, at minimum ReadHeaderTimeout and IdleTimeout, and consider also adding
ReadTimeout and WriteTimeout if they fit the API behavior. Keep the change
localized to the server construction used by the serve_http path so the API
handler continues to work with bounded connection lifetime.

In `@sdk/js/index.d.ts`:
- Around line 65-113: The ZeroClient declaration is using Promise<unknown> for
most API methods, which leaves SDK consumers without usable type safety. Update
the method signatures in ZeroClient to return concrete response types where
possible, starting with the key shapes already represented in the file such as
RunResult and adding named response interfaces for endpoints like global.health,
file.get/content/status, session.get/list/create/update, and the various
list/get methods. Keep the interface definitions organized near ZeroClient so
the return types are easy to find and reuse.

In `@sdk/js/test/client.test.mjs`:
- Around line 41-66: Add a new SSE coverage test in client.test.mjs alongside
event.subscribe to exercise the custom streaming parser with real chunk
boundaries. Build a ReadableStream for createZeroClient whose chunks split a
single data: JSON line across boundaries and include an interleaved
heartbeat/comment event, then verify client.event.subscribe({ sessionId: 's1' })
still yields the parsed text event correctly. Use the existing event.subscribe
test structure and identifiers (createZeroClient, client.event.subscribe) to
place the new regression case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: abd347f8-9e28-40d8-adc0-8cdaaf69b860

📥 Commits

Reviewing files that changed from the base of the PR and between 584ef75 and 15bcf68.

📒 Files selected for processing (19)
  • internal/cli/http_runner.go
  • internal/cli/serve.go
  • internal/cli/serve_http.go
  • internal/cli/serve_test.go
  • internal/httpapi/broker.go
  • internal/httpapi/errors.go
  • internal/httpapi/files.go
  • internal/httpapi/middleware.go
  • internal/httpapi/openapi.go
  • internal/httpapi/server.go
  • internal/httpapi/server_test.go
  • internal/httpapi/sessions.go
  • internal/httpapi/types.go
  • internal/tui/scroll_test.go
  • sdk/js/README.md
  • sdk/js/index.d.ts
  • sdk/js/index.js
  • sdk/js/package.json
  • sdk/js/test/client.test.mjs

Comment thread internal/httpapi/broker.go Outdated
Comment thread internal/httpapi/files.go
Comment thread internal/httpapi/middleware.go
Comment thread internal/httpapi/server.go Outdated
Comment thread sdk/js/index.d.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/httpapi/broker.go (1)

44-72: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Don’t hold broker.mu across the control-event send. A stalled subscriber can block publish for controlEventSendTimeout, freezing subscribe, unsubscribe, and other publishes behind the same mutex. Snapshot the targets or hand off delivery outside the global lock so one slow consumer can’t stall the broker.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/httpapi/broker.go` around lines 44 - 72, Move the blocking
control-event delivery in eventBroker.publish out from under broker.mu so a slow
subscriber cannot stall the broker. In publish, snapshot the target subscribers
under the lock, release the mutex before sending isBlockingControlEvent events
with the timeout, and only reacquire the lock to remove/close timed-out channels
if needed. Keep the existing sessionID filtering and non-blocking send behavior
intact while ensuring subscribe, unsubscribe, and other publishes are not
blocked by one slow consumer.
🧹 Nitpick comments (1)
sdk/js/index.d.ts (1)

149-149: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

FileInfo.type union collapses to string.

'file' | 'directory' | string simplifies to just string because string is a supertype of the literals, so the named variants provide no type-narrowing benefit. If arbitrary backend values are possible, consider 'file' | 'directory' | (string & {}) to preserve autocomplete hints while allowing other strings, or drop | string if only those two values are expected.

♻️ Suggested type refinement
 export interface FileInfo {
   path: string
-  type: 'file' | 'directory' | string
+  type: 'file' | 'directory' | (string & {})
   size: number
   modTime: string
   children?: FileInfo[]
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdk/js/index.d.ts` at line 149, The FileInfo.type union in the type
definition currently collapses to plain string, so the literal variants provide
no narrowing or autocomplete benefit. Update the FileInfo type declaration to
either keep only the expected literals if those are the only valid values, or
use a string-preserving pattern such as a branded string alongside the literals;
make the change in the FileInfo/type definition where type is declared.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@internal/httpapi/broker.go`:
- Around line 44-72: Move the blocking control-event delivery in
eventBroker.publish out from under broker.mu so a slow subscriber cannot stall
the broker. In publish, snapshot the target subscribers under the lock, release
the mutex before sending isBlockingControlEvent events with the timeout, and
only reacquire the lock to remove/close timed-out channels if needed. Keep the
existing sessionID filtering and non-blocking send behavior intact while
ensuring subscribe, unsubscribe, and other publishes are not blocked by one slow
consumer.

---

Nitpick comments:
In `@sdk/js/index.d.ts`:
- Line 149: The FileInfo.type union in the type definition currently collapses
to plain string, so the literal variants provide no narrowing or autocomplete
benefit. Update the FileInfo type declaration to either keep only the expected
literals if those are the only valid values, or use a string-preserving pattern
such as a branded string alongside the literals; make the change in the
FileInfo/type definition where type is declared.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 38185ad8-d3ea-461e-939a-a3823babedc4

📥 Commits

Reviewing files that changed from the base of the PR and between 15bcf68 and c83fec7.

📒 Files selected for processing (8)
  • internal/cli/serve_http.go
  • internal/httpapi/broker.go
  • internal/httpapi/files.go
  • internal/httpapi/middleware.go
  • internal/httpapi/server.go
  • internal/httpapi/server_test.go
  • sdk/js/index.d.ts
  • sdk/js/test/client.test.mjs
🚧 Files skipped from review as they are similar to previous changes (4)
  • sdk/js/test/client.test.mjs
  • internal/httpapi/middleware.go
  • internal/httpapi/files.go
  • internal/cli/serve_http.go

@jatmn jatmn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I found issues that need to be addressed before this is ready.

Findings

  • [P1] Complete CodeRabbit's control-event delivery request for late SSE subscribers
    internal/httpapi/broker.go:55
    The follow-up only waits for a control event when a subscriber is already present. prompt_async starts the run before its caller can open /event; if the run immediately requests permission or asks the user, publish snapshots no targets and discards the opaque request ID. The run then waits in the pending broker with no endpoint that can recover that ID, so it cannot be approved. Please retain/replay pending permission and ask events to a later matching subscription (or establish the subscription before accepting the async run). This completes the resolved CodeRabbit request that control events must not be silently lost.

  • [P1] Reject permission decisions that were not offered for the request
    internal/httpapi/server.go:497
    This endpoint accepts every non-empty action, while the broker discards PermissionRequest.AvailableDecisions. The agent loop normalizes values but does not enforce that set, so an API client can submit always_allow or always_allow_prefix for a prompt that offered only a one-time decision; those actions can persist a grant. Store the allowed decisions with the pending request and return a 4xx for actions outside that list.

  • [P2] Keep the SDK constructor declaration consistent with its runtime requirement
    sdk/js/index.d.ts:232
    The new optional parameter lets TypeScript callers write createZeroClient(), but the implementation immediately rejects that invocation because baseUrl is empty. Keep options required (as it was before the follow-up) or supply an actual runtime default base URL.

@coderabbitai coderabbitai Bot left a comment

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 comments (1)
sdk/js/index.d.ts (1)

232-232: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

createZeroClient options regression — still required in type declaration.

This was previously fixed (commit c83fec7) to make options optional, but the current diff reverts it to required. The runtime implementation still defaults to options = {}, so createZeroClient() is a valid call that TypeScript will reject.

Fix type declaration
-export function createZeroClient(options: ZeroClientOptions): ZeroClient
+export function createZeroClient(options?: ZeroClientOptions): ZeroClient
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdk/js/index.d.ts` at line 232, Update the createZeroClient declaration to
make its options parameter optional, matching the runtime implementation’s
default options behavior and allowing createZeroClient() without arguments.
🧹 Nitpick comments (1)
internal/httpapi/broker.go (1)

108-110: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add ask_user_request / ask_user_answer constants and use them here.

internal/streamjson/streamjson.go already centralizes EventPermissionRequest; these ask-user event types should be defined once too, then reused in internal/httpapi/broker.go and the matching answer path to avoid wire-contract typos.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/httpapi/broker.go` around lines 108 - 110, Define exported constants
for the ask-user request and answer event types in
internal/streamjson/streamjson.go alongside EventPermissionRequest, then update
isBlockingControlEvent in broker.go and the corresponding answer-handling path
to reference those constants instead of inline string values or
streamjson.EventType conversions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@sdk/js/index.d.ts`:
- Line 232: Update the createZeroClient declaration to make its options
parameter optional, matching the runtime implementation’s default options
behavior and allowing createZeroClient() without arguments.

---

Nitpick comments:
In `@internal/httpapi/broker.go`:
- Around line 108-110: Define exported constants for the ask-user request and
answer event types in internal/streamjson/streamjson.go alongside
EventPermissionRequest, then update isBlockingControlEvent in broker.go and the
corresponding answer-handling path to reference those constants instead of
inline string values or streamjson.EventType conversions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 74f9e75c-a8db-4aae-b1ce-4d54bc41343c

📥 Commits

Reviewing files that changed from the base of the PR and between 5bfbdfa and a5f9098.

📒 Files selected for processing (4)
  • internal/httpapi/broker.go
  • internal/httpapi/server.go
  • internal/httpapi/server_test.go
  • sdk/js/index.d.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/httpapi/server.go

@GautamBytes GautamBytes requested a review from jatmn July 10, 2026 11:07
@anandh8x

anandh8x commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the thorough work on this the HTTP server and SDK are a solid v1 design, and the security defaults show real care.

However, it adds a full local HTTP server + JS SDK which is basically plumbing for a web UI. We don’t think we need that right now. We don’t have a web product planned anytime soon, and we'd rather keep fixing bugs and polishing what Zero already does (CLI, TUI, MCP, sandbox) instead of taking on a whole new surface we have to maintain forever.

Also, CONTRIBUTING.md requires community PRs to target an issue with the issue-approved label before implementation, which #622 didn't have. We're closing this for now so you aren't stuck waiting in review limbo. If we eventually green-light an HTTP automation use case, we'd be happy to revisit this. Appreciate the effort regardless.

@anandh8x anandh8x closed this Jul 11, 2026

@Vasanthdev2004 Vasanthdev2004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed this against current main — build, vet, gofmt, and the httpapi/cli/tui tests all pass, including the scroll-test fixture this PR pins (that was the path-length-sensitive test I'd been skipping on Windows; pinning cwd fixes it cleanly, not a mask). The security model is solid: auth fails closed (empty token → 500, bad bearer → 401), --no-auth is rejected off loopback, withAuth wraps every route including SSE, file paths are confined to the workspace with symlink-escape checks, request bodies are capped and content-type-checked, and errors are redacted. The part I cared most about — an HTTP run can't bypass the permission gate — checks out: a tool needing permission publishes a request over SSE and blocks until a human POSTs a decision, and the decision action is allowlisted against what the agent offered, so there's no auto-approve or forged-allow path.

Holding for two things, both kevin's. Issue #622 is still open with no maintainer sign-off — like #653, this is a large feature landing against the author's own open issue, so I'd want your go-ahead on scope before it merges. And the branch is off the 0.3.0 release commit, seven commits behind main; it merges clean, but a rebase onto current main would let it pick up the workspace-trust gating from #529 and the Go 1.26.5 bump in tests. Minor nit: the bearer-token check in withAuth uses a plain string compare rather than constant-time — low-stakes for a local server, but crypto/subtle.ConstantTimeCompare would be the careful choice.

@Vasanthdev2004

Copy link
Copy Markdown
Collaborator

@gnanam1990 when you have time — #623 is the other big one from this contributor: a local HTTP/OpenAPI server + JS SDK (+3691, 19 files) with bearer auth, loopback-only --no-auth, CORS, SSE, sessions, workspace-confined file/find endpoints, and a permission/ask-user broker that blocks HTTP runs on permission requests. I reviewed it inline and the security model looks right (fail-closed auth, symlink-aware path confinement, no auto-approve path), but I'd value your independent read on the permission broker and SSE control-event flow. I'm holding it for an owner call on issue #622 and a rebase, not on the code.

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.

Add local HTTP/OpenAPI server and JS SDK for ZERO automation

5 participants