Documentation
¶
Overview ¶
Package artifacts centralizes filesystem mechanics shared by approach artifact stores. Domain stores still own their JSON schema, validation, and listing semantics.
Index ¶
- Constants
- func AcquireFileLock(path, label string, timeout time.Duration) (func(), error)
- func AcquireFileLockNoFollow(path, label string, timeout time.Duration) (func(), error)
- func AllocateTimestampedID(opts IDOptions) (string, error)
- func CollectionDir(root, collection string) string
- func DefaultRoot() (string, error)
- func EnsureCollection(root, collection string) error
- func EnsureRecordDir(root, collection, id string) (string, error)
- func IsSafeID(id string) bool
- func NormalizePhaseID(id string) string
- func RecordDir(root, collection, id string) string
- func ReleaseDefaultRoot() (string, error)
- func RequireAbsoluteRoot(root, storeName string) (string, error)
- func ResolveCanonicalRoot(root, label string) (string, error)
- func SecureCanonicalRoot(root, label string) (string, error)
- func Slug(text, fallback string) string
- func StageReplace(path string) (commit, rollback func() error, err error)
- func TimestampedIDCandidates(opts IDOptions) []string
- func WriteFileAtomic(path string, data []byte) error
- func WriteFileAtomicFromReader(path string, input io.Reader) error
- func WriteFileAtomicFunc(path string, write func(io.Writer) error) error
- type IDOptions
Constants ¶
const ( DirPerm os.FileMode = 0o700 FilePerm os.FileMode = 0o600 )
Variables ¶
This section is empty.
Functions ¶
func AcquireFileLock ¶
AcquireFileLock acquires an exclusive advisory lock with a bounded retry window. The lock parent directory remains the caller's responsibility.
func AcquireFileLockNoFollow ¶ added in v0.10.0
AcquireFileLockNoFollow opens a lock file without following its final path component and requires a regular file before acquiring the advisory lock.
func AllocateTimestampedID ¶
AllocateTimestampedID returns a timestamp+slug ID that does not already have a record directory in the configured collection.
func CollectionDir ¶
CollectionDir returns root/<collection>.
func DefaultRoot ¶
DefaultRoot returns the shared approach artifact root used by sessions, plans, and flows.
A development build defaults to its own root (`approach-dev`) so it cannot silently migrate the database a released build owns. Only the *default* changes: an explicit --state-root, the APPROACH_*_STATE_ROOT variables, and [sessions].root are untouched, so Flow-launched agents — which always receive an explicit root — are unaffected.
The blast radius is wider than Flows. artifacts.DefaultRoot backs the session store, the plan store, and the flow store alike, so a development build moves its session history and plan list along with its Flow list. That is the correct outcome — development state should be isolated as a unit rather than split across two roots — but it is a user-visible change, not just "an empty Flow list on first run".
func EnsureCollection ¶
EnsureCollection creates and secures root/<collection>.
func EnsureRecordDir ¶
EnsureRecordDir creates and secures root/<collection>/<id>.
func NormalizePhaseID ¶
NormalizePhaseID canonicalizes a phase identifier so superficially different spellings of the same logical phase (case or surrounding whitespace) compare equal and upsert in place instead of duplicating rows.
func ReleaseDefaultRoot ¶ added in v0.10.5
ReleaseDefaultRoot is the root a released build defaults to, whatever this build is. It exists so the migration guard can test for "this development build is about to migrate the release-owned database" by exact path equality rather than by a broader rule that would also fire in every temp directory.
func RequireAbsoluteRoot ¶
RequireAbsoluteRoot returns the same root when it is absolute.
func ResolveCanonicalRoot ¶ added in v0.10.5
ResolveCanonicalRoot resolves root through symlinks and returns the canonical path without creating, chmod'ing, or asserting anything about it.
It exists for read-only opens. SecureCanonicalRoot's two chmods succeed on a user-owned 0755 or 0500 directory, so calling it from a reader would silently tighten the root to 0700 — repairing the very directory state a diagnostic is supposed to report, and giving `approach serve` a different effect on the same directory depending on how its root was spelled. A reader reports the mode it found; only a writer or a migrator repairs it.
func SecureCanonicalRoot ¶ added in v0.10.5
SecureCanonicalRoot creates root when absent, forces it to 0700, resolves it through symlinks, and returns the canonical path only when the resolved directory is genuinely owner-only. label names the root in every error so a caller's diagnostics read the way its own messages always did.
Every consumer that stores executable or authoritative state under an approach root shares this check: the flow database is written through it, and the pinned launch binary is executed out of it.
func Slug ¶
Slug lowercases text, keeps [a-z0-9-], collapses separator runs, trims boundary dashes, caps length, and falls back when nothing usable remains.
func StageReplace ¶ added in v0.10.6
StageReplace snapshots path so a replacement can be published without buffering the previous contents or leaving the canonical name empty. The replacement must be a new inode (WriteFileAtomic); an in-place truncate would mutate a hardlinked backup. commit deletes the backup; rollback restores it by renaming over the canonical path, or removes path when there was no previous file.
Backups are for in-process rollback only. A crash after WriteFileAtomic publishes the replacement has the same torn window as WriteFileAtomic alone: metadata remains the publication marker. A leftover sibling named path+".prev" is removed the next time StageReplace runs.
func TimestampedIDCandidates ¶ added in v0.10.0
TimestampedIDCandidates returns the exact candidate sequence used by AllocateTimestampedID. Storage backends use it to preserve file-allocation naming and exhaustion behavior while checking uniqueness in their own authority.
func WriteFileAtomic ¶
WriteFileAtomic replaces path with data using a temporary sibling file.
func WriteFileAtomicFromReader ¶
WriteFileAtomicFromReader streams input to path using a temporary sibling file, then renames it into place with restrictive permissions.