Documentation
¶
Overview ¶
Package backuprestore verifies a DevX private backup by restoring it into a fixture-owned directory only. It never writes outside that directory and never executes anything restored.
Backup layout (produced by create-private-backup.py, schema 1):
manifest.json files[], worktrees[], archive_sha256, bundle_sha256 private-files.tar.gz members "files/<absolute source without leading />" devx.git.bundle `git bundle create --all` of the main repo worktree-NN.patch `git diff --binary HEAD` per worktree
Safety rules, checked BEFORE any write:
- the fixture root must be an existing, empty-or-owned directory with a marker file this package wrote, and must not be (or contain, or be inside) any protected root (real HOME config, project roots, /);
- archive/bundle/patch hashes must match the manifest;
- every archive member must be a regular file whose name is relative, clean, under "files/", without "..", and listed in the manifest with a matching sha256 and size; links/devices/dirs-as-files are rejected;
- every destination is computed by joining the fixture root with the cleaned member name and re-checked to stay inside the root.
Files are written 0600 into 0700 directories regardless of archived mode (they are data, not executables).
Index ¶
- Variables
- func CheckFixtureRoot(root string, protected []string) error
- func DestFor(root, member string) (string, error)
- func NewFixtureRoot(base string) (string, error)
- func VerifyArtifacts(dir string, m *Manifest) error
- type File
- type Git
- type Manifest
- type Options
- type Result
- type RewriteResult
- type Worktree
- type WorktreeResult
Constants ¶
This section is empty.
Variables ¶
var ErrRejected = errors.New("backuprestore: rejected")
ErrRejected marks a refusal made before any write.
Functions ¶
func CheckFixtureRoot ¶
CheckFixtureRoot validates root against protected roots and the marker.
func DestFor ¶
DestFor maps an archive member to its path under root, rejecting anything that is not a clean relative "files/..." path inside root.
func NewFixtureRoot ¶
NewFixtureRoot creates a fresh 0700 directory under base and marks it as owned by this package.
func VerifyArtifacts ¶
VerifyArtifacts checks archive, bundle and patch hashes against the manifest. It writes nothing.
Types ¶
type Git ¶
type Git struct {
Root string // fixture root
}
Git runs git with an environment that cannot reach the user's config, hooks, credentials or other repositories: HOME and XDG point inside the fixture, system/global config are disabled, GIT_* routing variables are removed, and hooks are pointed at an empty fixture directory.
type Manifest ¶
type Manifest struct {
Schema int `json:"schema"`
Files []File `json:"files"`
Worktrees []Worktree `json:"worktrees"`
ArchiveSHA256 string `json:"archive_sha256"`
BundleSHA256 string `json:"bundle_sha256"`
}
Manifest mirrors the backup manifest (only fields we use).
func LoadManifest ¶
LoadManifest reads and structurally validates the manifest.
type Options ¶
type Options struct {
BackupDir string
// FixtureRoot receives everything. Must be created by NewFixtureRoot.
FixtureRoot string
// Protected roots: restore refuses a fixture root equal to, inside, or
// containing any of these. Callers add the real HOME, config dirs and
// project roots.
Protected []string
}
Options controls a restore.
type Result ¶
type Result struct {
FilesRestored int `json:"files_restored"`
BytesRestored int64 `json:"bytes_restored"`
HashMatches int `json:"hash_matches"`
ManifestFiles int `json:"manifest_files"`
RestoredDigest string `json:"restored_set_digest"`
}
Result reports counts and hashes only.
func RestoreFiles ¶
RestoreFiles restores archive members as data under <root>/restored/files/... after all pre-write checks pass. The archive is streamed twice: a validation pass with no writes, then the write pass.
type RewriteResult ¶
type RewriteResult struct {
SessionsRewritten int `json:"sessions_rewritten"`
ProjectsRewritten int `json:"projects_rewritten"`
RealPathRefsAfter int `json:"real_path_references_after_rewrite"`
ConfigSanitized bool `json:"config_sanitized"`
AllowlistSynthetic bool `json:"allowlist_synthetic"`
}
RewriteResult reports the fixture-only config copies (counts only).
func RewriteForFixture ¶
func RewriteForFixture(root, realHome string, allowProject string) (*RewriteResult, error)
RewriteForFixture builds a fixture-only DevX config dir at <root>/home/.config/devx from the restored data copies:
- sessions.json and projects.json are parsed and every absolute path that pointed under realHome is remapped under <root>/mapped/...;
- config.yaml is NOT copied (it contains real tokens, domains and service settings). A fresh minimal config is written instead, with caddy/updates/web autostart disabled and a synthetic pi_mcp allowlist;
- nothing is executed.
The original restored data under <root>/restored is left untouched for hash comparison.
type WorktreeResult ¶
type WorktreeResult struct {
Index int `json:"index"`
Name string `json:"name"` // basename of the original worktree
HEADMatches bool `json:"head_matches"`
Dirty bool `json:"dirty"`
PatchApplied bool `json:"patch_applied"`
DiffMatchesPatch bool `json:"diff_matches_patch"`
UntrackedExpected int `json:"untracked_expected"`
UntrackedRestored int `json:"untracked_restored"`
UntrackedHashOK int `json:"untracked_hash_ok"`
TrackedTreeDigest string `json:"restored_tree_digest"`
Error string `json:"error,omitempty"`
}
WorktreeResult is per-worktree fidelity, hashes and counts only.
func RestoreWorktrees ¶
func RestoreWorktrees(opts Options, m *Manifest) ([]WorktreeResult, error)
RestoreWorktrees clones the bundle under <root>/git/repo, then for each manifest worktree checks out its HEAD into <root>/git/wt-NN, applies the patch, copies untracked files from the already-restored archive data, and verifies `git diff --binary HEAD` reproduces the patch byte-for-byte.