Documentation
¶
Overview ¶
Package globalstore implements the user-level, content-addressed skill store under the gskill home (~/.gskill/store). Objects are immutable content directories keyed by their canonical integrity.HashDir hash, with a schema-versioned metadata record alongside. Admission stages content under an object lock and promotes it with an atomic rename; verification fully re-hashes content before any activation and quarantines corruption fail-closed. Deletion happens only through the conservative mark-and-sweep garbage collector, never as a side effect of a project operation.
Index ¶
- Variables
- func WriteMetadata(path string, meta Metadata) error
- type FetchFunc
- type FindingKind
- type GCCandidate
- type GCOptions
- type GCReport
- type Locker
- func (l *Locker) GCLockPath() string
- func (l *Locker) LockGC(ctx context.Context) (*fsutil.Lock, error)
- func (l *Locker) LockObject(ctx context.Context, key string) (*fsutil.Lock, error)
- func (l *Locker) LockRegistry(ctx context.Context) (*fsutil.Lock, error)
- func (l *Locker) ObjectLockPath(key string) string
- func (l *Locker) RegistryLockPath() string
- type Metadata
- type Object
- type Origin
- type ScanFinding
- type ScanOptions
- type ScanReport
- type Store
- func (s *Store) Admit(ctx context.Context, expectedKey, srcDir string, origin Origin) (reused bool, err error)
- func (s *Store) ContentPath(key string) string
- func (s *Store) GC(ctx context.Context, opts GCOptions) (GCReport, error)
- func (s *Store) Has(key string) bool
- func (s *Store) Home() *home.Home
- func (s *Store) ListKeys() ([]string, error)
- func (s *Store) MetadataPath(key string) string
- func (s *Store) ObjectPath(key string) string
- func (s *Store) Open(key string) (*Object, error)
- func (s *Store) Pin(key string) error
- func (s *Store) Pinned(key string) bool
- func (s *Store) Pins() ([]string, error)
- func (s *Store) Quarantine(key string) error
- func (s *Store) RecordOrigin(ctx context.Context, key string, origin Origin) error
- func (s *Store) Repair(ctx context.Context, key string, fetch FetchFunc) error
- func (s *Store) Root() string
- func (s *Store) SetLocker(l *Locker)
- func (s *Store) TouchLastUsed(ctx context.Context, key string) error
- func (s *Store) Unpin(key string) error
- func (s *Store) VerifyObject(key string) error
- func (s *Store) VerifyStore(opts ScanOptions) (ScanReport, error)
Constants ¶
This section is empty.
Variables ¶
var ErrObjectNotFound = errors.New("store object not found")
ErrObjectNotFound reports a store lookup for a key with no admitted object.
var ErrSchemaVersion = errors.New("unsupported metadata schema version")
ErrSchemaVersion reports a metadata record written by a different gskill generation. The object itself may be perfectly healthy — callers must treat this as "cannot read", never as corruption.
Functions ¶
func WriteMetadata ¶
WriteMetadata writes meta atomically and deterministically with owner-only permissions.
Types ¶
type FetchFunc ¶
FetchFunc downloads the exact commit of a source into dest. The installer wires this to the git runner; tests substitute stubs.
type FindingKind ¶
type FindingKind string
FindingKind classifies one store-scan problem.
const ( FindingCorrupted FindingKind = "corrupted" FindingMalformed FindingKind = "malformed" FindingInvalidMetadata FindingKind = "invalid-metadata" FindingUnsafePerms FindingKind = "unsafe-permissions" FindingStrayStaging FindingKind = "stray-staging" )
Store-scan finding kinds (FR-022).
type GCCandidate ¶
type GCCandidate struct {
Key string
SizeBytes int64
// Skill and Version come from the first recorded origin (display only).
Skill string
Version string
LastUsed time.Time
}
GCCandidate is one object eligible for deletion.
type GCOptions ¶
type GCOptions struct {
// GracePeriod is the minimum object age (from max(createdAt, lastUsedAt))
// before an unreferenced object becomes deletable. Zero uses 30 days.
GracePeriod time.Duration
// Apply performs deletions; false (the default) is a dry run.
Apply bool
// MarkRefs returns the content keys referenced by live projects —
// lockfiles, state files, and active links, typically fed by the advisory
// registry. It is re-invoked under the GC lock before any deletion so a
// stale pre-lock scan can never justify a delete. A nil MarkRefs marks
// nothing (degraded mode: only pins, locks, and the grace period protect).
MarkRefs func() map[string]bool
}
GCOptions configures a garbage-collection run (FR-024/025).
type GCReport ¶
type GCReport struct {
Candidates []GCCandidate
ReclaimableBytes int64
// Deleted lists the keys actually removed (apply mode only).
Deleted []string
// Skipped lists keys that became protected between marking and deletion
// (gc-conflict: lock held, re-marked, or re-pinned) — reported, never an
// error (error contract).
Skipped []string
// Degraded reports that no reference marking was available.
Degraded bool
}
GCReport summarizes a GC run.
type Locker ¶
type Locker struct {
// contains filtered or unexported fields
}
Locker acquires the gskill lock taxonomy under <home>/locks: per-object admission locks, per-project mutation locks, the GC lock, and the registry lock. A contended lock waits up to the configured timeout, emitting a visible waiting notice, then fails with the lock-failure exit code.
func NewLocker ¶
NewLocker returns a Locker with the given acquisition timeout. notice receives the waiting message (nil silences it); commands pass stderr.
func NewLockerWithNotice ¶
func NewLockerWithNotice(h *home.Home, timeout time.Duration, notice io.Writer, noticeAfter time.Duration) *Locker
NewLockerWithNotice is NewLocker with a custom notice threshold (tests use a short one).
func (*Locker) GCLockPath ¶
GCLockPath returns the lock file guarding a whole GC apply run.
func (*Locker) LockObject ¶
LockObject takes the exclusive lock for one store object.
func (*Locker) LockRegistry ¶
LockRegistry takes the exclusive project-registry lock.
func (*Locker) ObjectLockPath ¶
ObjectLockPath returns the lock file guarding one store object.
func (*Locker) RegistryLockPath ¶
RegistryLockPath returns the lock file guarding project-registry writes.
type Metadata ¶
type Metadata struct {
SchemaVersion int `json:"schemaVersion"`
ContentHash string `json:"contentHash"`
SizeBytes int64 `json:"sizeBytes"`
CreatedAt time.Time `json:"createdAt"`
LastUsedAt time.Time `json:"lastUsedAt,omitzero"`
Origins []Origin `json:"origins"`
}
Metadata is the descriptive, mutable-under-lock record beside an object's immutable content. It never affects object identity.
func ReadMetadata ¶
ReadMetadata loads and validates a metadata record, rejecting unknown schema versions with a clear error (forward compatibility, Constitution II).
type Object ¶
type Object struct {
// Key is the canonical content key ("sha256:<hex>").
Key string
// Path is the object directory.
Path string
// ContentPath is the immutable content directory.
ContentPath string
// Metadata is the object's descriptive record.
Metadata Metadata
}
Object is an admitted store object.
type Origin ¶
type Origin struct {
SourceType string `json:"sourceType,omitempty"`
Source string `json:"source,omitempty"`
SkillPath string `json:"skillPath,omitempty"`
Version string `json:"version,omitempty"`
Ref string `json:"ref,omitempty"`
Commit string `json:"commit,omitempty"`
}
Origin records one known source of an object's content. Origins are descriptive only; identical content from different sources shares one object. Credentials and tokens are never recorded.
func MergeOrigins ¶
MergeOrigins returns existing with extra merged in, sorted by source (then skill path, then commit) and de-duplicated. The inputs are not mutated.
type ScanFinding ¶
type ScanFinding struct {
Kind FindingKind
// Key is the affected object's content key (empty for stray staging).
Key string
// Detail is a human-readable description.
Detail string
// Expected and Actual carry the hash pair for corruption findings.
Expected string
Actual string
// UsedBy lists the known projects referencing the object.
UsedBy []string
// Path is the offending filesystem path.
Path string
}
ScanFinding is one problem the store scan found.
type ScanOptions ¶
type ScanOptions struct {
// UsedBy resolves the projects known to reference an object key; nil
// leaves UsedBy empty (the scan stays registry-independent).
UsedBy func(key string) []string
// StrayAge is the minimum age of a tmp/ entry to report as abandoned
// staging; zero uses one hour.
StrayAge time.Duration
}
ScanOptions configures VerifyStore.
type ScanReport ¶
type ScanReport struct {
Checked int
Healthy int
Findings []ScanFinding
}
ScanReport summarizes a store-wide verification (FR-022).
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is the global content-addressed store rooted in a gskill home.
func (*Store) Admit ¶
func (s *Store) Admit(ctx context.Context, expectedKey, srcDir string, origin Origin) (reused bool, err error)
Admit stores srcDir's content under expectedKey, recording origin. It is the only way content enters the store (FR-006):
- take the object lock and re-check existence (a concurrent admitter may have won);
- stage into an owner-only temp dir under <home>/tmp;
- validate the content (path traversal, escaping symlinks) — fetched content is never executed;
- re-hash the staged copy and verify it equals expectedKey, failing closed on mismatch with nothing admitted;
- write metadata, then promote content with an atomic same-filesystem rename.
An object that already exists is reused: its metadata gains origin and the call reports reused=true.
func (*Store) ContentPath ¶
ContentPath returns the immutable content directory for a content key.
func (*Store) GC ¶
GC runs conservative mark-and-sweep over the store (FR-024/025). An object is a candidate only when it is unreferenced, unpinned, metadata-valid, safely owned, and older than the grace period. Dry runs only report; apply mode takes the global GC lock, re-marks, then re-checks each candidate under its object lock immediately before deletion (FR-031).
func (*Store) Has ¶
Has reports whether an admitted object exists for key. Presence means the content directory exists; integrity is established separately by VerifyObject.
func (*Store) ListKeys ¶
ListKeys returns the content keys of every object directory in the store, in no particular order. Malformed layouts are included (their key is the directory-derived name) so verification can report them.
func (*Store) MetadataPath ¶
MetadataPath returns the metadata file for a content key.
func (*Store) ObjectPath ¶
ObjectPath returns the object directory for a content key, whether or not it exists.
func (*Store) Open ¶
Open loads the object for key, returning ErrObjectNotFound when it is not admitted and a metadata error when its record is unreadable or from an unsupported schema.
func (*Store) Pin ¶
Pin exempts the object for key from garbage collection (FR-026). Pinning an unknown object is an error.
func (*Store) Quarantine ¶
Quarantine moves the object for key out of the store into <home>/quarantine/<key>-<timestamp>/ so it can never be activated, while preserving the evidence for repair and inspection (FR-021).
func (*Store) RecordOrigin ¶
RecordOrigin merges origin into an admitted object's metadata under the object lock. Content is never touched (FR-004).
func (*Store) Repair ¶
Repair restores the object for key from its recorded origin (FR-023): it picks a commit-bearing origin, re-fetches exactly that commit into staging, verifies the staged content hashes to key, and atomically replaces the object. It fails — leaving the existing object untouched — when no origin records an exact commit or when the re-fetched content differs: an object is never silently replaced with different content.
func (*Store) SetLocker ¶
SetLocker overrides the store's locker (timeout, notice writer). A nil locker resets to the default.
func (*Store) TouchLastUsed ¶
TouchLastUsed stamps the object's lastUsedAt under the object lock. It is best-effort bookkeeping for GC reporting; content is never touched.
func (*Store) Unpin ¶
Unpin removes the GC exemption for key. Unpinning a never-pinned object is a no-op.
func (*Store) VerifyObject ¶
VerifyObject establishes that the object for key is trustworthy: its metadata parses and matches, and a full re-hash of content/ equals key (clarification #2). On corruption the object is moved to quarantine and a fail-closed integrity error carrying expected and actual hashes is returned (FR-020/021). Missing objects report ErrObjectNotFound.
func (*Store) VerifyStore ¶
func (s *Store) VerifyStore(opts ScanOptions) (ScanReport, error)
VerifyStore scans every object: full content re-hash, metadata validation, malformed-layout detection, unsafe permissions, and abandoned staging directories (FR-022). It reports; it never quarantines or deletes — those are activation's and repair's jobs.