switch off the browser relay, pending extraction into a plugin
BROWSER_RELAY_PORT is gone and the second listener no longer starts. The Chrome
extension, api/browser/ and the /browser screen all stay on disk — this is going
to be extracted, and deleting it means writing it again.
Three things had to move together, and the middle one would have failed the boot
on its own:
server.tsx the listener, commented out with the variable name recorded
hono.ts the /api/browser mount, closed
registry.ts the 'browser' capability's claim on /browser, dropped
assertCapabilityTotality checks both directions: check 2 refuses to start on a
capability claiming a prefix nothing serves. Unmounting the router alone would
have left the registry describing it, and the server would not have come up.
The capability itself survives because it also claims /scrape, which shares
nothing with the relay — it launches its own headless chromium through playwright
and never speaks to the extension.
The comment in server.tsx carries the two facts that are not recoverable by
reading the remaining code. First, the port is an INPUT TO A CREDENTIAL:
relay-auth.ts derives each extension's token as HMAC(JWT_SECRET,
'officer-browser-relay-v1:${port}:${userId}:${salt}'), so bringing the relay back
on a different number silently invalidates every paired browser — reported by the
extension as "Relay not reachable", which SETUP.md blames on a wrong address,
port or token. Second, it cannot come back as a kernel-assigned port:0 like the
other sidecars: the extension is configured by hand and stores the value, so a
port that moves each restart breaks the pairing each restart.
Left alone deliberately: the /browser route in App.tsx, its Dock entry, and the
Settings → Browser Relay panel. They will not work against a closed endpoint.
Removing them is frontend work for the extraction, not part of switching the
listener off.
.env is down to PORT and POSTGRES_URL.
Not typechecked (empty node_modules, frozen installs). Every changed file parses;
the setup section was run and writes two variables.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
# What officer-setup writes. Everything below this block is optional, or is on its way out.
|
# What officer-setup writes. Everything below this block is optional, or is on its way out.
|
||||||
PORT=9000
|
PORT=9000
|
||||||
BROWSER_RELAY_PORT=18792
|
|
||||||
POSTGRES_URL="postgres://postgres:password@localhost:5432/officer"
|
POSTGRES_URL="postgres://postgres:password@localhost:5432/officer"
|
||||||
|
|
||||||
# ── Moving to the secret store ─────────────────────────────────────────────────────────────────
|
# ── Moving to the secret store ─────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ One Bun process (`src/server.tsx`) serves everything:
|
|||||||
- eight WebSocket providers — terminal, chat, task-runner, pipeline, cliamp, cliamp-audio, desktop,
|
- eight WebSocket providers — terminal, chat, task-runner, pipeline, cliamp, cliamp-audio, desktop,
|
||||||
vault — plus a sidecar registration socket. `terminal` is a byte relay onto the pty sidecar's own
|
vault — plus a sidecar registration socket. `terminal` is a byte relay onto the pty sidecar's own
|
||||||
listener, not a translating bridge; `vault` is the same shape onto Vaultwarden's notifications hub.
|
listener, not a translating bridge; `vault` is the same shape onto Vaultwarden's notifications hub.
|
||||||
- a browser relay on its own port (`BROWSER_RELAY_PORT`, default 18792)
|
- ~~a browser relay on its own port~~ — switched off 2026-08-13, awaiting extraction into a plugin.
|
||||||
|
The extension and `api/browser/` stay on disk; the listener and the `/api/browser` mount do not.
|
||||||
|
|
||||||
Long-running and privileged work lives in **sidecars**: separate processes that dial back in over
|
Long-running and privileged work lives in **sidecars**: separate processes that dial back in over
|
||||||
`/api/sidecar/register` and are tracked in `src/servers/sidecar-registry.ts`. PM2 runs them
|
`/api/sidecar/register` and are tracked in `src/servers/sidecar-registry.ts`. PM2 runs them
|
||||||
|
|||||||
@@ -460,7 +460,6 @@ if ! skip; then
|
|||||||
|
|
||||||
# Read back before anything is asked; existing values become the defaults.
|
# Read back before anything is asked; existing values become the defaults.
|
||||||
ENV_PORT="$(env_get PORT)"
|
ENV_PORT="$(env_get PORT)"
|
||||||
ENV_BROWSER_RELAY_PORT="$(env_get BROWSER_RELAY_PORT)"
|
|
||||||
|
|
||||||
if env_exists; then
|
if env_exists; then
|
||||||
echo " exists — its values are the defaults below"
|
echo " exists — its values are the defaults below"
|
||||||
@@ -471,11 +470,10 @@ if ! skip; then
|
|||||||
# ── what is asked ──
|
# ── what is asked ──
|
||||||
echo ""
|
echo ""
|
||||||
ask_required ENV_PORT "Port Officer listens on" "${ENV_PORT:-9000}"
|
ask_required ENV_PORT "Port Officer listens on" "${ENV_PORT:-9000}"
|
||||||
ENV_BROWSER_RELAY_PORT="${ENV_BROWSER_RELAY_PORT:-18792}"
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo " to write:"
|
echo " to write:"
|
||||||
echo " PORT=${ENV_PORT} BROWSER_RELAY_PORT=${ENV_BROWSER_RELAY_PORT}"
|
echo " PORT=${ENV_PORT}"
|
||||||
echo " POSTGRES_URL=${POSTGRES_URL%%:*}://…"
|
echo " POSTGRES_URL=${POSTGRES_URL%%:*}://…"
|
||||||
echo ""
|
echo ""
|
||||||
echo " the install root is not written here — the platform derives it as the"
|
echo " the install root is not written here — the platform derives it as the"
|
||||||
|
|||||||
@@ -65,9 +65,6 @@ write_env() {
|
|||||||
|
|
||||||
PORT="${ENV_PORT}"
|
PORT="${ENV_PORT}"
|
||||||
|
|
||||||
# The browser relay listens on its own port, separate from the app.
|
|
||||||
BROWSER_RELAY_PORT="${ENV_BROWSER_RELAY_PORT}"
|
|
||||||
|
|
||||||
POSTGRES_URL="${POSTGRES_URL}"
|
POSTGRES_URL="${POSTGRES_URL}"
|
||||||
|
|
||||||
ENVF
|
ENVF
|
||||||
|
|||||||
+29
-8
@@ -18,7 +18,7 @@ import { cliampWebsocket, cliampAudioWebsocket } from './servers/api/cliamp/rela
|
|||||||
import { desktopWebsocket } from './servers/api/desktop/websocket';
|
import { desktopWebsocket } from './servers/api/desktop/websocket';
|
||||||
import { vaultWebsocket, upgradeVaultWs } from './servers/api/vault/websocket';
|
import { vaultWebsocket, upgradeVaultWs } from './servers/api/vault/websocket';
|
||||||
import officerWeb from './apps/officer-web/index.gen.html';
|
import officerWeb from './apps/officer-web/index.gen.html';
|
||||||
import { startBrowserRelay } from './servers/api/browser/relay';
|
// import { startBrowserRelay } from './servers/api/browser/relay'; // switched off — see below
|
||||||
import { registerSidecar, unregisterSidecar, handleSidecarMessage } from './servers/sidecar-registry';
|
import { registerSidecar, unregisterSidecar, handleSidecarMessage } from './servers/sidecar-registry';
|
||||||
import './servers/api/chat/opencode/sidecar-server'; // subscribe to the opencode sidecar's port report
|
import './servers/api/chat/opencode/sidecar-server'; // subscribe to the opencode sidecar's port report
|
||||||
import type { SidecarRegistration } from './servers/sidecar/registration-protocol';
|
import type { SidecarRegistration } from './servers/sidecar/registration-protocol';
|
||||||
@@ -345,13 +345,34 @@ const server = serve({
|
|||||||
|
|
||||||
console.log(`🚀 Server running at ${server.url}`);
|
console.log(`🚀 Server running at ${server.url}`);
|
||||||
|
|
||||||
const BROWSER_RELAY_PORT = Number(process.env.BROWSER_RELAY_PORT ?? '18792');
|
// ── The browser relay is not started. Deliberate, 2026-08-13. ──
|
||||||
try {
|
//
|
||||||
await startBrowserRelay(BROWSER_RELAY_PORT);
|
// The Chrome extension, its CDP bridge (api/browser/) and the /browser screen all remain on disk: this
|
||||||
console.log(`[browser-relay] listening on port ${BROWSER_RELAY_PORT}`);
|
// is going to be extracted into a plugin, and deleting it would mean writing it again. What is switched
|
||||||
} catch (err) {
|
// off is the second listener and the /api/browser mount (see hono.ts) — the platform serves one port.
|
||||||
console.error('[browser-relay] failed to start:', err instanceof Error ? err.message : err);
|
//
|
||||||
}
|
// It used to read BROWSER_RELAY_PORT, default 18792. The name is recorded here because there is nothing
|
||||||
|
// left to grep for, and whoever does the extraction needs to know what it was called.
|
||||||
|
//
|
||||||
|
// ── Read this before turning it back on ──
|
||||||
|
//
|
||||||
|
// The port is not only configuration. relay-auth.ts derives each extension's token as
|
||||||
|
// HMAC(JWT_SECRET, `officer-browser-relay-v1:${port}:${userId}:${salt}`), so the port is an INPUT TO A
|
||||||
|
// CREDENTIAL. Bringing the relay back on a different number silently invalidates every paired browser,
|
||||||
|
// and the extension reports it as "Relay not reachable" — which SETUP.md attributes to a wrong address,
|
||||||
|
// port or token. Re-pairing means re-copying from Settings → Browser Relay.
|
||||||
|
//
|
||||||
|
// Whatever it comes back as, it cannot be a kernel-assigned port:0 the way the other sidecars are. The
|
||||||
|
// extension is configured by hand and stores the value, so a port that changes each restart breaks the
|
||||||
|
// pairing every restart. It has to be predictable — PORT + 2 is the shape the Anthropic proxy already
|
||||||
|
// uses for the same reason.
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// await startBrowserRelay(BROWSER_RELAY_PORT);
|
||||||
|
// console.log(`[browser-relay] listening on port ${BROWSER_RELAY_PORT}`);
|
||||||
|
// } catch (err) {
|
||||||
|
// console.error('[browser-relay] failed to start:', err instanceof Error ? err.message : err);
|
||||||
|
// }
|
||||||
|
|
||||||
// Initialize queue engine in API server process
|
// Initialize queue engine in API server process
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -327,12 +327,18 @@ export const CAPABILITIES: Capability[] = [
|
|||||||
ws: ['desktop'],
|
ws: ['desktop'],
|
||||||
routes: ['/desktop'],
|
routes: ['/desktop'],
|
||||||
},
|
},
|
||||||
|
// `/browser` — the Chrome-extension relay — is unmounted as of 2026-08-13 and dropped from this claim,
|
||||||
|
// because check 2 of assertCapabilityTotality refuses to boot on a capability claiming a prefix nothing
|
||||||
|
// serves. Put both back together or neither.
|
||||||
|
//
|
||||||
|
// `/scrape` is untouched and is what this capability still covers. It shares nothing with the relay: it
|
||||||
|
// launches its own headless chromium through playwright and never speaks to the extension.
|
||||||
{
|
{
|
||||||
key: 'browser',
|
key: 'browser',
|
||||||
label: 'Browser',
|
label: 'Browser',
|
||||||
description: 'Drives a real browser on the host',
|
description: 'Drives a real browser on the host',
|
||||||
kind: 'execution',
|
kind: 'execution',
|
||||||
api: ['/browser', '/scrape'],
|
api: ['/scrape'],
|
||||||
routes: ['/browser'],
|
routes: ['/browser'],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -50,7 +50,9 @@ import { dockRouter } from './api/dock/dock';
|
|||||||
import { integrationsRouter, googleCallbackHandler } from './api/integrations/integrations';
|
import { integrationsRouter, googleCallbackHandler } from './api/integrations/integrations';
|
||||||
import { queueRouter } from './api/queue/queue';
|
import { queueRouter } from './api/queue/queue';
|
||||||
import { emailRouter } from './api/email/router';
|
import { emailRouter } from './api/email/router';
|
||||||
import { browserRouter } from './api/browser/router';
|
// The browser relay is switched off — see server.tsx. Restoring this mount means restoring the
|
||||||
|
// registry's claim on '/browser' in the same commit, or assertCapabilityTotality refuses to boot.
|
||||||
|
// import { browserRouter } from './api/browser/router';
|
||||||
import { desktopRouter } from './api/desktop/rest';
|
import { desktopRouter } from './api/desktop/rest';
|
||||||
import { bugReportRouter } from './api/bug-report/bug-report';
|
import { bugReportRouter } from './api/bug-report/bug-report';
|
||||||
import { agentStatusRouter } from './api/agent-status/router';
|
import { agentStatusRouter } from './api/agent-status/router';
|
||||||
@@ -221,7 +223,7 @@ const PROTECTED_MOUNTS: [prefix: string, router: ReturnType<typeof createRouter>
|
|||||||
['/integrations', integrationsRouter],
|
['/integrations', integrationsRouter],
|
||||||
['/queue', queueRouter],
|
['/queue', queueRouter],
|
||||||
['/email', emailRouter],
|
['/email', emailRouter],
|
||||||
['/browser', browserRouter],
|
// ['/browser', browserRouter], // switched off — see server.tsx
|
||||||
['/bug-report', bugReportRouter],
|
['/bug-report', bugReportRouter],
|
||||||
['/agent-status', agentStatusRouter],
|
['/agent-status', agentStatusRouter],
|
||||||
['/chat', chatRouter],
|
['/chat', chatRouter],
|
||||||
|
|||||||
Reference in New Issue
Block a user