Embed the Composio key in the app bundle instead of shipping cred.txt

Root cause of the recurring 401: a revoked key was hardcoded in
startup-recovery.ts and onboarding-service.ts and reseeded into
composio_settings on every launch, and the interim fix shipped cred.txt
as a visible file under resources/.

- electron.vite.config.ts reads the gitignored cred.txt at build time
  and injects the key into the main-process bundle as
  __COMPOSIO_API_KEY__; no credential file is packaged and the key is
  not user-visible or configurable. The renderer bundle never sees it.
- Delete both hardcoded-key seeders; migration 046 purges stored
  api_key and never-read toolkits rows while keeping entity_id and
  auth_config state.
- Stop shipping composio_settings in the renderer snapshot.
This commit is contained in:
ZacharyZhang-NY committed 2026-07-07 01:40:19 -04:00
1 parent 2d43950123
commit a2de132083
16 files changed
+105 -181

No files matched your search

+2 -2
View File
@@ -329,9 +329,9 @@ export function registerIpcHandlers(deps: IpcDeps): void {
const composioConnections = new ComposioConnectionService(deps.connection.db);
const connectorGrants = new ConnectorGrantsService(deps.connection.db);
ipcMain.handle(IPC_CHANNELS.listComposioToolkits, async (_event, input) => {
ipcMain.handle(IPC_CHANNELS.listComposioToolkits, async () => {
try {
return ok(await composioConnections.listToolkits(input.orgId));
return ok(await composioConnections.listToolkits());
} catch (error) {
return fail("composio_toolkits_failed", error instanceof Error ? error.message : String(error));
}
-21
View File
@@ -2,8 +2,6 @@ import type { DatabaseSync } from "node:sqlite";
import { AuditService } from "../services/audit-service";
const INTERRUPTED_MESSAGE = "Interrupted by application shutdown before completion.";
const COMPOSIO_API_KEY = "***REMOVED***";
const DEFAULT_COMPOSIO_TOOLKITS = ["github", "gmail", "slack", "notion", "linear", "google_calendar"];
/**
* A single app instance executes runs in-process, so any task_run still
@@ -38,23 +36,4 @@ export function recoverInterruptedWork(db: DatabaseSync): void {
where state in ('pending', 'streaming')
and (execution_mode = 'foreground' or external_response_id is null)
`).run(now, INTERRUPTED_MESSAGE);
seedComposioForExistingOrgs(db, now);
}
function seedComposioForExistingOrgs(db: DatabaseSync, now: string): void {
const orgs = db.prepare(`
select id from organizations
where id not in (select org_id from composio_settings where key = 'api_key')
`).all() as Array<{ id: string }>;
if (orgs.length === 0) return;
const upsert = db.prepare(`
insert into composio_settings(org_id, key, value, updated_at) values (?, ?, ?, ?)
on conflict(org_id, key) do update set value = excluded.value, updated_at = excluded.updated_at
`);
for (const org of orgs) {
upsert.run(org.id, "api_key", COMPOSIO_API_KEY, now);
upsert.run(org.id, "toolkits", JSON.stringify(DEFAULT_COMPOSIO_TOOLKITS), now);
upsert.run(org.id, "entity_id", org.id, now);
}
}