Feature Specification: Data Management (Export / Import / GitHub Sync)
Feature ID: data-management
Status: Retrospective
Created: 2026-05-01
Last updated: 2026-05-01
Owner: Ever Works Team
1. Overview
Data Management provides three coordinated tools for moving an entire account's configuration in and out of an Ever Works instance: Export (download a versioned JSON snapshot), Import (upload that snapshot to restore or migrate), and GitHub Sync (continuous backup of the same data to a private GitHub repository, push or pull). The shared design principle is uncompromising secret hygiene: real credentials never leave the API surface, and sync repos never store usable secrets.
2. User Scenarios
2.1 Primary scenarios
- Export: Given I want to migrate my account, when I click
"Export" with
includeSecrets: true, then I download a JSON file containing all my works, items, plugins, etc. — with secret values masked (MASKED:sk-***1234) so they're identifiable but unusable. - Import preview: Given I have an export JSON, when I upload it to the preview endpoint, then I get a summary including work count, total items, missing-plugin warnings, masked-secret detection, and slug conflicts before any data changes.
- Import apply: Given I confirmed the preview and resolved conflicts, when I apply the import, then the platform creates/updates works and their relations, clones each data repo, writes items + categories + tags + collections + comparisons + site config + markdown templates, commits, and pushes — all atomic per work.
- GitHub Sync push: Given I configured a private
ever-works-configrepo, when I push, then the platform writes a structured manifest + per-work folders (with masked secrets) and commits. - GitHub Sync pull: Given my config exists in the repo, when I pull, then the platform reads the structured files, reconstructs an export payload, and presents it as an import preview — same conflict-resolution flow as file import.
2.2 Edge cases & failures
- Given the import file contains masked secrets, when I apply,
then every
MASKED:...value is skipped and a per-plugin warning lists which keys need to be filled in by hand afterwards. - Given my import file references a plugin that's not installed,
when the preview runs, then the missing plugin id appears in
missingPluginsand (on apply) those settings are skipped. - Given an imported work's slug collides with an existing one,
when I see the conflict, then I choose one of:
skip(keep existing),overwrite(update existing), orrename(use a fresh slug). - Given a malicious file tries to write to
works/../etc/passwd, when GitHub Sync writes it, thenpath.basename()strips any traversal and only the safe slug component is used as a filename. - Given my GitHub repo contains masked values from a previous push, when I pull, then all secret values are unconditionally ignored on import — masked or not — to prevent overwriting real credentials in the DB.
3. Functional Requirements
- FR-1 Export MUST produce a versioned JSON file with
version,exportedAt,includesSecrets, anddatafields. - FR-2 Export MUST include: profile, works (with items, categories, tags, collections, comparisons, site config, markdown templates, schedules, advanced prompts, members, custom domains, work plugins), and user plugins.
- FR-3 When
includeSecretsistrue, secret values MUST be masked asMASKED:<first-3>***<last-4>(orMASKED:********for values ≤ 8 chars). Real values MUST NEVER be exported. - FR-4 When
includeSecretsisfalse, secret keys MUST be omitted entirely. - FR-5 Import preview MUST return:
valid,version,includesSecrets,hasMaskedSecrets,workCount,totalItemCount,userPluginCount,conflicts,missingPlugins. - FR-6 Import apply MUST accept a
resolutions[]array per conflicting slug, each with strategyskip/overwrite/rename. - FR-7 Import apply MUST detect any value still containing
MASKED:...and SKIP it, recording a per-plugin warning. - FR-8 Import apply MUST persist works and their relations (members, domains, plugins, advanced prompts, schedules) atomically per-work; partial failures must not corrupt state.
- FR-9 GitHub Sync MUST support a private repo (default name
ever-works-config) and produce a structured layout withmanifest.json,profile.json, per-work folders. - FR-10 GitHub Sync push MUST follow the same secret hygiene as export — masked or omitted, never real values.
- FR-11 GitHub Sync pull MUST always ignore secret values regardless of what is in the repo (defence against masked values overwriting real DB credentials).
- FR-12 All sync operations MUST validate slug components with
path.basename()to prevent path traversal.