From ffca309a770aa8f29282266254d9d966c012b103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Thu, 13 Aug 2026 02:13:16 +0000 Subject: [PATCH] switch off every plugin router, pending extraction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Twelve capabilities unmounted: gitea, music, photos, jellyfin, memos, calendar, email, notify, transmission, soulseek, invoices, wallet. Mounts commented in place, implementation untouched on disk, same as vault and the browser relay. Each needed its capability's `api` claim commented in the same change. assertCapabilityTotality check 2 refuses to boot on a capability claiming a prefix nothing mounts, so unmounting alone would have stopped the server starting — the opposite of what happened with vault, which is exempt. music also claims two websockets. Its `ws: ['cliamp', 'cliamp-audio']` claim, the two handlers in server.tsx and the provider union entries all had to move together: check 3 fails on a served socket nothing claims, check 4 on a claimed socket nothing serves. calendar was the awkward one and the mount list was not where it lived. Four of its six doors are not in the routes table: honoServer.route('/dav', davSyncRouter) the sync door, top-level, not /api honoServer.all('/.well-known/caldav') RFC 6764 autodiscovery honoServer.all('/.well-known/carddav') the same for contacts four routes in server.tsx /dav, /dav/*, and both well-knowns and the two that ARE in the table carry trailing comments, which is why the first pass silently missed them and left /api/caldav and /api/dav mounted with their claim gone — exactly the boot failure this commit is about. /vpn deliberately stays. It reads as a plugin (kind 'app') but it is OffTail enrollment forwarding to officer-headscale, which is core because the tailnet is the perimeter. Verified by simulating assertCapabilityTotality against the post-change files rather than trusting the edits: parse hono's live mounts, the registry's live claims, server.tsx's live ws providers and totality's two exempt lists, then run all four checks. It reported the two stale caldav mounts, which is how they were found. Zero problems now, 34 live mounts, all core. Co-Authored-By: Claude Opus 5 (1M context) --- src/server.tsx | 16 ++++---- src/servers/capabilities/registry.ts | 38 ++++++++++++------ src/servers/hono.ts | 60 ++++++++++++++-------------- 3 files changed, 63 insertions(+), 51 deletions(-) diff --git a/src/server.tsx b/src/server.tsx index 89c4ca9c..b44c1c91 100644 --- a/src/server.tsx +++ b/src/server.tsx @@ -41,8 +41,8 @@ type WSData = { | 'chat' | 'task-runner' | 'pipeline' - | 'cliamp' - | 'cliamp-audio' + // | 'cliamp' + // | 'cliamp-audio' | 'desktop' // | 'vault' | 'sidecar'; @@ -135,8 +135,8 @@ const handlers: Record = { chat: chatWebsocket, 'task-runner': taskRunnerWebsocket, pipeline: pipelineWebsocket, - cliamp: cliampWebsocket, - 'cliamp-audio': cliampAudioWebsocket, + // cliamp: cliampWebsocket, + // 'cliamp-audio': cliampAudioWebsocket, desktop: desktopWebsocket, // vault: vaultWebsocket, sidecar: sidecarWebsocket, @@ -300,10 +300,10 @@ const server = serve({ // fixed, spec-defined paths — `/.well-known/caldav` unauthenticated, before they hold any // credential at all. They need naming explicitly here or the `/*` SPA fallback below swallows them // and the phone gets an HTML page where it expected a redirect. - '/.well-known/caldav': honoServer.fetch, - '/.well-known/carddav': honoServer.fetch, - '/dav': honoServer.fetch, - '/dav/*': honoServer.fetch, + // '/.well-known/caldav': honoServer.fetch, + // '/.well-known/carddav': honoServer.fetch, + // '/dav': honoServer.fetch, + // '/dav/*': honoServer.fetch, // Vaultwarden for third-party Bitwarden clients, unauthenticated at Officer's layer. Needs its own // 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 diff --git a/src/servers/capabilities/registry.ts b/src/servers/capabilities/registry.ts index 8852f14f..3e91b364 100644 --- a/src/servers/capabilities/registry.ts +++ b/src/servers/capabilities/registry.ts @@ -126,7 +126,8 @@ export const CAPABILITIES: Capability[] = [ label: 'Gitea', description: 'Repositories, issues, pull requests and notifications from your own Gitea account', kind: 'app', - api: ['/gitea'], + // api: ['/gitea'], // plugin — switched off 2026-08-13 + api: [], routes: ['/gitea'], // Every Gitea call is already scoped to the caller's own personal access token — the sidecar resolves // the token from the caller's row and the instance from the owner's, so a member holding `read` still @@ -143,8 +144,9 @@ export const CAPABILITIES: Capability[] = [ label: 'Music', description: 'The music library, playback, and your own favourites and playlists', kind: 'app', - api: ['/music'], - ws: ['cliamp', 'cliamp-audio'], + // api: ['/music'], // plugin — switched off 2026-08-13 + api: [], + // ws: ['cliamp', 'cliamp-audio'], // plugin — switched off 2026-08-13 routes: ['/music'], // Already per-caller in the sidecar contract (X-Officer-User), which is what makes them safe to write // at read level. The library itself — scanning, tags, file moves — is not, and is not listed. @@ -155,7 +157,8 @@ export const CAPABILITIES: Capability[] = [ label: 'Photos', description: 'Browse the photo library', kind: 'app', - api: ['/photos'], + // api: ['/photos'], // plugin — switched off 2026-08-13 + api: [], routes: ['/photos'], }, { @@ -163,7 +166,8 @@ export const CAPABILITIES: Capability[] = [ label: 'Video', description: 'Browse and play the Jellyfin library', kind: 'app', - api: ['/jellyfin'], + // api: ['/jellyfin'], // plugin — switched off 2026-08-13 + api: [], routes: ['/jellyfin'], }, { @@ -171,7 +175,8 @@ export const CAPABILITIES: Capability[] = [ label: 'Memos', description: 'Notes', kind: 'app', - api: ['/memos'], + // api: ['/memos'], // plugin — switched off 2026-08-13 + api: [], routes: ['/memos'], }, { @@ -179,7 +184,8 @@ export const CAPABILITIES: Capability[] = [ label: 'Calendar and contacts', description: 'Calendars, contacts, and the app passwords that sync them to a phone', kind: 'app', - api: ['/caldav', '/dav'], + // api: ['/caldav', '/dav'], // plugin — switched off 2026-08-13 + api: [], routes: ['/calendar', '/contacts'], // App passwords are minted for and revoked by their own owner; `/dav` holds nothing shared. personal: ['/'], @@ -189,7 +195,8 @@ export const CAPABILITIES: Capability[] = [ label: 'Email', description: 'Mail accounts and messages', kind: 'app', - api: ['/email'], + // api: ['/email'], // plugin — switched off 2026-08-13 + api: [], routes: ['/email'], }, { @@ -197,7 +204,8 @@ export const CAPABILITIES: Capability[] = [ label: 'Notifications', description: 'Push notifications to your devices', kind: 'app', - api: ['/notify'], + // api: ['/notify'], // plugin — switched off 2026-08-13 + api: [], // Device registration is the caller's own — a phone subscribing to its own push channel. personal: ['/devices', '/subscriptions'], }, @@ -206,7 +214,8 @@ export const CAPABILITIES: Capability[] = [ label: 'Transmission', description: 'Torrent downloads', kind: 'app', - api: ['/transmission'], + // api: ['/transmission'], // plugin — switched off 2026-08-13 + api: [], routes: ['/transmission'], }, { @@ -214,7 +223,8 @@ export const CAPABILITIES: Capability[] = [ label: 'Soulseek', description: 'Search and download from the Soulseek network', kind: 'app', - api: ['/slskd'], + // api: ['/slskd'], // plugin — switched off 2026-08-13 + api: [], routes: ['/soulseek'], }, { @@ -222,7 +232,8 @@ export const CAPABILITIES: Capability[] = [ label: 'Invoices', description: 'InvoiceShelf books', kind: 'app', - api: ['/invoiceshelf'], + // api: ['/invoiceshelf'], // plugin — switched off 2026-08-13 + api: [], routes: ['/invoices'], }, { @@ -387,7 +398,8 @@ export const CAPABILITIES: Capability[] = [ label: 'Wallet', description: "The server owner's bitcoin", kind: 'admin', - api: ['/wallet'], + // api: ['/wallet'], // plugin — switched off 2026-08-13 + api: [], routes: ['/wallet'], }, ]; diff --git a/src/servers/hono.ts b/src/servers/hono.ts index 887223d0..df6a5d50 100644 --- a/src/servers/hono.ts +++ b/src/servers/hono.ts @@ -19,27 +19,27 @@ import { uploadRouter } from './api/upload/upload'; import { settingsRouter } from './api/settings/settings'; import { dashboardsRouter } from './api/dashboards'; import { router as fileBrowserRouter } from './api/file-browser/router'; -import { musicRouter } from './api/music/router'; +// import { musicRouter } from './api/music/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 { slskdRouter } from './api/slskd/router'; import { headscaleRouter } from './api/headscale/router'; -import { transmissionRouter } from './api/transmission/router'; -import { invoiceshelfRouter } from './api/invoiceshelf/router'; -import { jellyfinRouter } from './api/jellyfin/router'; -import { photosRouter } from './api/photos/router'; -import { walletRouter } from './api/wallet/router'; +// import { transmissionRouter } from './api/transmission/router'; +// import { invoiceshelfRouter } from './api/invoiceshelf/router'; +// import { jellyfinRouter } from './api/jellyfin/router'; +// import { photosRouter } from './api/photos/router'; +// import { walletRouter } from './api/wallet/router'; import { vpnRouter } from './api/vpn/router'; import { terminalRouter } from './api/terminal/sidecar-server'; -import { caldavRouter } from './api/dav/sidecar-server'; -import { memosRouter } from './api/memos/router'; -import { giteaRouter } from './api/gitea/router'; +// import { caldavRouter } from './api/dav/sidecar-server'; +// import { memosRouter } from './api/memos/router'; +// import { giteaRouter } from './api/gitea/router'; import { appStoreRouter } from './api/app-store/router'; -import { davSyncRouter } from './api/dav/sync-router'; -import { davRouter } from './api/dav/router'; +// import { davSyncRouter } from './api/dav/sync-router'; +// import { davRouter } from './api/dav/router'; import { claimIosProfile } from './api/dav/ios-profile'; -import { notifyRouter } from './api/notify/router'; +// import { notifyRouter } from './api/notify/router'; 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 @@ -48,7 +48,7 @@ import { activityRouter } from './api/activity/router'; import { dockRouter } from './api/dock/dock'; import { integrationsRouter, googleCallbackHandler } from './api/integrations/integrations'; import { queueRouter } from './api/queue/queue'; -import { emailRouter } from './api/email/router'; +// import { emailRouter } from './api/email/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'; @@ -158,7 +158,7 @@ honoServer.get('/dav/provision/:file', (ctx) => { }); }); -honoServer.route('/dav', davSyncRouter); +// honoServer.route('/dav', davSyncRouter); // plugin — switched off 2026-08-13 // Autodiscovery. This is most of what makes adding an account on a phone feel transparent instead of // fiddly: the client is given a bare domain and probes these paths UNAUTHENTICATED before it has any @@ -168,8 +168,8 @@ honoServer.route('/dav', davSyncRouter); // `.all`, not `.get`: RFC 6764 §6 has the client probe the well-known URI with the method it actually // wants to use, and iOS sends PROPFIND, not GET. Registered as GET-only these answered 404 to every real // client while looking perfectly healthy in a browser. -honoServer.all('/.well-known/caldav', (ctx) => ctx.redirect('/dav/', 301)); -honoServer.all('/.well-known/carddav', (ctx) => ctx.redirect('/dav/', 301)); +// honoServer.all('/.well-known/caldav', (ctx) => ctx.redirect('/dav/', 301)); +// honoServer.all('/.well-known/carddav', (ctx) => ctx.redirect('/dav/', 301)); const protectedRouter = createRouter(); protectedRouter.use(bodyParser()); @@ -199,28 +199,28 @@ const PROTECTED_MOUNTS: [prefix: string, router: ReturnType ['/api-keys', apiKeysRouter], // your own keys; the `account` core capability covers it ['/dashboards', dashboardsRouter], ['/file-browser', fileBrowserRouter], - ['/music', musicRouter], - ['/slskd', slskdRouter], + // ['/music', musicRouter], // plugin — switched off 2026-08-13 + // ['/slskd', slskdRouter], // plugin — switched off 2026-08-13 ['/terminal', terminalRouter], - ['/memos', memosRouter], - ['/gitea', giteaRouter], + // ['/memos', memosRouter], // plugin — switched off 2026-08-13 + // ['/gitea', giteaRouter], // plugin — switched off 2026-08-13 ['/app-store', appStoreRouter], - ['/caldav', caldavRouter], // the JSON door for Officer's own calendar/contacts UI - ['/dav', davRouter], // app-password management (the sync door is /dav, top-level) - ['/notify', notifyRouter], + // ['/caldav', caldavRouter], // the JSON door for Officer's own calendar/contacts UI — plugin, switched off 2026-08-13 + // ['/dav', davRouter], // app-password management (the sync door is /dav, top-level) — plugin, switched off + // ['/notify', notifyRouter], // plugin — switched off 2026-08-13 ['/headscale', headscaleRouter], - ['/transmission', transmissionRouter], - ['/invoiceshelf', invoiceshelfRouter], - ['/jellyfin', jellyfinRouter], - ['/photos', photosRouter], - ['/wallet', walletRouter], + // ['/transmission', transmissionRouter], // plugin — switched off 2026-08-13 + // ['/invoiceshelf', invoiceshelfRouter], // plugin — switched off 2026-08-13 + // ['/jellyfin', jellyfinRouter], // plugin — switched off 2026-08-13 + // ['/photos', photosRouter], // plugin — switched off 2026-08-13 + // ['/wallet', walletRouter], // plugin — switched off 2026-08-13 ['/vpn', vpnRouter], ['/system-monitor', systemMonitorRouter], ['/activity', activityRouter], ['/dock', dockRouter], ['/integrations', integrationsRouter], ['/queue', queueRouter], - ['/email', emailRouter], + // ['/email', emailRouter], // plugin — switched off 2026-08-13 // ['/browser', browserRouter], // switched off — see server.tsx ['/bug-report', bugReportRouter], ['/agent-status', agentStatusRouter],