Architecture
Goal
Adding a filesystem (FAT32 later, say) or a technique should mean writing a new package that fits existing interfaces. It should not mean editing code that already works.
That lets contributors own a filesystem or technique on their own and ship it step by step. They do not step on each other or on the cmd/ command files.
File structure
nemo/
main.go
cmd/ // Cobra commands, one file per command, plus the live-mode router
internal/
image/ // Image interface, raw file backend, read-only and aligned wrappers
binutil/ // bounds-safe binary readers
filesystem/ // FileSystem/Entry interfaces, registry, in-memory fakefs
ntfs/ apfs/ ext4/ // parser, named streams, slack, timestomp, live mode
technique/ // Technique interface, slack framing, backup manifest
custody/ // write-recording Image decorator and custody log
validation/ // local-dataset scoring
tools/validate/ // CLI entry point for internal/validation
Layers
cmd/ (hide.go, detect.go, clear.go, extract.go, features.go, live.go) Cobra commands
|
internal/technique Finding/Result, the Technique interface
|
internal/filesystem FileSystem/Entry interfaces, registry
ntfs/ apfs/ ext4/ one implementation package per FS
|
internal/custody Image decorator: hash + record every write
|
internal/image Image interface: ReadAt/WriteAt/Size/Path
Each layer depends only on the interfaces of the layer below it, never on a concrete sibling. For example, internal/technique never imports internal/filesystem/ext4. It calls through the FileSystem interface. That is what lets filesystems and techniques plug in independently.
Each layer’s contract has its own page:
- Image and custody: the
Imageinterface and theinternal/custodywrite decorator. - Filesystem interfaces:
FileSystemandEntry, the registry, and the three capability interfaces. - Technique interfaces: the
Techniqueinterface,Request,Finding,Result, and the backup and manifest contract. - Mode implementation: how image mode and live mode split in the code, and the two
Entryimplementations per filesystem. - Architecture decisions: the custody, framing, backup, and capability contracts.
Why this shape
- Independent ownership. One person can build
internal/filesystem/apfswhile another buildsinternal/filesystem/ext4. Both only need to satisfyFileSystemandEntry. Neither blocks the other, and neither touches shared code. - Incremental support. A filesystem can implement only
NamedStreamCapable.SlackSpaceCapableandTimestompCapableare separate optional interfaces. Partial support means a missing interface, not a half-built implementation of one big interface. - Cheap to extend. A new filesystem is a new package plus one registry line.
internal/techniqueand the commands stay unchanged. - Custody by structure. Custody recording is a decorator over
Image, so a technique cannot skip it. EveryWriteAton the wrapped image is hashed and recorded, whoever the caller is.
Non-goals
This matches the scope on Home. There is no runtime plugin loading (dynamic .so or .dll). There is no config-driven technique selection beyond the --technique flag. “Pluggable” here means new Go packages behind existing interfaces, compiled in.