claim the root paths back, so the setup is the bare Officer URL again

Before the extraction this worked by pasting https://officer.example.com into a
Bitwarden client and logging in normally. The extraction moved the door to
/api/vault/bw, which needs five URLs configured by hand — technically the same
thing, practically nobody's setup.

Declares what it actually needs, now that the platform can express it:

  unauthenticatedRootPaths  /identity, /notifications, /icons, /events — the
                            four a Bitwarden client derives from a bare server
                            URL. None is a name Officer serves, so the path
                            alone decides and no sniffing is involved.

  protocolHeaders           bitwarden-client-name, bitwarden-client-version,
                            device-type — for /api/*, the one namespace both
                            servers claim, where the path cannot separate
                            Vaultwarden's /api/sync from Officer's /api/chat.

`publicRouter` is exported for the platform to mount above the gate. One router
serves all three doors: it strips /api/vault/bw when reached that way, and
otherwise the path is already Vaultwarden's own.

/bw stays. It costs nothing and remains the explicit address for a client that
would rather be told than infer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 14:47:20 +00:00
co-authored by Claude Opus 5
parent 224a525751
commit 4006be0e3b
3 changed files with 40 additions and 0 deletions
+21
View File
@@ -49,4 +49,25 @@ export const manifest: PluginManifest = {
re-checks containment at boot, so it can only ever mean `/api/vault/bw/*`.
*/
unauthenticatedPaths: ['/bw/*'],
/*
The four paths a Bitwarden client derives from a bare server URL, claimed at the ROOT so that URL can
be the bare Officer one. This is what makes the setup "paste https://officer.example.com and log in"
rather than five fields copied into an advanced pane — which is the difference between a feature and
a thing nobody uses.
None of them is a name Officer serves: they belong to Vaultwarden's protocol and to nothing else here,
so the path alone decides and no sniffing is involved.
*/
unauthenticatedRootPaths: ['/identity', '/notifications', '/icons', '/events'],
/*
`/api/*` is the one namespace BOTH servers claim — Vaultwarden has /api/sync, /api/ciphers; Officer
has /api/chat, /api/settings. The path cannot separate them, so the client says who it is: every
Bitwarden client stamps one of these, and older ones `device-type`.
Routing, not authentication. A forged header reaches Vaultwarden, which demands its own credential
exactly as it would have; nothing of Officer's is authorised by it.
*/
protocolHeaders: ['bitwarden-client-name', 'bitwarden-client-version', 'device-type'],
};