feat: public dotfiles tier — no credential, no identity, one installer

Fresh history. This is the repo a throwaway VM clones anonymously: it brings a
machine to a working baseline and carries nothing that makes it mine.

56 files. 50 land in $HOME, 3 are chezmoi metadata, 2 are repo documentation,
1 is the manifest, and a 15-file test harness stays behind in .tests/.

What did not travel, and why:

  encrypted_private_bws-token.age   a real credential; age is dropped entirely
  .chezmoidata/bws.toml             env-var -> secret-id map; belongs with the
                                    tier that can use it
  SECRETS.md                        documentation of the rules, not config
  finish-setup.sh.tmpl              superseded by dotup
  nvim/init.lua.backup              dead file
  dot_claude/**, dot_codex/**,      120 files of agent config, private tier
  dot_pi/**

De-identified rather than dropped:

  .gitconfig   [user], the GitHub ssh rewrite and both Gitea host rewrites are
               identity, not configuration. They move behind an [include] of
               ~/.config/git/config.local, which the private tier writes. Git
               treats a missing include as a no-op, so a public-only machine
               reads the file and stops.
  .zshrc       the two gitea aliases carried a personal domain and a LAN IP.
               They move behind a guarded source of ~/.config/zsh/local.zsh,
               the sibling of the secrets.zsh seam phase 2 established.
  nvim         a commented-out LM Studio endpoint naming a LAN address.
  ghostty      a stale auto-generated header naming an absolute home directory.

Newly captured, never tracked before: ~/.zshenv, ~/.config/gh/config.yml. The
former sourced ~/.cargo/env unguarded, so every zsh on a machine without rustup
printed an error -- the same shape as the unguarded oh-my-zsh source phase 2
fixed. It is guarded now.

.chezmoiexternal.toml grows from one entry to six. oh-my-zsh, powerlevel10k,
zsh-autosuggestions, zsh-ai and tpm were hand-installed and declared nowhere,
which is why `chezmoi init --apply` on a clean box produced a .zshrc that broke
the shell it configures. The theme and both plugins nest under
.oh-my-zsh/custom/, which is what $ZSH_CUSTOM resolves to.

dotup gains an install engine. It resolves each selected package to a channel
(apt, brew, npm, uv, snap, deb, flatpak, tarball, script, builtin) through one
function every consumer reads, probes apt-cache before batching so a name apt
does not know moves to brew instead of failing all thirty, and retries
individually if a batch still fails -- which earned its keep on the first real
container run, where mermaid-cli's puppeteer dependency failed and the other
twelve npm packages installed anyway. --unattended computes safe defaults fresh
from the manifest rather than inheriting a state file, and refuses private and
invasive rows outright even when a stale state file ticks them.

The manifest gains @spec, a second directive kind alongside @needs, carrying the
argument a channel needs but a package name cannot supply -- the scoped npm
name, the flatpak app id, the .deb source. The TSV stays five columns wide.

Three bugs the container runs found, all fixed here:

  * `apt install nodejs` gives you node WITHOUT npm on Ubuntu, so all thirteen
    npm packages failed on a fresh box. The manifest asks apt for both names.
  * A tool installed a moment ago is not on this process's PATH -- uv lands in
    ~/.local/bin, npm -g honours the ~/.npmrc prefix, linuxbrew is outside a
    non-login PATH. Resolved by looking in the places we just wrote to, never by
    exporting a modified PATH.
  * `A || { B && C; }` is one || list, so when `command -v sudo` failed the list
    failed and `set -e` killed dotup at load. On a non-root machine with no
    sudo it died before printing anything. There is a regression test.

.zshenv and .p10k.zsh are marked private_. Both are shell code the login shell
executes and both applied at 664, group-writable. Third occurrence of the class
of bug phase 1 found on .pi/agent/auth.json and phase 2 found on .zshrc; the
first one found on purpose rather than by accident.

Verification: 81 assertions, 81/81 on this box and in ubuntu:24.04, ubuntu:22.04
and debian:12. The installer is driven against a directory of fake package
managers that record what they were asked to do and install nothing, so the
engine is exercised end to end without a package landing on the test machine.
`gitleaks detect` over the full history and the working tree: no leaks found,
with no allowlist and no .gitleaks.toml.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
bcherb2
2026-08-17 00:11:52 -04:00
commit b487b0e855
71 changed files with 7824 additions and 0 deletions
@@ -0,0 +1,162 @@
-- UI enhancement plugins for better visual experience
return {
-- Render markdown with better formatting in buffers
{
"MeanderingProgrammer/render-markdown.nvim",
ft = "markdown",
enabled = true,
dependencies = { "nvim-treesitter/nvim-treesitter", "nvim-tree/nvim-web-devicons" },
opts = {
render_modes = { "n", "i", "c", "t" },
heading = {
enabled = true,
sign = true,
icons = { "󰲡 ", "󰲣 ", "󰲥 ", "󰲧 ", "󰲩 ", "󰲫 " },
},
code = {
enabled = true,
sign = true,
style = "normal",
width = "block",
},
bullet = {
enabled = true,
icons = { "●", "○", "◆", "◇" },
},
pipe_table = {
cell = "trimmed",
},
},
},
-- Highlight function arguments with different colors
-- Makes it easier to distinguish parameters
{
"m-demare/hlargs.nvim",
event = "VeryLazy",
enabled = true, -- Set to false to disable
dependencies = { "nvim-treesitter/nvim-treesitter" },
opts = {
color = "#ef9062",
highlight = {},
excluded_filetypes = {},
paint_arg_declarations = true,
paint_arg_usages = true,
performance = {
parse_delay = 1,
slow_parse_delay = 50,
max_iterations = 400,
max_concurrent_partial_parses = 30,
},
},
},
-- Dim inactive portions of code
-- Great for focusing on specific functions/blocks
-- Toggle with :Twilight
{
"folke/twilight.nvim",
cmd = { "Twilight", "TwilightEnable", "TwilightDisable" },
enabled = true, -- Set to false to disable
opts = {
dimming = {
alpha = 0.25,
color = { "Normal", "#ffffff" },
term_bg = "#000000",
inactive = false,
},
context = 10,
treesitter = true,
expand = {
"function",
"method",
"table",
"if_statement",
},
},
keys = {
{ "<leader>ut", "<cmd>Twilight<cr>", desc = "Toggle Twilight" },
},
},
-- Theme switcher with live preview
-- Browse and switch between colorschemes with instant preview
-- Open with :Themery
{
"zaldih/themery.nvim",
lazy = false, -- Load on startup to apply saved theme
priority = 1000, -- Load before other plugins
enabled = true,
keys = {
{ "<leader>uT", "<cmd>Themery<cr>", desc = "Theme Picker" },
},
config = function()
require("themery").setup({
themes = {
-- Catppuccin variants
{ name = "Catppuccin Latte", colorscheme = "catppuccin-latte" },
{ name = "Catppuccin Frappe", colorscheme = "catppuccin-frappe" },
{ name = "Catppuccin Macchiato", colorscheme = "catppuccin-macchiato" },
{ name = "Catppuccin Mocha", colorscheme = "catppuccin-mocha" },
-- Tokyo Night variants
{ name = "Tokyo Night", colorscheme = "tokyonight" },
{ name = "Tokyo Night - Night", colorscheme = "tokyonight-night" },
{ name = "Tokyo Night - Storm", colorscheme = "tokyonight-storm" },
{ name = "Tokyo Night - Day", colorscheme = "tokyonight-day" },
{ name = "Tokyo Night - Moon", colorscheme = "tokyonight-moon" },
-- Kanagawa variants
{ name = "Kanagawa", colorscheme = "kanagawa" },
{ name = "Kanagawa Wave", colorscheme = "kanagawa-wave" },
{ name = "Kanagawa Dragon", colorscheme = "kanagawa-dragon" },
{ name = "Kanagawa Lotus", colorscheme = "kanagawa-lotus" },
-- Rose Pine variants
{ name = "Rose Pine", colorscheme = "rose-pine" },
{ name = "Rose Pine Moon", colorscheme = "rose-pine-moon" },
{ name = "Rose Pine Dawn", colorscheme = "rose-pine-dawn" },
-- Nightfox variants
{ name = "Nightfox", colorscheme = "nightfox" },
{ name = "Nordfox", colorscheme = "nordfox" },
{ name = "Dawnfox", colorscheme = "dawnfox" },
{ name = "Duskfox", colorscheme = "duskfox" },
{ name = "Carbonfox", colorscheme = "carbonfox" },
{ name = "Terafox", colorscheme = "terafox" },
-- Gruvbox variants
{ name = "Gruvbox Dark", colorscheme = "gruvbox" },
{ name = "Gruvbox Material", colorscheme = "gruvbox-material" },
-- Popular themes that actually work
{ name = "Nord", colorscheme = "nord" },
{ name = "Everforest", colorscheme = "everforest" },
{ name = "Dracula", colorscheme = "dracula" },
{ name = "OneDark", colorscheme = "onedark" },
{ name = "Solarized", colorscheme = "solarized" },
{ name = "Monokai Pro", colorscheme = "monokai-pro" },
{ name = "Cyberdream", colorscheme = "cyberdream" },
{ name = "Oxocarbon", colorscheme = "oxocarbon" },
{ name = "Melange", colorscheme = "melange" },
{ name = "Nightfly", colorscheme = "nightfly" },
{ name = "Moonfly", colorscheme = "moonfly" },
{ name = "Sonokai", colorscheme = "sonokai" },
{ name = "Edge", colorscheme = "edge" },
{ name = "Ayu", colorscheme = "ayu" },
{ name = "Material", colorscheme = "material" },
{ name = "Palenight", colorscheme = "palenight" },
{ name = "GitHub Dark", colorscheme = "github_dark" },
{ name = "GitHub Dark Dimmed", colorscheme = "github_dark_dimmed" },
{ name = "GitHub Light", colorscheme = "github_light" },
{ name = "Tokyodark", colorscheme = "tokyodark" },
{ name = "Darkplus", colorscheme = "darkplus" },
{ name = "Poimandres", colorscheme = "poimandres" },
{ name = "Modus Vivendi", colorscheme = "modus" },
{ name = "Zenbones", colorscheme = "zenbones" },
{ name = "Nordic", colorscheme = "nordic" },
{ name = "Jellybeans", colorscheme = "jellybeans" },
{ name = "Tender", colorscheme = "tender" },
{ name = "Horizon", colorscheme = "horizon" },
{ name = "Moonlight", colorscheme = "moonlight" },
{ name = "Lackluster", colorscheme = "lackluster" },
},
livePreview = true, -- Apply theme while navigating
})
end,
},
}