CheckFS validates files and directories against declarative options in Go
A small Go library that turns existence, permission, size and ownership checks into typed option structs — and can create the resource too.
CheckFS is a small Go library that answers one question: does this path satisfy my requirements? Instead of hand-writing os.Stat, permission masks and string checks in every program, you describe what you expect in an options struct and let the package verify it — or create the resource if that's part of the contract.
The repository is 64 KB across 18 files, licensed Apache-2.0, with its last commit dated 2025-05-12; the latest release tag is v1.0.4. The module declares Go 1.20 in go.mod (the README says "Go 1.18+"). It has no runtime dependencies beyond the standard library.
Two entry points
The top-level package is deliberately thin. checkfs.go defines two functions that delegate to subpackages:
func File(path string, opts file.Options) error {
return file.File(path, opts)
}
func Directory(path string, opts directory.Options) error {
return directory.Directory(path, opts)
}
The real work lives in file/ and directory/, which share a small common/ package providing the path-containment check IsPathInBase plus platform-specific helpers. The build-tagged files matter here: owner/group lookup and creation-time reading differ across Unix, Darwin and Windows. On Windows, GetOwnerAndGroup returns an error (an error like "owner and group checks are not supported on Windows: <path>"), while the permission helpers simplify Unix modes — for example IsMorePermissiveThan compares read bits (perms&0444) and IsLessPermissiveThan masks to 0666 — because NTFS modes don't map cleanly onto Unix semantics.
What you can check
The file.Options struct is the largest surface. A zero-value call stats the path; if it exists, it confirms it is a regular file, and missing paths pass unless Exists is set. Every field is opt-in:
- Existence:
Exists bool— require presence; it does not forbid an existing path, and a missing path passes wheneverCreate.Kindis notIfNotExistsandExistsis false (so bothNoActionandIfExistspass). - Permissions:
ReadOnly,WriteOnly,RequireWrite. - Ownership:
RequireOwnerandRequireGroup, compared as UID/GID strings from the stat result. - Location:
RequireBaseDirusescommon.IsPathInBaseto ensure the path stays inside a given directory. - Time:
CreatedBeforeandModifiedBefore. - Naming:
RequireExt,RequirePrefix,IsBaseNameLen. - Size:
IsLessThan,IsSize,IsGreaterThan. - Mode:
IsFileModefor an exact match, plusMorePermissiveThanandLessPermissiveThanbounds.
Failures come back as typed errors for owner, group, base-directory containment, write permission and read-only/open-permission failures (each with its directory counterpart) — so callers can distinguish "wrong owner" from "permissions too open" without parsing strings. The remaining checks return plain fmt.Errorf.
The directory.Options struct mirrors most of this, minus size checks (directories have no meaningful byte count) but adding WillCreate, which verifies the parent directory exists and is writable before allowing creation.
Creating on demand
Both subpackages embed a Create struct in their options. It carries three kinds:
const (
NoAction CreateKind = iota // 0, do nothing
IfNotExists // create only if the path is absent
IfExists // remove first, then recreate (destructive)
)
With Kind left at NoAction, the check flow performs no create action — calling Create.Run() directly returns an unsupported-kind error. In a directory check, setting WillCreate promotes the kind to IfNotExists. Set it to IfNotExists, and a missing file or directory gets made when you pass an empty Path — the original path is used instead. The README warns plainly that switching this on means checkfs may destroy the existing path and its contents in a non-reversible way.
For files, Create also exposes OpenFlag (so you can combine os.O_CREATE|os.O_TRUNC|os.O_WRONLY) and Size, which fills the new file with bytes up to 1 TB. For directories it's simpler: FileMode and Path.
The structs expose a .Run() method directly, so you don't have to go through an options check at all:
err := directory.Create{
Kind: directory.IfNotExists,
Path: filepath.Join(os.TempDir(), "test-directory"),
FileMode: 0755,
}.Run()
One quirk worth knowing: the package also provides NewCreate(*Create) *Create, but it currently returns an empty &Create{} rather than copying its argument, so prefer constructing and calling .Run() directly.
How a check flows
The file path is instructive. file.File stats the target first. If it doesn't exist and opts.Create.Kind == IfNotExists, it fills in the default path and calls Create.Run(). Otherwise it returns an error only when opts.Exists demands presence — a missing but unrequired file passes silently.
When the file does exist, checks run sequentially and short-circuit on the first failure: regular-file test, creation time, modification time, extension, prefix, base directory, size bounds, basename length, exact mode, permission bounds, read/write flags, then owner and group. Each is independent of the others, so a caller who sets several constraints gets them all enforced in one call.
The directory flow differs in two ways worth remembering: it handles WillCreate up front (checking the parent), and when the directory exists while Exists is false and WillCreate is false — regardless of what Create.Kind says — it returns an error rather than passing. That's a deliberate asymmetry versus files, so read the tests before relying on either default.
Testing
The repository ships top-level tests (TestFile, TestDirectory in checkfs_test.go) that exercise both wrappers against temp directories, along with benchmarks.
Who it's for
CheckFS is aimed at programs that must assert filesystem invariants before acting — installers checking a config file's ownership and mode, tools verifying a writable data directory exists (creating it if not), or scripts that want typed errors instead of shelling out to test -w. It trades the flexibility of hand-rolled checks for a declarative contract: describe what must be true, get an error naming exactly which requirement failed.
andreimerlescu/checkfs: 64 KB in 18 files, indexed Nov 9, 2026 after 0.00 hours of indexing. License: Apache-2.0. Source: https://github.com/andreimerlescu/checkfs.
Ask your agent to name it in its tool calls (the repo argument of query_graph, recall_project and read_project_source).
Written by AI models on the operator's own hardware, checked by a second model, from the repository's README and its code. Back to the shelf