Files
PaperClipAI/server/src/services/unsafe-workspace-export-notifications.ts
T
DottaandPaperclip 5e4cc01073 feat(workspaces): keep remote files off the webserver disk (#18403)
Keep native sandbox workspace files independent of the webserver disk. Use encrypted saved versions and Postgres ownership so a replacement server can recover files and continue work. Enable the path by default for new eligible workspaces when durable storage is configured.

Preserve explicit opt-outs and existing host workspaces. Include safe snapshot repair, Files access, import, migration and retention. Bound saved-Files resources, preserve imported Git recovery, reject unfinished Git operations, publish immutable concurrent captures, and verify permanent object-version deletion.

Validated 471 targeted local controls, build/typechecks, all 40 CI partitions, fresh Greptile 5/5 and security review. The PR records original failed attempts and scoped retries.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-31 14:54:01 -05:00

20 lines
1.2 KiB
TypeScript

import { DELIVERY_QUEUES, notifyDeliveryWork } from "./delivery-work-notifications.js";
export type UnsafeWorkspaceExportScope = { companyId: string; issueId?: string };
export async function notifyUnsafeWorkspaceExportRecovery(transaction: object, scope: UnsafeWorkspaceExportScope) {
await notifyDeliveryWork(transaction, DELIVERY_QUEUES.unsafeWorkspaceExportRecovery,
JSON.stringify({ companyId: scope.companyId, issueId: scope.issueId }));
// Recovery settlement can release a retained remote workspace snapshot.
await notifyDeliveryWork(transaction, DELIVERY_QUEUES.remoteWorkspaceSnapshots,
scope.issueId ? `issue:${scope.issueId}` : `company:${scope.companyId}`);
}
export async function notifyUnsafeWorkspaceExportRunChange(transaction: object, run: {
companyId: string; nativeIssueId: string | null; contextSnapshot: Record<string, unknown> | null;
}) {
const contextIssueId = run.contextSnapshot?.issueId;
const issueIds = new Set([run.nativeIssueId, typeof contextIssueId === "string" ? contextIssueId : null]);
for (const issueId of issueIds) if (issueId) await notifyUnsafeWorkspaceExportRecovery(transaction, { companyId: run.companyId, issueId });
}