Co-authored-by: Jeffrey Smith <jasafpro@gmail.com> Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
7.2 KiB
DESIGN-0.34.0 — Data Portability
Export your data, import it elsewhere, delete your account. Six changesets covering user export, user import, GDPR delete, admin team export/import, ChatGPT import, and K8s scheduled backups.
Depends on: v0.33.0 (Observability, current HEAD).
Design decision: One new migration (021) — indexes only, no new
tables. The export archive is a ZIP file (.switchboard extension)
containing a JSON manifest and per-entity JSON files. Sensitive fields
(password hashes, encrypted keys, storage keys) are stripped at export
time via entity sanitization.
Archive format: .switchboard ZIP with format_version=1:
manifest.json
data/users.json
data/channels.json
data/messages.json
data/channel_participants.json
data/channel_models.json
data/channel_cursors.json
data/notes.json
data/note_links.json
data/memories.json
data/projects.json
data/project_channels.json
data/project_knowledge_bases.json
data/project_notes.json
data/workspaces.json
data/workspace_files.json
data/folders.json
data/user_settings.json
data/notification_preferences.json
data/usage_entries.json
data/persona_groups.json
data/persona_group_members.json
data/files.json
files/{file_id}/{filename}
What Already Existed
- Package export —
.pkgZIP format,package_export.go - Markdown export — pandoc-based,
export.go - Workspace archive — ZIP/tar.gz with size limits,
workspace/archive.go - Soft deletes —
deleted_aton messages - Audit trail —
audit_logstable - 39 store interfaces covering all entity types
CS0 — Export Format + User Export
Foundation changeset. Defines the archive format and ExportStore interface.
server/export/format.go (new)
Archive types: Manifest, ManifestScope, ArchiveWriter, ArchiveReader.
Size limits: 500 MB archive, 100 MB per file, 10K files max.
Key methods: WriteManifest, WriteEntityJSON, WriteFile,
ReadManifest, ReadEntityJSON, FileEntries.
server/export/entities.go (new)
Per-entity sanitization functions. Export types strip sensitive fields:
ExportChannel— removesprovider_config_idExportMessage— no changes neededExportWorkspace— removesroot_path, git credentialsExportUsageEntry— removesprovider_config_id- Users — removes
password_hash,encrypted_uek,uek_salt
server/store/interfaces.go (modified)
Added Export ExportStore to Stores struct. The ExportStore
interface provides 21 user-scoped read methods, 20 import methods
(returning (imported, skipped, error) counts), 14 team-scoped
read methods, and 4 GDPR methods.
server/store/{postgres,sqlite}/export.go (new)
Full implementations for both dialects (~1400-1500 lines each).
Import uses INSERT ON CONFLICT DO NOTHING (PG) / INSERT OR IGNORE
(SQLite) for UUID-based dedup.
server/handlers/export_data.go (new)
DataExportHandler.ExportMyData — GET /api/v1/export/me.
Streams ZIP directly to response using zip.NewWriter(c.Writer).
Includes file blobs from object store.
Migration 021
Indexes only — idx_messages_channel_active, idx_channels_user_id,
idx_files_user_id. PG version uses partial index
(WHERE deleted_at IS NULL).
CS1 — User Import
server/handlers/import_data.go (new)
DataImportHandler.ImportMyData — POST /api/v1/import/me.
Opens archive, validates manifest format_version, reads entities
in FK-dependency order, remaps user_id for cross-instance import.
Returns {imported: {channels: N, ...}, skipped: {...}, errors: [...]}.
Import order: Folders → Projects → Workspaces → Channels → Participants/Models/Cursors → Messages → Notes → Note Links → Memories → Project links → Workspace files → Files + blobs → Settings → Persona groups.
CS2 — GDPR Account Delete
server/handlers/gdpr.go (new)
GDPRHandler.DeleteMyAccount — DELETE /api/v1/me.
Requires {"confirm": "DELETE", "password": "..."}. Verifies
password, prevents last-admin deletion, then:
- Soft-deletes messages, archives channels
- Hard-deletes: notes, memories, workspaces, files, projects, settings
- Revokes tokens (refresh + WS tickets)
- Anonymizes user:
deleted-user-{sha256(id)[:12]} - Writes audit log entry
CS3 — Admin Team Export/Import
DataExportHandler.ExportTeam — GET /api/v1/admin/teams/:id/export
DataImportHandler.ImportTeam — POST /api/v1/admin/teams/:id/import
Same archive format, scoped to team entities (channels, personas,
knowledge bases, workflows, groups, projects, resource grants).
Team import skips members whose user_id doesn't exist on target.
CS4 — ChatGPT Import
server/export/chatgpt.go (new)
Parses ChatGPT conversations.json format. Each conversation maps
to one channel (type=direct). The mapping DAG is walked depth-first
to produce ordered messages with parent_id and sibling_index.
Role mapping: user→user, assistant→assistant, system→system,
tool→tool. Content: parts[] joined with \n, non-string parts
skipped. Timestamps: float64 epoch → time.Time.
DataImportHandler.ImportChatGPT — POST /api/v1/import/chatgpt.
Accepts multipart upload of conversations.json or ZIP containing it.
CS5 — K8s Backup + ICD Tests
chart/templates/cronjob-backup.yaml (new)
CronJob running pg_dump | gzip on schedule (default daily 02:00 UTC).
Optional S3 upload via AWS CLI. Local retention pruning. Gated on
backup.enabled=true and database.driver=postgres.
chart/templates/pvc-backup.yaml (new)
Conditional PVC for local backup storage (when backup.persistence.enabled).
chart/values.yaml (modified)
New backup: section with schedule, retention, S3 config, persistence,
and resource limits.
ICD Tests
~30 new tests in crud/portability.js covering:
- Export download + archive validation (ZIP structure, manifest, entities)
- Sensitive field exclusion verification
- Import re-import (dedup validation)
- Invalid archive rejection
- ChatGPT import (valid, invalid JSON, empty array)
- ChatGPT channel/message verification
- GDPR delete flow (register → create data → delete → verify lockout)
- GDPR validation (missing confirm, wrong password)
- Admin team export/import
New Routes
| Method | Path | Auth | Handler |
|---|---|---|---|
| GET | /api/v1/export/me |
JWT | DataExportHandler.ExportMyData |
| POST | /api/v1/import/me |
JWT | DataImportHandler.ImportMyData |
| POST | /api/v1/import/chatgpt |
JWT | DataImportHandler.ImportChatGPT |
| DELETE | /api/v1/me |
JWT | GDPRHandler.DeleteMyAccount |
| GET | /api/v1/admin/teams/:id/export |
Admin | DataExportHandler.ExportTeam |
| POST | /api/v1/admin/teams/:id/import |
Admin | DataImportHandler.ImportTeam |
Verification
cd server && go build ./...— all changesets compile cleanhelm template ./chart --set backup.enabled=true— CronJob renders- ICD test suite: ~615 tests (585 existing + ~30 portability)
- Manual smoke: export → inspect ZIP → re-import → verify dedup
- GDPR: delete → verify login fails → inspect anonymized DB record
- Sensitive fields: no
password_hash,encrypted_uek,storage_keyin export