tohru
tohru  /  docs

Manifests

Writing Tohru manifests

Profiles are defined by a tohru.jsonc manifest.

Manifests should include the version of Tohru they target. Tohru rejects manifests whose version is incompatible with the running binary according to semver.

JSONC manifests may include $schema for editor support:

{
  "$schema": "https://raw.githubusercontent.com/olimci/tohru/refs/heads/main/_assets/manifest.schema.json",
  "version": "1.0.0",
  "profile": {
    "slug": "my-dotfiles",
    "name": "my-dotfiles",
    "description": "personal setup"
  },
  "roots": [
    {
      "source": "home",
      "dest": "~",
      "defaults": {
        "type": "link"
      },
      "tree": {
        ".zshrc": ["copy"],
        ".config": {
          "kitty": {
            "kitty.conf": [],
            "theme.conf": ["l"],
            "kitty.app.png": ["copy", "untracked"]
          },
          "nvim": {
            "after": {
              ".": ["untracked"]
            }
          }
        }
      }
    }
  ]
}

Profile

KeyMeaning
slugStable identifier used for cached profile lookup.
nameHuman-readable profile name.
descriptionOptional profile description.

When a loaded profile has profile.slug, Tohru caches slug -> profile path in state. Future commands can use the slug instead of the full manifest path.

Roots

Each root maps a source tree inside the profile to a destination tree on the machine.

KeyMeaning
sourceProfile source directory. Relative paths are resolved from the profile directory.
destDestination directory. ~ expands to the current user’s home directory.
defaults.typeDefault file operation, either link or copy.
defaults.trackOptional default tracking mode for copied files and directories.
treeStructural tree describing managed destinations.
platformsOptional list of GOOS platform names where the root is active.
profilesOptional list of profile slugs where the root is active.
envOptional environment variable matches required for the root to be active.
runOptional argv command that materializes the root source before plan or load.
tempRemove the dynamic root source after plan or load. Temporary roots are copy-only.

In the structural tree, arrays represent files and objects represent directories. Directory metadata uses the reserved "." key. An empty array inherits defaults with no overrides.

Profile source trees encode hidden path segments with dot_, so .config/nvim is stored as dot_config/nvim. Literal source names starting with dot_ are escaped by adding another underscore.

Root source and dest values expand environment variables.

Root Selection

Roots can be scoped to platforms, profile slugs, and environment values:

{
  "source": "home",
  "dest": "~",
  "platforms": ["darwin", "linux"],
  "profiles": ["work"],
  "env": {
    "TOHRU_LAPTOP": "1"
  },
  "defaults": {
    "type": "copy"
  },
  "tree": {
    ".gitconfig": []
  }
}

The root is skipped unless all configured selectors match.

Dynamic Roots

Dynamic roots run a command before plan or load so the profile can materialize source files internally:

{
  "source": "generated/kitty",
  "dest": "~/.config/kitty",
  "run": ["./scripts/build-kitty-profile"],
  "temp": true,
  "defaults": {
    "type": "copy"
  },
  "tree": {
    "kitty.conf": []
  }
}

Dynamic root commands run from the profile directory with TOHRU_ROOT_SOURCE, TOHRU_ROOT_DEST, TOHRU_PROFILE_DIR, TOHRU_PROFILE_SLUG, and TOHRU_STORE_DIR in the environment.

Temporary dynamic roots are removed after plan or load and cannot contain link entries.

File Flags

FlagMeaning
linkManage the destination as a symbolic link to the profile source.
copyCopy the profile source to the destination.
trackedTrack destination state and restore conflicts when unloading.
untrackedManage without recording the destination for restore. Existing destinations are replaced when clobber_untracked is enabled. Only valid for copied files or directories.

Type flags are valid on files only. Directory metadata may set tracking flags.

Aliases are also supported: l, c, t, and u for link, copy, tracked, and untracked.