Skip to main content

Toda

CI codecov

Toda (תודה) deploys your dotfiles as symlinks, tells you exactly where each link came from, and reports when your system has drifted. Pure Python, no dependencies.

$ toda reconcile
- missing        /home/me/.vimrc expected=/home/me/dotfiles/vimrc
! wrong_target   /home/me/.config/git/config expected=/home/me/dotfiles/gitconfig actual=/home/me/old-config
totals ok=12 missing=1 wrong_target=1 overwritten_file=0 overwritten_dir=0 manifest_conflict=0

Why toda

Most dotfile managers tell you how to make links. Toda also tells you whether they are still right, and where each one came from.

toda GNU Stow dotbot chezmoi
Runtime dependencies none (core Python) Perl Python Go binary
Links files by manifest directory tree config directives file contents
Drift / diff report yes (reconcile) no no yes (diff)
Link provenance yes (trace) no no partial
Templating and secrets no no no yes
Windows yes no yes yes

Install

uv tool install toda     # or: pipx install toda

Toda requires Python 3.10 or newer. It supports Linux, macOS, the BSDs and Windows.

On Windows, creating symlinks requires Developer Mode or administrator rights. Toda checks this before it changes anything and refuses to run when symlinks are unavailable.

Quickstart

Put your dotfiles in a repo next to a MANIFEST:

dotfiles/
├── MANIFEST
├── gitconfig
└── vimrc
$default
~/.vimrc: vimrc
~/.config/git/config: gitconfig

Then:

$ toda install --dry-run      # see the plan, change nothing
would link /home/me/.vimrc -> /home/me/dotfiles/vimrc
would link /home/me/.config/git/config -> /home/me/dotfiles/gitconfig

$ toda install                # do it
$ toda reconcile              # is the system still correct?

Sources are relative to the directory holding the manifest, so the commands work from any working directory, and the same manifest works on every machine.

Actions

install     create links from manifest sections
purge       remove destination paths defined by manifest sections
inspect     print section include relationships
trace       show resolved link provenance (declaration source + include chain)
reconcile   diff expected links vs filesystem (exit 0 clean, 2 drift, 1 error)
help        show this help message and exit

install and purge accept -n/--dry-run, which prints the plan instead of applying it, and -f/--force, which replaces an existing file or directory after moving it aside to <dest>.toda-backup. Under --force, install also removes an @delete destination that is a directory, along with everything in it.

purge removes only symlinks toda owns, meaning links pointing at the expected source. A regular file that happens to sit at a manifest destination is reported and left alone.

Exit codes

code meaning
0 success; for reconcile, no drift
1 operational failure, or a skipped entry under --strict
2 reconcile found drift or a manifest conflict

MANIFEST file syntax

  • ~/bin/destination_link: ./section/source_file

    • destination-to-source mapping, with the two arguments delimited by a colon
  • $ bin

    • defines the bin section
  • ~/.old_config: @delete

    • deletes ~/.old_config if it exists; a directory needs --force, which removes it recursively
  • @include: bin default

    • includes bin and default
    • includes are resolved recursively in deterministic order and each included section is processed once
  • ~/.config/: config/*

    • links every entry of a source directory into a destination directory; the destination must end in /

See docs/manifest.md for the full grammar and its edge cases.

Manifest discovery

toda locates the manifest in this order:

  1. the -m/--manifest flag
  2. the TODA_MANIFEST environment variable
  3. the nearest MANIFEST found walking up from the current directory

That means you can run toda reconcile default anywhere inside a dotfiles repo and it will find the repo's manifest. If none is found, ./MANIFEST is used and the error names the path.

Variables

${VAR} in a destination or source is replaced with the value of the environment variable VAR. Only the braced form is expanded, and an unset variable is left as written:

${XDG_CONFIG_HOME}/git/config: gitconfig

Windows paths

A colon that follows a single drive letter (C:\, c:/) is part of the path, not the destination/source separator, so absolute Windows paths work in both positions:

~/note: C:\Users\me\notes.txt

Provenance (trace)

trace shows where each resolved link came from in the manifest:

$ toda trace default
/Users/me/.config/git/config <- /repo/dotfiles/gitconfig [section=base line=12 chain=default -> base]

Use JSON for tooling:

$ toda trace --format json default

Reconciliation (reconcile)

reconcile compares manifest expectations against the filesystem and prints a colored diff-style report. It also returns non-zero for drift in CI usage.

Statuses:

  • ok (green): destination exists as a symlink and points to expected source.
  • missing (red): destination does not exist.
  • wrong_target (red): destination is a symlink but points somewhere else.
  • overwritten_file (yellow): destination exists as a regular file.
  • overwritten_dir (yellow): destination exists as a directory.
  • manifest_conflict (magenta): multiple manifest declarations resolve to the same destination with different sources.

Examples:

$ toda reconcile --only-changed default
$ toda reconcile --format json default
$ toda reconcile --color never default

JSON output

trace, reconcile and inspect --format json emit a document with a schema_version field, currently 1. Pin against it when consuming the output from other tools. See docs/json-schema.md.

CLI reference

usage: toda [-h] [-n] [-m MANIFEST] [--version] [-f] [--strict] [-v] [-d DIR]
            [--no-preflight] [--format {text,json}]
            [--color {auto,always,never}] [--only-changed]
            [{install,purge,inspect,trace,reconcile,help}] [section ...]

creates symlinks described by a manifest

positional arguments:
  {install,purge,inspect,trace,reconcile,help}
                        action to run (default: inspect)
  section               manifest target

options:
  -h, --help            show this help message and exit
  -n, --dry-run         print the plan for install/purge without touching the
                        filesystem
  -m MANIFEST, --manifest MANIFEST
                        path to custom manifest file (default: $TODA_MANIFEST,
                        else the nearest MANIFEST walking up from the current
                        directory)
  --version             show program's version number and exit
  -f, --force           allow clobbering files in target paths, and recursive
                        removal of an @delete destination that is a directory
  --strict              treat skipped install/purge entries as failures
  -v, --verbose
  -d DIR, --dir DIR     override HOME and USERPROFILE (tilde expansion)
  --no-preflight        skip the preflight sanity checks
  --format {text,json}  output format for trace/reconcile actions
  --color {auto,always,never}
                        color mode for text output
  --only-changed        for reconcile text output, hide entries with status=ok

CI example

Fail a build when a machine has drifted from its dotfiles:

- run: toda install --manifest ./MANIFEST --no-preflight default
- run: toda reconcile --manifest ./MANIFEST --format json --color never default

toda reconcile exits 2 when links are missing or wrong, so this step fails the job. See examples/dotfiles for a complete repo.

Contributing

See CONTRIBUTING.md. Bug reports and pull requests are welcome.

License

MPL-2.0. See LICENSE.

Metadata

Release files for toda 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for toda 0.2.0
File Size Uploaded Provenance
toda-0.2.0.tar.gz 105.6 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for toda 0.2.0
File Interpreter ABI Platform
toda-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 129.4 kB

Release files / toda-0.2.0.tar.gz

Download URL toda-0.2.0.tar.gz
Size 105.6 kB
Tags Source
SHA-256 checksum
How to use checksums
b44bcf882026b8ed642ca8514df3691c00ca76803c732fee7b0d2ae025862101
BLAKE2b-256 checksum
How to use checksums
2060d9b01f1ad809849a6feb528573af911f138e7571a1f3eb78e5b738f4dfad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / toda-0.2.0-py3-none-any.whl

Download URL toda-0.2.0-py3-none-any.whl
Size 23.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
40b28edb89bd7bcc61650b71f70fe18d6b9abd0f2a7543305e431da7004c6200
BLAKE2b-256 checksum
How to use checksums
fd4b36148753aae717bf8ed20cf445d2903e968f21cc54dfa371ce1cbbd85ca8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Security for toda 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for toda-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page