/** * pending-secrets — credential values authored in Studio BEFORE the agent they * belong to exists in the cloud. * * WHY this exists: Sapiom stores secrets per cloud definition * (`POST /v1/workflows/definitions/:id/secrets`), so until a project is linked * there is no id to store anything against. That made "set a credential" a * thing you could only do after deploying — the deploy you needed the * credential for. This store closes that gap: values are held locally, injected * into local runs, and pushed to the vault the moment a definition id exists. * * WHERE, and why not a `.env`: the obvious move is to write `.env` into the * agent's project directory. Two things kill it. Nothing would read it — * there is no `dotenv` anywhere in `@sapiom/agent-core`, `@sapiom/agent` or the * CLI — and it would put plaintext credentials inside a git-tracked tree, one * `git add -A` away from being published. So values live under the harness * state root, which is outside every project, and reach a local run through the * run-local child's environment instead (server/actions.ts). * * SECURITY POSTURE. This file holds plaintext, so it follows the same rules as * `cli/machine-id.ts`: directory 0700, file 0600 on create, and permissions * re-asserted on every write so a file created by a looser earlier version does * not stay world-readable. Values are never serialized to the browser — the * routes expose {@link PendingSecretsStore.names} only, and `values`/`entries` * exist for the two server-side consumers (run-local injection, vault flush). * * DURABILITY. Writes are atomic (temp file in the same directory, then rename) * so a crash mid-write leaves the temp file rather than a truncated store — * the pattern `core/workflow-registry.ts` uses for the same reason. Reads are * served from an in-memory snapshot loaded once at boot, because the run-local * route spawns its child synchronously and must not become async to ask a disk * for four strings. */ export interface PendingSecretsStore { /** * The names held for a project, sorted. This is the ONLY accessor whose * result may reach the browser: a name is listable, a value never is. */ names(projectPath: string): string[]; /** * Name→value for injection into a local run's environment. Server-side only. * Returns a fresh object so a caller cannot mutate the store through it. */ values(projectPath: string): Record; /** Name/value pairs for the vault flush. Server-side only. */ entries(projectPath: string): { key: string; value: string; }[]; /** Writes a value, replacing any existing one under the same name. */ set(projectPath: string, key: string, value: string): Promise; /** Forgets one name. Used by the tab's delete, and by "remove local copy". */ remove(projectPath: string, key: string): Promise; /** * Forgets several names at once — the explicit "remove local copies after * upload" action. Deliberately NOT called by the deploy flush: see the note * on {@link createPendingSecretsStore}. */ removeMany(projectPath: string, keys: string[]): Promise; } /** * Loads the store from disk and returns it. Never throws: an unreadable or * corrupt file yields an empty store rather than taking server boot down over * credentials the user can re-enter. A corrupt file is NOT deleted — it is left * in place so the values can be recovered by hand. * * NOTE ON THE DEPLOY FLUSH: a successful upload does NOT clear the local copy. * The vault has no read path by design, so once the local value is gone the * harness can never re-populate it — and local runs would silently stop * receiving credentials at exactly the moment the agent starts working in the * cloud. The tab shows an uploaded name as synced and offers removing the local * copy as its own explicit action. * * @param filePath Where the store lives. Defaults to the real state root; the * server passes a path derived from `--state-root` so a test or a scripted * check can never touch the developer's real credentials. */ export declare function createPendingSecretsStore(filePath?: string): Promise; //# sourceMappingURL=pending-secrets.d.ts.map