calendar and contacts are core again, with radicale as a plain sidecar
Reverses the 2026-08-13 decision to make caldav an app-store entry. Three reasons it does not belong there. It is not an integration. Photos-via-Immich or torrents-via-Transmission assume you already run the thing; a calendar assumes nothing, and a personal server without one is missing a baseline rather than an optional app. It needs TWO top-level routes, /calendar and /contacts, and a plugin gets one — derived from its appName, which is what makes it structurally impossible for a plugin to shadow a core route. Nobody thinks "I'll open my dav", so collapsing them under /dav was the wrong trade, and weakening the namespace guarantee to allow two was a worse one. As core routes the question does not arise. And it never actually needed Docker. Radicale is a ~2MB Python package; sidecar/caldav/radicale.ts already spawns it directly on loopback with http_x_remote_user. The container came from the app store making everything a container, not from anything Radicale requires. So: the routers are mounted again (/caldav for the UI's JSON door, /dav for app passwords, the top-level /dav sync door and the two .well-known redirects), officer-caldav joins CORE_PROCESSES, radicale joins the machine-setup package list for all four package managers, and the catalogue entry is gone. members.test.ts re-anchored a fifth time — caldav was one of two `accounts` services and memos is now the last. Noted in the test that the sixth time should be a literal fixture instead. Verified: assertPermissionTotality accepts /caldav and /dav, tsgo clean, 1085 pass. NOT yet verified against a real client — radicale is not installed on this machine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+8
-8
@@ -26,12 +26,12 @@ import { isPluginPublicPath, pluginForProtocolHeaders } from './plugins/public-p
|
||||
import { agentHandoffRouter } from './api/agent-handoff/router';
|
||||
// import { walletRouter } from './api/wallet/router';
|
||||
import { terminalRouter } from './api/terminal/sidecar-server';
|
||||
// import { caldavRouter } from './api/dav/sidecar-server';
|
||||
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 { systemMonitorRouter } from './api/system-monitor/system-monitor';
|
||||
@@ -143,8 +143,8 @@ const PROTECTED_MOUNTS: [prefix: string, router: ReturnType<typeof createRouter>
|
||||
// ['/gitea', giteaRouter], // plugin — switched off 2026-08-13
|
||||
['/app-store', appStoreRouter],
|
||||
['/plugins', pluginsRouter],
|
||||
// ['/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
|
||||
['/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], // plugin — switched off 2026-08-13
|
||||
// ['/photos', photosRouter], // plugin — switched off 2026-08-13
|
||||
// ['/wallet', walletRouter], // plugin — switched off 2026-08-13
|
||||
@@ -274,7 +274,7 @@ export function buildHonoApp(plugins: MountedPlugin[] = []): Hono<{ Variables: H
|
||||
});
|
||||
});
|
||||
|
||||
// app.route('/dav', davSyncRouter); // plugin — switched off 2026-08-13
|
||||
app.route('/dav', davSyncRouter);
|
||||
|
||||
// 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
|
||||
@@ -284,8 +284,8 @@ export function buildHonoApp(plugins: MountedPlugin[] = []): Hono<{ Variables: H
|
||||
// `.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.
|
||||
// app.all('/.well-known/caldav', (ctx) => ctx.redirect('/dav/', 301));
|
||||
// app.all('/.well-known/carddav', (ctx) => ctx.redirect('/dav/', 301));
|
||||
app.all('/.well-known/caldav', (ctx) => ctx.redirect('/dav/', 301));
|
||||
app.all('/.well-known/carddav', (ctx) => ctx.redirect('/dav/', 301));
|
||||
|
||||
const protectedRouter = createRouter();
|
||||
protectedRouter.use(bodyParser());
|
||||
|
||||
Reference in New Issue
Block a user