tags are how the marketplace is browsed rather than scrolled: a list of them on the left, the plugins that carry one on the right. a closed vocabulary (PLUGIN_TAGS in the platform) because free text becomes homelab, home-lab, Home Lab and selfhosted inside a month, and a filter list of synonyms is worse than no filter at all. declared here rather than curated in the catalogue, so a plugin can say what it is and nobody edits a table when one ships. the marketplace caches it on sync, exactly as it already does for label and summary.
76 lines
3.5 KiB
TypeScript
76 lines
3.5 KiB
TypeScript
import type { PluginManifest } from '@@/plugins/manifest';
|
|
|
|
/**
|
|
* Vault — a reverse-proxy to a self-hosted Vaultwarden, with Officer holding the credential.
|
|
*
|
|
* ── What this plugin is, and what it deliberately is not ──
|
|
*
|
|
* It is NOT a password manager. It stores no vault items, renders no cipher list, and cannot decrypt
|
|
* anything: the vault is end-to-end encrypted and every route here is a dumb pass-through. What it owns is
|
|
* the CREDENTIAL problem — Officer brokers the Vaultwarden token, keeps it server-side, and injects it on
|
|
* the way through, so a device that has an Officer session never holds a vault token at all.
|
|
*
|
|
* It also does not install Vaultwarden. You point it at one you already run. That is the whole
|
|
* configuration surface, and it lives in the database rather than in `.env` — see `db/schema.ts`.
|
|
*
|
|
* ── No `osDependencies` ──
|
|
*
|
|
* Nothing here shells out. The sidecar is a Bun HTTP/WS proxy and the upstream is reached with `fetch`.
|
|
*/
|
|
export const manifest: PluginManifest = {
|
|
publisher: 'officerdev',
|
|
version: '1.0.0',
|
|
platform: '>=1.0.0',
|
|
|
|
label: 'Vault',
|
|
summary: 'Your Vaultwarden, behind an Officer session — passwords for the apps that speak Bitwarden',
|
|
/** passwords, off a cloud manager. */
|
|
tags: ['Security', 'Personal', 'Degoogling'],
|
|
// A lucide glyph rather than artwork: this ships no assets/icon.png, and `KeyRound` is what the app
|
|
// store catalogue already used for it.
|
|
icon: 'KeyRound',
|
|
color: '#175ddc',
|
|
|
|
permissions: [
|
|
{
|
|
key: 'vault',
|
|
label: 'Vault',
|
|
description: 'The password vault, and the Officer-held session that opens it',
|
|
},
|
|
],
|
|
|
|
/*
|
|
The Bitwarden door: served without an Officer session, because a stock Bitwarden client has nowhere to
|
|
put one. It carries its own Vaultwarden bearer token, which Vaultwarden itself judges — see
|
|
`api/public-router.ts` for why this is the same exposure through one door rather than a new one, and
|
|
why it is temporary.
|
|
|
|
ONE prefix, not four. A Bitwarden client is given `https://<officer>/api/vault/bw` as its server URL
|
|
and derives `/api`, `/identity`, `/icons` and `/notifications` beneath it, so everything the protocol
|
|
needs sits under a single declared path. The platform resolves this against the plugin's own mount and
|
|
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'],
|
|
};
|