Personal Arch Linux dotfiles and workstation automation managed with Ansible
and go-task.
This repository contains the user-level dotfiles workflow and the former
ans-workstation system automation layer. The default workflow remains
intentionally user-level and sudo-free: it links managed files from
dotfiles/ into $HOME. System-wide workstation configuration and browser,
Thunderbird, and VS Code policies are available only through explicit opt-in
playbooks and go-task targets.
- Arch Linux
gitgo-taskuvsudofor opt-in system or policy tasks- Docker for full validation, Super-Linter, and container-based smoke tests
Install the base tools:
sudo pacman -Sy --noconfirm --needed git go-task uvClone the repository:
_INSTALL_DIR="$HOME/.dotfiles" \
&& git clone https://github.com/tenhishadow/dotfiles.git "$_INSTALL_DIR" \
&& cd "$_INSTALL_DIR"Start with the user-level dry run:
go-task dotfiles:checkApply the user-level dotfiles:
go-taskgo-task runs playbook_install.yml only. That playbook loads
roles/dotfiles, validates the dotfiles contract, creates required user
directories, links managed payload files, and removes a small set of explicit
legacy user config paths.
go-task dotfiles:check runs the default Ansible playbook in check mode with
diff output. The default playbook is user-level, uses become: false, and
loads roles/dotfiles only. It does not apply privileged system or browser
policy configuration.
Taskfile dependency bootstrap runs before Ansible and may install missing local
prerequisites such as uv or git through pacman and sudo on Arch Linux.
The dotfiles workflow can still replace managed destinations with symlinks and
remove explicit legacy user paths, so review
inventory/host_vars/this_host/dotfiles.yml before applying it on another
account or fork.
Do not run go-task system or go-task browser-policies until you have
reviewed managed /etc paths, Docker group behavior, SSHD/sysctl values, the
system package manifest, and browser/Thunderbird/VS Code policy ownership.
Use read-only reports for a local view before applying changes:
go-task doctor
go-task dotfiles:plan
go-task system:report
go-task browser-policies:reporttenhishadow/ans-workstation previously contained the standalone Arch Linux
workstation automation. That layer has been consolidated into this repository.
The consolidation did not change the execution boundary:
go-task/playbook_install.ymlremains user-level and sudo-free.go-task system/playbook_system.ymlapplies the opt-in system layer.go-task browser-policies/playbook_browser_policies.ymlapplies the opt-in browser, Thunderbird, and VS Code policy layer.
The default dotfiles install path must not apply privileged configuration.
| Layer | Command | Privileged | Purpose |
|---|---|---|---|
| User dotfiles | go-task |
No | Link managed dotfiles into $HOME. |
| System workstation | go-task system:check / go-task system |
Yes | Check or apply the opt-in Arch Linux workstation layer. |
| Browser policies | go-task browser-policies:check / go-task browser-policies |
Yes | Check or apply opt-in browser, Thunderbird, and VS Code policies. |
| Validation | go-task verify |
No direct system apply | Run repository validation, linting, documentation checks, and smoke tests. |
Check targets run after Taskfile dependency bootstrap. go-task system:check
then runs playbook_system.yml in Ansible check mode with diff output.
| Path | Purpose |
|---|---|
dotfiles/ |
Canonical user-level payload linked into $HOME. |
inventory/host_vars/this_host/ |
Local host data split by dotfiles, system, security, and browser policy ownership. |
playbook_install.yml |
Default local user-level install playbook. |
playbook_system.yml |
Opt-in privileged Arch Linux workstation playbook. |
playbook_browser_policies.yml |
Opt-in privileged browser, Thunderbird, and VS Code policy playbook. |
roles/dotfiles/ |
User-level dotfiles validation and symlink role. |
roles/system/ |
Arch Linux workstation system provisioning role. |
roles/browser_policies/ |
Enterprise browser, Thunderbird, and VS Code policy role. |
docs/ |
Architecture, adoption, security, migration, and generated operator manuals. |
.github/ |
GitHub Actions, issue forms, PR template, labeler, and release automation. |
.test/ |
Isolated smoke-test fixtures and container test entry points. |
| Document | Purpose |
|---|---|
docs/architecture.md |
Repository layer model and safety boundaries. |
docs/adoption.md |
Practical first-use and forking notes. |
docs/security-notes.md |
Security caveats for this personal workstation baseline. |
docs/privacy-policy-surfaces.md |
Managed privacy, policy, and user-dotfile surfaces. |
docs/migration-from-ans-workstation.md |
Location map for the former ans-workstation layer. |
docs/github-labels.md |
GitHub labeler pipeline and repository label expectations. |
roles/system/README.md |
System role managed paths, validation, and rollback notes. |
roles/browser_policies/README.md |
Browser, Thunderbird, and VS Code policy targets, variables, and rollback notes. |
| Command | Purpose |
|---|---|
go-task |
Apply user-level dotfiles only. |
go-task dotfiles:check |
Dry-run the user-level dotfiles playbook with diff output. |
go-task dotfiles:plan |
Print existing user dotfile destinations and cleanup paths. |
go-task doctor |
Print read-only local tool, managed user-tool, Docker, user, and systemd availability. |
go-task lint |
Run ansible-lint for playbooks, inventory, and roles. |
go-task yamllint |
Run YAML linting through the pinned uv environment. |
go-task vint |
Run Vint with Neovim syntax enabled for Vimscript payloads. |
go-task docs:nvim-keymaps |
Regenerate the Neovim keymap manual. |
go-task docs:nvim-keymaps:check |
Check that the generated Neovim keymap manual is current. |
go-task verify |
Run the full local aggregate validation path, including Super-Linter. |
go-task superlinter |
Run the GitHub Super-Linter container locally. |
go-task test:nvim |
Run the isolated Neovim smoke test. |
go-task test:nvim:mason-tools |
Validate configured Mason package names against the Mason registry. |
go-task test:nvim:profile |
Run the Neovim smoke test, then print startup and loaded-plugin counts. |
go-task system:list |
List tasks in the opt-in system playbook. |
go-task system:report |
Print managed system paths and local Docker/SSHD status. |
go-task system:check |
Bootstrap dependencies, then dry-run the opt-in system playbook. |
go-task system |
Apply the opt-in system playbook. |
go-task test:system |
Run the system role smoke and idempotency test in an Arch Linux container. |
go-task browser-policies:report |
Print managed browser, Thunderbird, and VS Code policy app versions and expected policy paths. |
go-task browser-policies:check |
Dry-run system browser, Thunderbird, and VS Code policy management. |
go-task browser-policies |
Apply system browser, Thunderbird, and VS Code policy management. |
go-task pacdiff |
List pending pacman .pacnew and .pacsave files. |
User dotfiles are managed by playbook_install.yml with:
connection: localbecome: falseroles/dotfiles- explicit symlink mappings from
inventory/host_vars/this_host/dotfiles.yml - explicit cleanup entries from
dotfiles_cleanup_paths
Add new managed payload files under dotfiles/ and add matching mapping
entries in inventory/host_vars/this_host/dotfiles.yml. Mapping entries use
name, repository-relative payload, and absolute dest; the role computes
the source path and creates destination parent directories automatically. Do
not add secrets, browser profiles, caches, local databases, generated test
workspaces, SSH private keys, or GPG private keys.
The managed user payload includes privacy-focused configs for Gemini CLI, K9s,
Git Delta, Terraform CLI, bat, ripgrep, btop, direnv, npm, Yarn, and pip. These
configs are normal dotfiles and do not include account state, kubeconfigs,
tokens, private registries, AI conversation history, or runtime profiles. See
docs/privacy-policy-surfaces.md for the
managed surface list and intentionally unmanaged files.
Generated xdg-user-dirs state such as ~/.config/user-dirs.dirs is not
managed. xdg-user-dirs-update rewrites that path as a regular local file, so
linking it from the repository would make the default dotfiles run non-idempotent.
go-task doctor reports availability and versions for these managed user tools
without reading credentials, kubeconfigs, cloud auth, browser profiles, or mail
profiles.
The Neovim payload uses a structured lazy.nvim setup:
init.lualoads core config first, then the plugin layer.lua/config/lazy.luabootstrapslazy.nvimand honors the pinnedlazy.nvimcommit inlazy-lock.json.lua/config/languages.luais the shared source for Tree-sitter languages and install requirements, LSP server binaries, Mason package lists, formatters, and linters.lua/config/filetypes.luaowns plugin-independent filetype detection so filetype-lazy plugins work for the first opened buffer.lua/config/keymaps_spec.luais the source of truth for user-facing keymaps and generated keymap documentation.lua/plugins/contains all plugin specs; there is no separate kickstart plugin layer.- Automatic linting is save-triggered and limited to lightweight file-local
linters. Heavier project-wide linters are available manually through
:DotfilesLintManualor explicit validation commands for Kubernetes, Helm, Kustomize, Terraform/OpenTofu, Trivy, Gitleaks, and Semgrep. - Formatters are configured for explicit manual use only. Saving a file must not auto-format content or strip whitespace.
NVIM_USE_MASON=offis the default.automakes already-installed Mason tools available without startup installs;alwaysallows Mason to install configured missing tools on startup.- Unused Node.js, Perl, and Ruby remote plugin providers are disabled by
default; Python remains enabled for
python-pynvim.
Neovim 0.11.3+ gets the modern LSP path via vim.lsp.config() and
vim.lsp.enable(). Older Neovim versions keep the core editor config and skip
the LSP plugin layer because current upstream nvim-lspconfig requires
Neovim 0.11.3+. Tree-sitter parser installs need the
tree-sitter CLI (tree-sitter-cli on Arch Linux), a C compiler, and curl;
go-task test:nvim skips that parser install step when those tools are missing
instead of producing noisy compile failures. Cold installs are validated by
go-task test:nvim, including a lockfile drift check after Lazy restore and
Mason registry package-name validation for configured Mason tools.
Startup-sensitive changes can be measured with go-task test:nvim:profile.
User-facing keymaps are documented in docs/nvim-keymaps.md; regenerate it
with go-task docs:nvim-keymaps after keymap changes.
Host variables are split by ownership to keep reviews small and reduce accidental conflicts:
| File | Owns |
|---|---|
inventory/host_vars/this_host/dotfiles.yml |
User-level mappings, extra directories, and cleanup paths. |
inventory/host_vars/this_host/system.yml |
Non-security system settings such as MOTD and journald. |
inventory/host_vars/this_host/security.yml |
SSHD, sysctl, and limits security-sensitive workstation settings. |
inventory/host_vars/this_host/browser_policies.yml |
Browser, Thunderbird, and VS Code policy overrides. |
Keep host variables role-prefixed: dotfiles_*, system_*, or
browser_policies_*. Upstream configuration keys inside settings maps keep
their native casing, for example SSHD, journald, sysctl, Chromium, Firefox,
and VS Code policy keys.
System-wide workstation configuration is opt-in:
go-task system:check
go-task systemThis path runs playbook_system.yml, uses roles/system, and may require
sudo. It manages Arch Linux packages, system drop-ins, selected /etc
configuration, sysctl values, PAM limits, Docker daemon and overlay settings,
cron, reflector, and laptop-related settings.
The system role ships role-owned default tuning for workstation use:
- sysctl defaults for unprivileged BPF,
fq, BBR,somaxconn, and local port range. - PAM limits through
/etc/security/limits.d/10-dotfiles.conf. - Docker overlay module options through
/etc/modprobe.d/99-dotfiles-overlay.conf.
Host-specific additions and overrides belong in
inventory/host_vars/this_host/security.yml; keep role-owned defaults in
roles/system/defaults/main.yml.
See roles/system/README.md for managed paths, validation, and rollback
notes.
Browser, Thunderbird, and VS Code enterprise policies are opt-in:
go-task browser-policies:check
go-task browser-policiesThis path writes root-owned policy files under /etc and intentionally does
not manage runtime browser, Thunderbird, or VS Code profile state. See
roles/browser_policies/README.md for variables, validation, and rollback
notes.
Use the narrowest validation that covers the change:
git diff --check
go-task lint
go-task yamllint
go-task vintUse the aggregate local check before finishing broad repository, role, inventory, documentation, or automation changes:
go-task verifygo-task verify includes the GitHub Super-Linter container and requires a
running Docker daemon. Use the narrower area-specific checks above only when
Docker is unavailable and state that limitation in review notes.
Additional checks by area:
- User dotfiles or symlink mappings:
go-task dotfiles:check, thengo-task - Full local validation, including Super-Linter and JSCPD:
go-task verify - Vimscript payloads:
go-task vint - Neovim keymap docs:
go-task docs:nvim-keymaps:check - Neovim config:
go-task test:nvim - Neovim Mason tool inventory:
go-task test:nvim:mason-tools - Neovim startup-sensitive changes:
go-task test:nvim:profile - System role behavior:
go-task system:checkandgo-task test:system - Browser policy behavior:
go-task browser-policies:check - CI or repository-wide lint changes:
go-task superlinter
go-task verify and go-task superlinter require Docker.
GitHub issue forms and the PR template live under .github/. The labeler is
path-based and mirrors the current repository structure, including dotfiles,
inventory, roles, tests, automation, and AI instructions. Label expectations
are documented in docs/github-labels.md.
GitHub Copilot review guidance lives in .github/copilot-instructions.md,
with path-specific rules under .github/instructions/.
Renovate manages supported dependency updates for GitHub Actions, pre-commit,
Ansible Galaxy requirements, the Python toolchain, and the Super-Linter Docker
image referenced by Taskfile.yml. Renovate intentionally ignores .test/
fixtures because they are detector and smoke-test inputs, not repository
dependency surfaces.
Use go-task deps-upgrade for local, reviewable dependency refreshes. It
updates uv.lock, refreshes the installed Ansible Galaxy collections allowed by
requirements.yml, runs pre-commit autoupdate, updates Neovim
lazy-lock.json, and validates Renovate config. GitHub Actions are updated by
Renovate PRs; use go-task deps-report:github-actions when you want a local
Renovate extraction/dry-run report for workflow dependencies.
Documentation and AI instructions are part of the repository contract. Update
README.md, the nearest AGENTS.md, .github/copilot-instructions.md, and
path-specific .github/instructions/*.instructions.md whenever commands,
roles, inventory ownership, validation paths, automation, or runtime behavior
change.
- Keep the default
go-taskworkflow sudo-free. - Keep privileged behavior behind explicit opt-in tasks.
- Prefer drop-ins and snippets over editing upstream main config files where supported.
- Keep generated and machine-local state out of git.
- Keep repository text, comments, and documentation in English.