Files
platform/src/servers/permissions/core-collision.test.ts
T
pastilhasandClaude Opus 5 ee321f49fd refuse a plugin install whose permission key the platform still claims
Three times in one day — photos, soulseek, jellyfin — the same failure on the
same machine: install reports success, the dock tile appears, the frontend
rebuilds with the route in it, and every request 404s. The plugin never mounted,
because setPluginPermissions refused its key as already core.

That refusal was correct and useless. It happens at mount time, after the
install has reported success, and its only trace is one line on stderr that
nobody is reading. The cause is always the same: the key was a CORE permission
until the extraction commit, so a server whose platform predates that commit
still owns it. The fix is always git pull, and nothing in the product said so.

installPlugin now checks the manifest's keys against the core registry before
anything is written, and refuses with a message naming the colliding keys and
telling the operator to pull the platform and restart. Placed with the other
pre-flight checks, so it changes nothing that would need undoing.

corePermissionCollisions is exported from the registry rather than reimplemented
in install.ts — the core key set has one owner.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 03:33:00 +00:00

31 lines
1.5 KiB
TypeScript

import { describe, expect, test } from 'bun:test';
import { corePermissionCollisions } from './registry';
// The failure this exists to prevent, in one sentence: a plugin whose permission key the platform still
// claims installs "successfully", shows a dock tile, rebuilds the frontend with its route — and 404s on
// every request, because `setPluginPermissions` refused the key at mount time and said so only on stderr.
//
// It caught photos, soulseek and jellyfin on the same machine inside a day. Each had been a CORE
// permission until its extraction commit, so any server whose platform predates that commit still owns
// the key. The remedy is always `git pull`, and nothing in the product ever said so.
describe('core permission collisions', () => {
test('names a key the platform still claims', () => {
// 'chat' is core and not deniable — the clearest case of something a plugin must never redefine.
expect(corePermissionCollisions(['chat'])).toEqual(['chat']);
});
test('an ordinary plugin key collides with nothing', () => {
expect(corePermissionCollisions(['soulseek', 'jellyfin', 'photos'])).toEqual([]);
});
test('reports every collision, not just the first — an install should fail once, naming all of them', () => {
const found = corePermissionCollisions(['chat', 'soulseek', 'terminal']);
expect(found.sort()).toEqual(['chat', 'terminal']);
});
test('nothing declared collides with nothing', () => {
expect(corePermissionCollisions([])).toEqual([]);
});
});