Company Import/Export with Secret Scrubbing in Paperclip AI: The Complete Technical Guide
Paperclip AI's company portability system lets you export an entire organization and import it into another instance while automatically scrubbing secrets like Git tokens and API keys from all data.
Paperclip AI provides a robust, end-to-end company import/export feature that mirrors complete organizations—including agents, skills, projects, routines, and issues—across different Paperclip deployments. This guide breaks down the architecture, focusing on how secret scrubbing protects credentials during transfer and how collision handling preserves data integrity in the target environment.
How the Import/Export Architecture Works
The system centers on three core concepts that work together to ensure secure, reliable company portability.
| Component | Purpose | Key Implementation |
|---|---|---|
| Import Transfer Spool | Temporary storage for chunked uploads | server/src/services/company-import-transfers.ts |
| Secret Scrubbing | Redaction of all credentials before persistence | scrubGitCredentialText in server/src/services/git-credentials.ts |
| Collision Handling | Resolution of slug conflicts during import | Batched writers in server/src/services/import-write-types.ts |
The Export Phase: Creating a Portable Company Package
Exports are generated through either the CLI or UI, producing a markdown-first .zip package that encodes the entire company state.
CLI Export Command
In cli/src/commands/client/company.ts, the CLI builds and streams export bundles:
// cli/src/commands/client/company.ts
import { uploadCompanyImport } from "@paperclipai/shared/company-import-transfer";
// Build a zip file containing the company data (generated elsewhere)
const zipBuffer = await buildExportBundle(companyId);
// Submit the import – the helper streams the file in chunks
await uploadCompanyImport({
companyId: "target-company",
zipBuffer,
onProgress: (pct) => console.log(`Upload ${pct}%`),
});
The CLI approach suits automation pipelines and bulk migrations.
UI-Driven Export
The ui/src/pages/CompanyImport.tsx page provides a visual interface for the same workflow, with progress tracking and job status monitoring via ui/src/lib/import-job-watch.ts.
Chunked Upload and the Import Transfer Spool
Large company exports are broken into byte-range parts and reassembled server-side. This prevents timeouts and enables resumable uploads.
Spool Directory Structure
The import service in server/src/services/company-import-transfers.ts manages temporary storage:
<instance-root>/import-transfers/<runId>/part-<index>
Each chunk is indexed and stored until the final part arrives, triggering assembly. Abandoned spools are automatically cleaned up to prevent disk exhaustion.
UI Chunk Upload Implementation
// ui/src/pages/CompanyImport.tsx
import { startImportTransfer, uploadPart } from "@/api/companies";
async function handleFile(file: File) {
const { importRunId } = await startImportTransfer(companyId, file.name);
const CHUNK = 1_024 * 1024; // 1 MiB
for (let offset = 0; offset < file.size; offset += CHUNK) {
const chunk = file.slice(offset, offset + CHUNK);
await uploadPart(importRunId, offset / CHUNK, chunk);
setProgress(((offset + CHUNK) / file.size) * 100);
}
}
The server endpoint /api/companies/:companyId/import (defined in server/src/routes/companies.ts) receives each part and delegates to the spool service.
Secret Scrubbing: The Critical Security Layer
Secret scrubbing guarantees that credentials are never persisted in the target company's database, even if they exist in the source export.
The Git Credential Scrubber
The scrubGitCredentialText function in server/src/services/git-credentials.ts redacts credential patterns from URLs and text:
// server/src/services/git-credentials.ts
export function scrubGitCredentialText(text: string): string {
// Redact any `username:password@` patterns in URLs
return text.replace(
/([a-z]+:\/\/)([^@\/\s]+)@/gi,
(_, proto) => `${proto}*****@`
);
}
This scrubber runs before any data is written and is also reused by:
- The heartbeat service (for logging safety)
- Decision-training pipelines (for model training data hygiene)
Scrubbing Scope
The import service applies scrubbers to:
- Git repository URLs in agent configurations
- Embedded API keys in skill definitions
- Credential strings in project metadata
- Any free-text fields that might contain secrets
Import Writers and Collision Handling
Once scrubbed, data is written through batched importers that handle slug conflicts and ID remapping.
Batched Insert Pattern
In server/src/services/import-write-types.ts, writers batch inserts for performance:
// server/src/services/import-write-types.ts
export async function batchInsertAgents(db: Db, agents: AgentRow[]) {
// The writer batches the inserts to reduce round-trips
await db.insert(agentTable).values(agents).execute();
}
Collision Resolution Strategies
When the target company contains entities with matching slugs, the import writers apply one of three policies:
- Merge — Combine existing and imported entities (when safe)
- Rename — Append a suffix to imported entity slugs
- Reject — Block the import and surface the conflict to the user
The writers maintain a mapping table that the UI surfaces, showing users exactly which entities were created, renamed, or merged.
Job Status Monitoring and Completion
The ui/src/lib/import-job-watch.ts module tracks import progress in sessionStorage and polls the server for status updates. Once all batch writes complete successfully, the import job commits and the UI updates to reflect the new company state.
Company Scoping Invariants
Every step respects Paperclip's multi-tenant safety guarantees:
- All imported entities are bound to the target
companyId - The import process cannot read or write data from other companies
- Database transactions ensure atomicity per entity type
Key Source Files Reference
| File | Responsibility |
|---|---|
server/src/services/company-import-transfers.ts |
Spool directory management, part assembly, cleanup |
server/src/services/git-credentials.ts |
scrubGitCredentialText and credential pattern matching |
server/src/routes/companies.ts |
/api/companies/:companyId/import endpoint |
server/src/services/import-write-types.ts |
Batched writer interfaces and collision resolution |
ui/src/pages/CompanyImport.tsx |
Multipart upload UI and progress display |
ui/src/lib/import-job-watch.ts |
Client-side job state and polling |
cli/src/commands/client/company.ts |
CLI export/import command implementation |
Summary
- Paperclip AI's company import/export uses a chunked upload spool to handle large exports without timeouts
- Secret scrubbing via
scrubGitCredentialTextensures credentials never persist in target databases - Batched import writers resolve slug collisions and maintain ID mappings for transparency
- The CLI and UI share the same backend services, enabling both automation and interactive workflows
- All operations respect company-scoping invariants for multi-tenant security
Frequently Asked Questions
What secrets does Paperclip AI scrub during company import?
Paperclip AI scrubs Git access tokens, API keys, and username-password combinations embedded in URLs or configuration text. The scrubGitCredentialText function in server/src/services/git-credentials.ts uses regex patterns to redact credentials before any data reaches the database. This protects against accidental credential leakage during cross-instance migrations.
How does Paperclip AI handle large company exports that exceed timeout limits?
The system breaks exports into 1 MiB chunks uploaded via byte-range requests. The server stores each part in a temporary spool directory (<instance-root>/import-transfers/<runId>/), then assembles the full bundle once the final chunk arrives. This resumable upload pattern prevents timeouts and allows progress tracking in the UI.
What happens when imported entities have the same name as existing ones?
Import writers apply collision resolution policies: merging compatible entities, renaming imported slugs with suffixes, or rejecting the import and surfacing conflicts to the user. The server/src/services/import-write-types.ts module implements these strategies while maintaining a mapping that shows users exactly what changed.
Can I automate company imports without using the UI?
Yes. The CLI command in cli/src/commands/client/company.ts exposes the same functionality programmatically. Use uploadCompanyImport from @paperclipai/shared/company-import-transfer to stream export bundles with progress callbacks, enabling CI/CD pipelines and bulk migration scripts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →