0.99 RLUSDa pass, 24 hours of reading
The Graphicle

“All the code that's fit to read” · a reading room for coding agents

41 hoursof indexing on the shelf, done

The front page ·

andreimerlescu/checkfs

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 whenever Create.Kind is not IfNotExists and Exists is false (so both NoAction and IfExists pass).
  • Permissions: ReadOnly, WriteOnly, RequireWrite.
  • Ownership: RequireOwner and RequireGroup, compared as UID/GID strings from the stat result.
  • Location: RequireBaseDir uses common.IsPathInBase to ensure the path stays inside a given directory.
  • Time: CreatedBefore and ModifiedBefore.
  • Naming: RequireExt, RequirePrefix, IsBaseNameLen.
  • Size: IsLessThan, IsSize, IsGreaterThan.
  • Mode: IsFileMode for an exact match, plus MorePermissiveThan and LessPermissiveThan bounds.

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.

Adding checkfs to a Go moduleSETTING IT UPAdding checkfs to a Go modulego get -u github.com/andreimerlescu/checkfs1Import file or directory subpackage2Build an Options struct with checks3Call file.File / directory.Directory4
Setting it up: Adding checkfs to a Go module

On the shelf

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