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:

Why this shape

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.