saferm v0.8.1
On this page

saferm is an AI-first safe rm replacement that archives files instead of permanently deleting them, with rich metadata capture and full undo support.

#saferm

AI-first safe rm replacement. Archives files instead of deleting them.

#CLI Reference

#Packages

saferm is organized into internal packages that handle distinct responsibilities: file and directory archival with integrity verification, SQLite-based metadata storage, git context detection, and environment and process metadata capture. Each package is independently testable and designed for concurrent use via WAL-mode SQLite and atomic file operations. Configuration is handled by strictcli's built-in config system (TOML format at ~/.saferm/config.toml).

#internal/archive

Package archive handles file and directory archival to the saferm archive. It supports atomic same-filesystem renames, cross-device copy-and-verify, and tar+zstd compression for directories.

#KindFile

Go go
const KindFile Kind = iota

The three shapes an archived entry takes on disk.

#KindDirectory

Go go
const KindDirectory
Go go
const KindSymlink

#ErrFileNotFound

Go go
var ErrFileNotFound      = errors.New("file not found")

Sentinel errors.

#ErrRecursiveRequired

Go go
var ErrRecursiveRequired = errors.New("target is a directory; recursive flag required")

#ErrConflict

Go go
var ErrConflict          = errors.New("destination already exists")

#ErrHashMismatch

Go go
var ErrHashMismatch      = errors.New("hash mismatch after copy")

#ArchiveResult

Go go
type ArchiveResult struct

ArchiveResult holds the outcome of archiving a file or directory.

#Kind

Go go
type Kind int

Kind names what an archival is about to move.

#Plan

Go go
type Plan struct

Plan is everything an archival can determine by reading: what the entry is, where it will land, and (for a symlink) what it points at. Building one mutates nothing, so a caller can render a plan as a preview and stop, or hand it to [Execute] and go through with it.

#NewPlan

Go go
func NewPlan(path string, archiveDir string, isRecursive bool) (*Plan, error)

NewPlan inspects path and resolves where archiving it would put it. It performs no mutation.

#Execute

Go go
func Execute(p *Plan) (*ArchiveResult, error)

Execute carries out the archival a [Plan] describes.

#Archive

Go go
func Archive(path string, archiveDir string, isRecursive bool) (*ArchiveResult, error)

Archive moves a file or directory into archiveDir, returning the result. For files: moved directly (or copied cross-device) with SHA-256 hash. For directories: compressed into a .tar.zst archive.

#Restore

Go go
func Restore(uuid string, archiveDir string, destPath string, isDirectory bool, force bool, symlinkTarget string) error

Restore extracts an archived file or directory to destPath. When symlinkTarget is non-empty, the entry is restored as a symlink pointing to that target (no physical archive file is read).

#internal/db

Package db manages the SQLite database tracking all saferm deletions. It uses WAL mode and busy_timeout for concurrency safety across multiple simultaneous sessions.

#SchemaSQL

Go go
const SchemaSQL = `

#ErrNotFound

Go go
var ErrNotFound = errors.New("record not found")

ErrNotFound is returned when a queried record does not exist.

#DB

Go go
type DB struct

DB wraps a *sql.DB connection to the saferm SQLite database.

#DeletionRecord

Go go
type DeletionRecord struct

DeletionRecord represents a single archived deletion in the database.

#Open

Go go
func Open(dbPath string) (*DB, error)

Open opens (or creates) the SQLite database at dbPath with WAL mode and busy_timeout=5000ms, then runs the schema DDL.

#DB.Close

Go go
func (d *DB) Close() error

Close closes the underlying database connection.

#DB.Insert

Go go
func (d *DB) Insert(rec *DeletionRecord) (int64, error)

Insert inserts a DeletionRecord and returns the auto-increment ID.

#DB.QueryByID

Go go
func (d *DB) QueryByID(id int64) (*DeletionRecord, error)

QueryByID retrieves a single record by ID. Returns ErrNotFound if it does not exist.

#DB.QueryByPath

Go go
func (d *DB) QueryByPath(path string) ([]*DeletionRecord, error)

QueryByPath returns all non-restored records matching the given original_path, ordered by deleted_at DESC (newest first).

#DB.QueryAll

Go go
func (d *DB) QueryAll(includeAll bool) ([]*DeletionRecord, error)

QueryAll returns all records ordered by deleted_at DESC. If includeAll is false, restored and purged records are excluded.

#DB.MarkRestored

Go go
func (d *DB) MarkRestored(id int64, restoredTo string) error

MarkRestored sets restored_at to now and restored_to to the given path. Returns ErrNotFound if the record does not exist.

#DB.MarkPurged

Go go
func (d *DB) MarkPurged(id int64) error

MarkPurged sets purged_at to now, preserving the metadata record. Returns ErrNotFound if the record does not exist.

#DB.QueryOlderThan

Go go
func (d *DB) QueryOlderThan(before time.Time) ([]*DeletionRecord, error)

QueryOlderThan returns all non-restored, non-purged records deleted before the given time.

#internal/git

Package git provides helpers for managing the git index alongside saferm's archive/restore operations.

#IsInGitRepo

Go go
func IsInGitRepo(dir string) bool

IsInGitRepo returns true if dir is inside a git working tree.

#IsGitTracked

Go go
func IsGitTracked(path string) bool

IsGitTracked returns true if the file at path is tracked by git (i.e., known to the index). The path must be absolute or relative to the current working directory; the command runs from the file's parent directory.

#GitRmCached

Go go
func GitRmCached(path string, recursive bool) error

GitRmCached stages the removal of path in the git index without touching the working tree (the file is already archived). When recursive is true, -r is added for directory removal.

#GitAdd

Go go
func GitAdd(path string) error

GitAdd stages a file in the git index. The command runs from the file's parent directory.

#internal/meta

Package meta collects rich metadata about each deletion: environment variables, git repository context, parent process information, and arbitrary user-supplied key-value pairs.

#Metadata

Go go
type Metadata struct

Metadata holds contextual information captured at deletion time.

#Collect

Go go
func Collect(excludePatterns []string, customMeta map[string]string) (*Metadata, error)

Collect gathers metadata from the current environment.

Best-effort per collector -- git context and parent-process details degrade to empty values rather than failing. The one thing it refuses to work around is an exclude pattern that does not compile: that is the caller asking for a redaction, and continuing without it would write the very variables the caller meant to keep out. See collectEnv.

Search