diff --git a/src/databases/officer_db/src/schema.ts b/src/databases/officer_db/src/schema.ts index 7d76a4f0..bc3b9264 100644 --- a/src/databases/officer_db/src/schema.ts +++ b/src/databases/officer_db/src/schema.ts @@ -52,7 +52,7 @@ export * from './service-connections/schema'; // service_connections // export * from './jellyfin/schema'; // jellyfin_servers officer-jellyfin // export * from './invoiceshelf/schema'; // invoiceshelf_accounts officer-invoiceshelf // export * from './soulseek/schema'; // soulseek_favorites, _browse_snapshots, _browse_dirs -// export * from './vault/schema'; // vault_tokens, vault_unlock_keys officer-vault +export * from './vault/schema'; // vault_tokens, vault_unlock_keys officer-vault // export * from './wallet/schema'; // wallet_wallets, _labels, _frozen_utxos, _chain_cache // ── Plugin tables are NOT here ─────────────────────────────────────────────────────────────────── diff --git a/src/server.tsx b/src/server.tsx index e5455ab7..71b15ec7 100644 --- a/src/server.tsx +++ b/src/server.tsx @@ -18,7 +18,7 @@ import { chatWebsocket } from './servers/api/chat/websocket'; import { taskRunnerWebsocket } from './servers/api/tasks/task-executor'; import { pipelineWebsocket } from './servers/api/tasks/pipeline-executor'; // 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 { startBrowserRelay } from './servers/api/browser/relay'; // switched off — see below import { registerSidecar, unregisterSidecar, handleSidecarMessage } from './servers/sidecar-registry'; @@ -144,7 +144,7 @@ const handlers: Record = { 'task-runner': taskRunnerWebsocket, pipeline: pipelineWebsocket, // desktop: desktopWebsocket, - // vault: vaultWebsocket, + vault: vaultWebsocket, sidecar: sidecarWebsocket, }; @@ -407,10 +407,10 @@ const server = serve({ }, // Vaultwarden notifications hub: upgrade the WebSocket here (proxied to upstream by vaultWebsocket); // everything else on this path (SignalR long-poll negotiate/poll) falls through to the HTTP proxy. - // '/api/vault/notifications/*': (req, server) => { - // if (req.headers.get('upgrade') === 'websocket') return upgradeVaultWs(req, server); - // return honoServer.fetch(req, server); - // }, + '/api/vault/notifications/*': (req, server) => { + if (req.headers.get('upgrade') === 'websocket') return upgradeVaultWs(req, server); + return honoServer.fetch(req, server); + }, // A dev server's HMR socket, framed by the Preview panel. Must be in the route table rather than // behind the `/api/*` catch-all: Bun matches this table first, and an upgrade that reaches Hono is // an ordinary GET by then. @@ -441,14 +441,14 @@ const server = serve({ // entry for the same reason /dav does: only the paths listed here reach hono, and anything else // falls through to the SPA — which answers 200 with the React shell, so a missing line here looks // like a working endpoint returning nonsense rather than a 404. - // '/vaultwarden/*': honoServer.fetch, + '/vaultwarden/*': honoServer.fetch, // The same proxy at the root, so the extension needs only the bare Officer URL. These four prefixes // are Vaultwarden's alone — nothing in Officer answers on them — so routing them here costs nothing. // `/api/*` already reaches hono below, where a Bitwarden client header diverts it. - // '/identity/*': honoServer.fetch, - // '/notifications/*': honoServer.fetch, - // '/icons/*': honoServer.fetch, - // '/events/*': honoServer.fetch, + '/identity/*': honoServer.fetch, + '/notifications/*': honoServer.fetch, + '/icons/*': honoServer.fetch, + '/events/*': honoServer.fetch, ...spaRoutes, '/api': honoServer.fetch, // A CLOSURE, deliberately, and not the bound `honoServer.fetch`. diff --git a/src/servers/hono.ts b/src/servers/hono.ts index 34d60027..2ea82c50 100644 --- a/src/servers/hono.ts +++ b/src/servers/hono.ts @@ -22,8 +22,8 @@ import { dashboardsRouter } from './api/dashboards'; import { router as fileBrowserRouter } from './api/file-browser/router'; import { devProxyRouter } from './api/dev-proxy/router'; import { pluginsRouter } from './api/plugins/router'; -// import { vaultRouter } from './api/vault/router'; -// import { publicVaultRouter, VAULT_ONLY_PREFIXES, isBitwardenClient } from './api/vault/public-router'; +import { vaultRouter } from './api/vault/router'; +import { publicVaultRouter, VAULT_ONLY_PREFIXES, isBitwardenClient } from './api/vault/public-router'; import { agentHandoffRouter } from './api/agent-handoff/router'; // import { slskdRouter } from './api/slskd/router'; // import { transmissionRouter } from './api/transmission/router'; @@ -44,7 +44,7 @@ import { systemMonitorRouter } from './api/system-monitor/system-monitor'; import { activityRouter } from './api/activity/router'; // Vault still hand-rolls its port capture, so it keeps a side-effect import; every other HTTP sidecar // registers its listener when createSidecarProxy runs inside the router this file already imports. -// import './api/vault/sidecar-server'; // side-effect: capture the officer-vault reverse-proxy port +import './api/vault/sidecar-server'; // side-effect: capture the officer-vault reverse-proxy port import { dockRouter } from './api/dock/dock'; import { integrationsRouter, googleCallbackHandler } from './api/integrations/integrations'; import { queueRouter } from './api/queue/queue'; @@ -175,7 +175,7 @@ export const UNPROTECTED_API_PREFIXES: string[] = [ '/landing-page-data', '/themes', '/waitlist', - // '/vault', // switched off with the Vaultwarden mounts — see above + '/vault', '/sidecar', '/agent-handoff', '/dev-proxy', @@ -205,7 +205,7 @@ export function buildHonoApp(plugins: MountedPlugin[] = []): Hono<{ Variables: H // Vaultwarden reverse-proxy — mounted TOP-LEVEL (not under protectedRouter): the Bitwarden client // carries its own bearer token, not a platform session JWT, so userMiddleware would 401 it. The // notifications WebSocket is upgraded at the serve level (server.tsx). - // app.route('/api/vault', vaultRouter); // switched off 2026-08-13 — Vaultwarden is a plugin + app.route('/api/vault', vaultRouter); // The same Vaultwarden, with NO Officer authentication, so the Bitwarden browser extension can point at // this host instead of at a second public hostname for Vaultwarden. Deliberately its own mount rather @@ -213,7 +213,7 @@ export function buildHonoApp(plugins: MountedPlugin[] = []): Hono<{ Variables: H // Authorization header for a server-held token, and blending the two would put an unauthenticated branch // inside the authenticated path. Temporary — see public-router.ts for what replaces it and why leaving it // open is not a new exposure. - // app.route('/vaultwarden', publicVaultRouter); // switched off with the above + app.route('/vaultwarden', publicVaultRouter); // …and at the ROOT, so the extension can be pointed at the bare Officer URL with no path at all. // @@ -221,12 +221,12 @@ export function buildHonoApp(plugins: MountedPlugin[] = []): Hono<{ Variables: H // for a Bitwarden client. It is deliberately narrow: the four prefixes below belong to Vaultwarden and // to nothing else here, and `/api/*` is diverted ONLY when the request carries a Bitwarden client // header. An ordinary Officer request never matches, so nothing that worked before changes. - // for (const prefix of VAULT_ONLY_PREFIXES) app.route(prefix, publicVaultRouter); - // - // app.use('/api/*', async (ctx, next) => { - // if (!isBitwardenClient(ctx.req.raw.headers)) return next(); - // return publicVaultRouter.fetch(ctx.req.raw, ctx.env); - // }); + for (const prefix of VAULT_ONLY_PREFIXES) app.route(prefix, publicVaultRouter); + + app.use('/api/*', async (ctx, next) => { + if (!isBitwardenClient(ctx.req.raw.headers)) return next(); + return publicVaultRouter.fetch(ctx.req.raw, ctx.env); + }); app.get('/api/integrations/google/callback', googleCallbackHandler); // Agent-to-agent handoff — mounted TOP-LEVEL for the same reason the vault is: the caller is a Claude