feat: add cross-plugin secret references gated by the provider's RBAC permission

This commit is contained in:
LukeGus committed 2026-09-21 17:09:28 -05:00
1 parent 2d6c1cdecb
commit 9db66d5780
13 files changed
+1782 -10

No files matched your search

+22
View File
@@ -54,6 +54,28 @@ export async function activate(ctx) {
routesModule = await loadRelative("routes");
routesModule.startAiService();
// Offered by reference, not copied: this hands over a resolver, so the key
// stays ours. Rotating or deleting the provider takes effect on the next
// read by any borrower, and deactivate() below withdraws the offer entirely.
// The resolve is per-user because the keys are stored per-user and encrypted
// under that user's data key, so a borrowed read returns the acting user's
// own default key, never someone else's.
ctx.secrets.offer("api-key", async (userId) => {
const { createCurrentAiRepository } = await loadRelative(
"../../../backend/backend/database/repositories/factory",
);
const repository = createCurrentAiRepository();
const providers = await repository.listProviders(userId);
const preferred = providers.find((provider) => provider.apiKeyPrefix);
if (!preferred) return null;
const withSecret = await repository.findProviderWithSecret(
preferred.id,
userId,
);
return withSecret?.apiKey ?? null;
});
ctx.log.info("AI assistant router registered at /ai");
}
+9 -1
View File
@@ -31,7 +31,8 @@
"ai.use",
"ai.manage_providers",
"ai.apply_proposals",
"ai.services.use"
"ai.services.use",
"ai.secrets.share"
],
"defaultForRole": {
"admin": ["ai.services.use"]
@@ -49,5 +50,12 @@
}
]
},
"providesSecret": [
{
"key": "api-key",
"permission": "ai.secrets.share",
"descriptionKey": "ai.secrets.shareApiKey"
}
],
"sidecars": []
}