This repository manages system configurations for multiple machines using Nix and Org mode. All configuration is written as literate programs—Org documents where prose explains the reasoning behind each decision, and Nix code blocks are tangled into the actual configuration files.
The full configuration document is published at:
- English: https://natsukium.github.io/dotfiles/
- Japanese: https://natsukium.github.io/dotfiles/ja/
Nix is a purely functional package manager and build system. This repository uses several Nix ecosystem tools:
- Flakes for reproducible dependency management
- NixOS for declarative Linux system configuration
- nix-darwin for declarative macOS system configuration
- home-manager for user environment management
- nix-on-droid for Android (Termux) environment
| Name | Platform | Device | Role |
|---|---|---|---|
| kilimanjaro | NixOS (x86_64) | i5-12400F / RTX 3080 | Main desktop |
| tarangire | NixOS (x86_64) | Ryzen 9 9950X | Build server |
| manyara | NixOS (x86_64) | Beelink Mini S12 | Home server |
| arusha | NixOS (x86_64) | WSL2 | WSL environment |
| serengeti | NixOS (aarch64) | OCI A1 Flex | Build server |
| katavi | macOS (aarch64) | M1 MacBook Air | Main laptop |
| work | macOS (aarch64) | M4 MacBook Pro | Work laptop |
| mikumi | macOS (aarch64) | M1 Mac mini | Build server |
| android | nix-on-droid | Galaxy S24 FE | Phone |
Nix is declarative. Reading a Nix expression reveals what the system should become, and Nix itself handles how to get there. But neither the code nor the build system captures why a particular configuration exists, or why alternatives were rejected.
Why was fish chosen over zsh or bash? Why does the desktop profile enable this specific set of services? Why was a particular package pinned to an older version? The code shows the decision, but not the reasoning behind it. Without this context, future changes risk undoing intentional tradeoffs or repeating previously rejected approaches.
This repository uses literate programming to preserve intent. Configuration lives in Org mode documents where prose surrounds code. Each decision—from high-level architecture to individual package overrides—is accompanied by its rationale: why this approach was chosen, and why alternatives were not.
Configurations are documented with the problem that motivated them, the alternatives considered, and the reasoning behind the final choice. For temporary workarounds, the conditions for removal are also noted.
This repository provides a Nix development shell with all the tools needed for working on the configurations. Enter the shell by running:
nix developThe shell includes infrastructure tools (Terraform, sops, ssh-to-age),
translation tools (po4a, gettext), and build utilities (nix-fast-build).
On entry, it automatically sets up pre-commit hooks, configures MCP servers,
and syncs CLAUDE.md from the literate source.
devShells = {
default = pkgs.mkShell {
packages = with pkgs; [
aws-vault
nix-fast-build
sops
ssh-to-age
(terraform.withPlugins (p: [
p.carlpett_sops
p.cloudflare_cloudflare
p.determinatesystems_hydra
p.hashicorp_aws
p.hashicorp_external
p.hashicorp_null
p.integrations_github
p.oracle_oci
]))
<<translation-packages>>
];
shellHook =
config.pre-commit.installationScript
+ config.mcp-servers.shellHook
+ ''
echo "Syncing CLAUDE.md..."
make CLAUDE.md >/dev/null 2>&1 || echo "Warning: Failed to generate CLAUDE.md"
'';
};
};This project uses po4a to manage translations.
The required packages are included in the development shell.
gettext
self'.packages.po4a_0_74gettext: provides msgfmt and other internationalization utilitiespo4a_0_74: po4a >= 0.74 is required for Org mode support. Using a pinned derivation since nixpkgs ships an older version (see packages).
Configure the target language,
location for generated po files,
and documents
to translate as follows.
The -k 0 option forces output of translated files
even if the translation is incomplete
(default threshold is 80%).
[po4a_langs] ja
[po4a_paths] po/dotfiles.pot $lang:po/$lang.po
[type: org] configuration.org $lang:configuration.$lang.org opt:"-k 0"
[type: org] .github/README.org $lang:.github/README.$lang.org opt:"-k 0"
[type: org] applications/emacs/init.org $lang:applications/emacs/init.$lang.org opt:"-k 0"
[type: org] applications/emacs/early-init.org $lang:applications/emacs/early-init.$lang.org opt:"-k 0"
[type: org] overlays/configuration.org $lang:overlays/configuration.$lang.org opt:"-k 0"
[type: org] modules/configuration.org $lang:modules/configuration.$lang.org opt:"-k 0"
For detailed information about po4a.cfg configuration, see man po4a.
When documents are updated and you need to create/update po files,
run the following command.
This generates template (pot) and po files for each language
at the paths configured in po4a.cfg.
po4a --no-translations po4a.cfgEdit the target language po using a po editor. Popular options include Emacs po-mode, poedit, GNOME’s Gtranslator, and KDE’s Lokalize.
After completing translations, generate files with the following command. Since po files are also updated at this time, in practice you only need to run this command.
po4a po4a.cfg