store

package
v0.8.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 11, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ContentHash

func ContentHash(data []byte) string

ContentHash computes the SHA-256 hash of plaintext content.

func FormatSize

func FormatSize(bytes int64) string

FormatSize formats a byte count as a human-readable string.

func LoadOverrides added in v0.8.0

func LoadOverrides(sealDir string) map[string]string

LoadOverrides reads the device-local project-key overrides. Missing file or parse error yields an empty map — overrides are a convenience, never required.

func MatchGlob

func MatchGlob(path, pattern string) bool

MatchGlob matches a path against a glob pattern with ** support. It avoids strings.Split on both path and pattern strings to eliminate slice allocations.

func NormalizeProjectKey added in v0.8.0

func NormalizeProjectKey(s string) string

NormalizeProjectKey accepts either an absolute project path or an already-encoded segment and returns the encoded segment, so `project map` can take whichever the user has on hand.

func RemapKey added in v0.8.0

func RemapKey(relPath, srcHomeEnc, dstHomeEnc string) (string, bool)

RemapKey swaps a leading encode(srcHome) segment of a projects/ relPath for encode(dstHome), preserving the in-project remainder. Returns (relPath,false) for non-projects/ keys and for project paths not under srcHome.

func ResolveMergeStrategy

func ResolveMergeStrategy(relPath string, strategies map[string]string) string

ResolveMergeStrategy finds the merge strategy for a file based on glob patterns.

func ResolveMergeStrategyWithPattern

func ResolveMergeStrategyWithPattern(relPath string, strategies map[string]string) (strategy string, pattern string)

ResolveMergeStrategyWithPattern returns both the strategy and the pattern that matched. An empty pattern means a built-in default was used. When multiple glob patterns match, the most specific wins (most segments, fewest wildcards). This ensures deterministic resolution regardless of Go map iteration order.

func Rotate

func Rotate(cfg *config.Config, oldIdentity age.Identity, newRecipient age.Recipient, verbose bool, progress ProgressFunc) (int, error)

Rotate re-encrypts all sealed objects with a new key.

func SaveOverrides added in v0.8.0

func SaveOverrides(sealDir string, overrides map[string]string) error

SaveOverrides writes the device-local project-key overrides. The file is machine-specific and must never be synced; init's .gitignore force-ignores it for new stores, and this also appends the ignore rule for older ones.

Types

type DiffResult

type DiffResult struct {
	Added    []string // files in new but not old
	Modified []string // files in both but with different hashes
	Deleted  []string // files in old but not new
}

DiffResult describes the differences between two manifests.

func Status

func Status(cfg *config.Config) (*DiffResult, error)

Status returns the diff between the current claude directory and the seal manifest.

func UnsealStatus

func UnsealStatus(cfg *config.Config, opts ...UnsealOption) (*DiffResult, error)

UnsealStatus returns what Unseal would do without actually writing anything. "Added" = files in manifest but missing on disk (would be restored). "Modified" = files on disk with different content than manifest (would be overwritten). "Deleted" = managed files on disk but not in manifest (would be deleted).

type FileEntry

type FileEntry struct {
	ContentHash   string `json:"content_hash"`
	SizePlaintext int64  `json:"size_plaintext"`
	SizeEncrypted int64  `json:"size_encrypted"`
	Mtime         string `json:"mtime"`
	// ModTimeNs is the file's mtime in nanoseconds since epoch. Stored
	// alongside Mtime so the Status/UnsealStatus fast path can compare at
	// the highest resolution the filesystem exposes — RFC3339 truncates to
	// seconds and millisecond precision still aliases sub-millisecond writes,
	// either of which would let same-window content changes slip past.
	ModTimeNs     int64  `json:"mtime_ns,omitempty"`
	MergeStrategy string `json:"merge_strategy"`
	// For JSONL files, track line count for efficient dedup merge
	JSONLLineCount int `json:"jsonl_line_count,omitempty"`
	// Whether a session file is complete (immutable)
	SessionComplete bool `json:"session_complete,omitempty"`
}

FileEntry describes a single file in the seal store.

type Manifest

type Manifest struct {
	Version  int    `json:"version"`
	DeviceID string `json:"device_id"`
	SealedAt string `json:"sealed_at"`
	// OriginHome is the absolute home directory of the machine that sealed this
	// manifest. It is the authoritative signal for the cross-device project-key
	// remap (see remap.go): the projects/ keys encode this home, and on another
	// machine they must be rewritten to the local one. Optional — older stores
	// (and the heuristic fallback) work without it; it self-populates on the
	// next seal from an updated binary.
	OriginHome string               `json:"origin_home,omitempty"`
	Files      map[string]FileEntry `json:"files"`
}

Manifest tracks all files in the seal store with their content hashes and metadata.

func ApplyRemap added in v0.8.0

func ApplyRemap(m *Manifest, plans []RemapPlan) *Manifest

ApplyRemap returns a NEW manifest whose keys under each accepted plan's SrcKey are rewritten to its DstKey; FileEntry values are shared (treated immutable) and untouched keys pass through. When a rewrite collides with an existing key (the same logical project synced from two machines), the winner is chosen by a total order — higher JSONLLineCount, then the un-remapped (local) entry, then the larger ContentHash — so the result is deterministic regardless of map iteration order. Non-JSONL files (line count 0) therefore prefer the local entry rather than whichever happened to be visited first.

func LoadManifest

func LoadManifest(sealDir string) (*Manifest, error)

Load reads a manifest from disk.

func NewManifest

func NewManifest(deviceID string) *Manifest

NewManifest creates an empty manifest for the given device.

func (*Manifest) Diff

func (m *Manifest) Diff(other *Manifest) DiffResult

Diff compares this manifest against another and returns the differences.

func (*Manifest) Save

func (m *Manifest) Save(sealDir string) error

Save writes the manifest to disk.

type ObjectStore

type ObjectStore struct {
	// contains filtered or unexported fields
}

ObjectStore manages content-addressed encrypted blobs. Files are stored as objects/<hash[0:2]>/<hash[2:]>.age

func NewObjectStore

func NewObjectStore(sealDir string) *ObjectStore

NewObjectStore creates an ObjectStore rooted at the given directory.

func (*ObjectStore) Delete

func (s *ObjectStore) Delete(hash string) error

Delete removes an object by its content hash.

func (*ObjectStore) Exists

func (s *ObjectStore) Exists(hash string) bool

Exists checks if an object with the given hash exists.

func (*ObjectStore) Init

func (s *ObjectStore) Init() error

Init creates the objects directory.

func (*ObjectStore) ListAll

func (s *ObjectStore) ListAll() ([]string, error)

ListAll returns all content hashes of objects on disk.

func (*ObjectStore) ObjectPath

func (s *ObjectStore) ObjectPath(hash string) string

ObjectPath returns the filesystem path for a given content hash.

func (*ObjectStore) Read

func (s *ObjectStore) Read(hash string) ([]byte, error)

Read returns the encrypted data for a given content hash.

func (*ObjectStore) Size added in v0.8.1

func (s *ObjectStore) Size(hash string) (int64, bool)

Size returns the on-disk size of the object for the given hash, and whether it exists — one stat for callers that reuse an existing object and need its encrypted size without reading it.

func (*ObjectStore) Write

func (s *ObjectStore) Write(hash string, encrypted []byte) error

Write stores encrypted data at the content-addressed path.

type ProgressFunc

type ProgressFunc func(current, total int, path string)

ProgressFunc is called during long operations to report progress.

type ProjectInfo added in v0.8.0

type ProjectInfo struct {
	Key      string // encoded project segment
	Files    int    // manifest entries under it
	Foreign  bool   // its home prefix differs from this machine's
	Override string // device-local override target, "" if none
}

ProjectInfo describes one synced project directory for `enclaude project list`.

func ProjectDirs added in v0.8.0

func ProjectDirs(m *Manifest, cfg *config.Config) []ProjectInfo

ProjectDirs groups the manifest's project keys and classifies each as local or foreign relative to this machine, attaching any device-local override.

type RemapMode added in v0.8.0

type RemapMode int

RemapMode controls how foreign project keys are handled on unseal.

const (
	// RemapOff restores verbatim (legacy behavior).
	RemapOff RemapMode = iota
	// RemapAuto accepts the deterministic home-prefix swaps without prompting.
	RemapAuto
	// RemapInteractive consults DefaultRemapResolver to let the user decide.
	RemapInteractive
)

type RemapPlan added in v0.8.0

type RemapPlan struct {
	SrcKey   string // encoded project segment, e.g. "-home-daniel-core-enclaude"
	DstKey   string // local segment, e.g. "-Users-bob-core-enclaude"
	Accepted bool
	// Pinned marks a plan resolved by a device-local override. Pins are
	// settled decisions, so interactive unseals apply them without re-asking.
	Pinned bool
}

RemapPlan is the decision for one foreign project dir: every manifest key under projects/<SrcKey>/ is rewritten to the same subpath under projects/<DstKey>/.

func PlanRemap added in v0.8.0

func PlanRemap(m *Manifest, dstHomeEnc, originHomeEnc string, overrides map[string]string) []RemapPlan

PlanRemap groups the manifest's project dirs by their encoded segment and, for each whose home prefix differs from dstHomeEnc, proposes a swap to dstHomeEnc. originHomeEnc (encode(manifest.OriginHome), may be "") is treated as an extra known source prefix so non-standard homes still match. A device-local override (srcKey→dstKey) wins over the heuristic. One plan per project dir, sorted by SrcKey for deterministic output. Already-local and unrecognized dirs get no plan (restored verbatim) unless an override names them.

type RemapResolver added in v0.8.0

type RemapResolver func([]RemapPlan) ([]RemapPlan, error)

RemapResolver lets the cmd layer present proposed plans to the user and return the accepted/edited set. Consulted only in RemapInteractive mode; nil on a non-TTY. Mirrors the crypto.DefaultPassphraseFunc package-var seam so the store package stays free of any terminal dependency and tests can inject one.

var DefaultRemapResolver RemapResolver

DefaultRemapResolver is wired by cmd to a ui-backed prompt.

type RepairResult

type RepairResult struct {
	TotalManifest  int
	TotalOnDisk    int
	MissingObjects []string // manifest entries with no .age file
	CorruptObjects []string // objects that fail decrypt or hash mismatch
	OrphanObjects  []string // .age files not in manifest
	Fixed          int
}

RepairResult describes the outcome of a seal store integrity check.

func Repair

func Repair(cfg *config.Config, identity age.Identity, deleteOrphans bool, verbose bool) (*RepairResult, error)

Repair fixes seal store integrity issues.

func Verify

func Verify(cfg *config.Config, identity age.Identity, verbose bool) (*RepairResult, error)

Verify checks seal store integrity without modifying anything.

type ScanResult

type ScanResult struct {
	// RelPath is the path relative to the claude directory.
	RelPath string
	// AbsPath is the absolute filesystem path.
	AbsPath string
	// Size in bytes.
	Size int64
	// ModTime as Unix timestamp (nanoseconds).
	ModTimeNs int64
}

ScanResult represents a file found during scanning.

func ScanFiles

func ScanFiles(claudeDir string, includes, excludes []string) ([]ScanResult, error)

ScanFiles walks the claude directory and returns files matching include patterns but not matching exclude patterns.

type SealStats

type SealStats struct {
	Scanned        int
	Added          int
	Modified       int
	Deleted        int
	Unchanged      int
	Errors         int
	BytesPlaintext int64
	BytesEncrypted int64
	Sessions       SessionStats
	Elapsed        time.Duration
}

SealStats tracks what happened during a seal operation.

func Seal

func Seal(cfg *config.Config, recipient age.Recipient, verbose bool, progress ProgressFunc) (SealStats, error)

Seal encrypts changed files from claudeDir into the seal store.

func (SealStats) HasChanges

func (s SealStats) HasChanges() bool

HasChanges returns true if the seal produced any modifications worth committing.

func (SealStats) Multiline added in v0.7.0

func (s SealStats) Multiline(indent string) string

Multiline returns a 2–3 line summary for sync display: scan counters, data volume + elapsed, and session activity (when any sessions tracked). Each line is prefixed with the given indent.

func (SealStats) String

func (s SealStats) String() string

String returns a compact single-line summary suitable for commit messages. Multiline() is the right choice for terminal display.

type SessionStats added in v0.7.0

type SessionStats struct {
	Tracked int // session JSONLs in the new manifest
	New     int // session JSONLs not present in the prior manifest
	Updated int // session JSONLs whose JSONLLineCount grew since prior manifest
}

SessionStats counts session JSONL files under projects/ and how they changed relative to the previous manifest.

type UnsealOption added in v0.8.0

type UnsealOption func(*unsealConfig)

UnsealOption configures Unseal without changing its call-site signature — existing callers pass nothing and get the defaults. Mirrors the project's preference for stable signatures over threading a param through every caller.

func WithRemap added in v0.8.0

func WithRemap(mode RemapMode) UnsealOption

WithRemap sets how foreign project keys are handled (default RemapAuto).

type UnsealStats

type UnsealStats struct {
	Total          int
	Restored       int
	Unchanged      int
	Deleted        int
	Errors         int
	BytesDecrypted int64
	Merges         merge.Aggregate
	Elapsed        time.Duration
}

UnsealStats tracks what happened during an unseal operation.

func Unseal

func Unseal(cfg *config.Config, identity age.Identity, verbose bool, progress ProgressFunc, opts ...UnsealOption) (stats UnsealStats, err error)

Unseal decrypts seal contents back to claudeDir and removes managed files not in the manifest. The manifest is the source of truth.

func (UnsealStats) Multiline added in v0.7.0

func (s UnsealStats) Multiline(indent string) string

Multiline returns the single-line summary plus an optional merge-activity line when the preceding pull invoked the merge driver.

func (UnsealStats) String

func (s UnsealStats) String() string

String returns a compact single-line summary.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL