Findings
Admin users and config
- "Disabled" is enforced inside the credential verifiers (
verifyApiKey,verifySession,verifyAccessToken,verifyCurlToken) rather than at each of their call sites, because every call site goes through one of them: a new consumer cannot forget the check. Credential issuance is checked separately, because it does not pass through a verifier: both SSO callbacks and both OAuth/tokengrants refuse a disabled user. Disabling also deletes the user's refresh tokens, so a token they already hold cannot mint a new access token. - A user with no row is not "disabled":
isUserDisabledis true only for a row withdisabled_atset, so tokens signed for ids the table has never seen keep working as before. disabled_atis a nullable timestamp, not a BOOLEAN, so it avoids the PostgreSQL 1/0 trap, and it records when the account was disabled.- An admin cannot disable themselves or any email on
ADMIN_EMAILS. Admins come from the env var, so this also prevents locking every admin out. Registrytakes an injectedisDisabledpredicate and filtersgetTool,getIntegration,listTools,listIntegrations,listToolsByIntegrationandsearchTools. About 25 call sites read those, so filtering there beats patching each. The maps are private, so nothing can bypass it.listAllIntegrationsis the one unfiltered view, for the admin Config tab.- Instance settings are read from an in-memory snapshot (the registry and the
request path are synchronous), replaced whole on every write and by a poll
(
INSTANCE_SETTINGS_POLL_SECONDS, default 5). Other workers and pods lag by at most that interval. A corrupt stored value falls back to the default for its key; it never throws and never disables anything. disabled_integrationsis one JSON list updated read-modify-write, so two admins toggling different integrations within the poll interval through different workers can lose one update (last write wins). Admin config changes are rare and human-driven; per-integration rows would remove the race if it ever matters.- The custom-app policy check runs before the cached index lookup in
ensureIndex, so a policy change does not wait for the cache to expire, andPOST /api/custom-appsrefuses before it does any network discovery. - A bodyless
POSTfrom the portal must not sendContent-Type: application/json(the 2026-06-10 finding), so the disable, enable and revoke calls use the auth-only header helper. - In a vitest
beforeEach,() => mock.mockReset()returns the mock and vitest runs it as a teardown call; use a block body (the 2026-09-28 note).