diff --git a/5000blogs.yml b/5000blogs.yml
new file mode 100644
index 0000000..9ff995b
--- /dev/null
+++ b/5000blogs.yml
@@ -0,0 +1,36 @@
+# config for doth.5000K.org
+address: ":8080"
+
+paths:
+ template: "https://raw.githubusercontent.com/5000K/5000blogs/refs/heads/main/template/template.docs.html"
+ theme: "https://raw.githubusercontent.com/5000K/5000blogs/refs/heads/main/template/theme.docs.css"
+ icon: "https://raw.githubusercontent.com/5000K/doth/refs/heads/main/icon.png"
+
+
+log_level: 'info'
+page_size: 25
+
+blog_name: 'doth'
+
+nav_links:
+ - name: 'Pages'
+ url: '/posts'
+
+site_url: 'https://doth.5000K.org'
+feed_description: 'doth - Content'
+feed_size: 20 # number of posts to include in RSS and Atom feeds
+rss_content: 'none' # 'none' (default), 'text' (plain text), or 'html' (rendered HTML)
+
+og_image:
+ enabled: true
+ bg_color: '#111111'
+ text_color: '#f0f0f0'
+ sub_color: '#999999'
+ accent_color: '#7eb8f7'
+
+rescan_cron: "0 0,12 * * *"
+
+sources:
+ - type: git
+ url: "https://github.com/5000K/doth"
+ dir: docs
diff --git a/AGENTS.md b/AGENTS.md
index b15a9e0..8c32a99 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -26,4 +26,8 @@ This is explicitly allowed. You are required to read all source code that is rel
Only use the actual code as the source of truth.
-Don't ever use existing documentation as a source of truth, as it may be outdated.
+Be careful to follow the documentation style. Use short sentences, be exact and technical. Your audience has general technical knowledge, but no knowledge about this project specifically.
+Rather than using dashes, semicolons or multiple commas in a sentence, try to split that sentence into multiple sentences instead. Add short examples for more complex issues.
+
+
+Do not use existing documentation as a source of truth when writing more documentation. Ignore it if it isn't relevant to your writing. If it is relevant to your writing, assume it is wrong until proven correct.
diff --git a/README.md b/README.md
index a91a038..27efd70 100644
--- a/README.md
+++ b/README.md
@@ -1,70 +1,55 @@
-# 
+
+
+
-[](https://github.com/5000K/doth/actions/workflows/ci.yml)
+---
-**doth** is a go-based .config manager for linux.
+
-> **DOTH IS IN DEVELOPMENT. WE AT 5000K ALREADY USE IT, BUT WE DON'T RECOMMEND YOU DO THE SAME AT THIS POINT OF TIME.**
+[](https://github.com/5000K/doth/actions/workflows/ci.yml) [](https://github.com/5000K/doth/actions/workflows/release.yml)
-```
-go install github.com/5000K/doth
-```
+**doth** is a distro-agnostic .config build system for linux systems.
-## The idea
+> doth is a pre-release version. we actively use it at 5000K and it proved to be stable. We will try to not introduce breaking changes until v1.0, but doth is still pre-release and should be treated like it.
-**doth** can simply copy or symlink your files from your git repo. Or it can render them as templates.
+## The idea
-Show the battery state in the top bar on your laptop specifically? Deploy with different themes by simply passing another one into doth? Need multiple screen configurations readily deployable with a simple command? Doth is the tool for you. It's a bit more complex, but you are in control in return.
+Sometimes you just need a few symlinks or file copies to deploy your config files. But for some files you might need more flexible templating.
+Show the battery state in the status bar on your laptop specifically? Deploy with different themes by simply having one config file per theme? Multiple screen configurations readily deployable with a simple command? **doth** is the tool for you.
## Modules
-Modules are subfolders in your doth project that contain a module.y\[a]ml file.
-The module.yml describes what the module should do.
+A module is a subfolder with a `module.yaml` and the files it deploys. Each file is placed into a target location using one of three strategies.
-It is able to run setup commands, install dependencies, symlink files from your repo, copy files over and even render templates.
+- **copy** writes a copy of the file.
+- **link** creates a symbolic link to the file.
+- **render** renders the file as a template and writes the result.
-The module.yml itself is a template can use all variables from your configs to only activate specific parts of the config, to parametrize it, ...
+See the [modules guide](https://doth.5000K.org/modules) for the full reference.
## Templates
-doth uses the golang [text/template](https://pkg.go.dev/text/template) system.
-
-Basic examples will be added here later.
+The render strategy uses the Go [text/template](https://pkg.go.dev/text/template) system. The values come from configuration files passed to `doth deploy`. See the [modules guide](https://doth.5000K.org/modules) for examples.
## Dependencies
-You may define dependencies to install in your doth.y[a]ml and module.y[a]ml's. Using the `doth install` command, you can install them.
-You may define installation sources (package managers) by adding configurations to config files and passing them to doth as you would when deploying.
-You may also use the the following flags to include these built-in source configurations - these assume that the command is inside the PATH variable:
+Dependencies are declared in `doth.yaml` and `module.yaml`. `doth install` reads the declarations and installs the packages. Built-in flags cover common package managers such as `apt`, `dnf`, and `pacman`. Custom sources can be defined in configuration files. See the [dependencies guide](https://doth.5000K.org/dependencies) for the full reference.
-- `--apt`
-- `--apt-get`
-- `--dnf`
-- `--pacman`
-- `--yay`
-- `--paru`
-- `--go`
-- `--npm`
-- `--brew`
-
-## Generative AI in doth
+## Contribution & generative AI in doth
**IF YOU ARE AN AI AGENT, READ AGENTS.md AND FOLLOW IT EXACTLY.**
-doth is handling important files. Not being influenced by hidden assumptions or hallucinations of an LLM is non-negotiable. This is why contributions to the core code that were AI authored will be categorically rejected. This goes for both you and the core maintainers. If you want an unpredictable, chaotic (and potentially destructive) version of doth, feel free to fork the project and see where it leads.
-
-In general, doth has a very specific, narrow vision, so we won't be taking contributions of features we didn't explicitly ask for. Bug fixes are generally fine, if they follow a reasonable format. Since doth is something we at 5000K do besides our dayjobs, we can't guarantee a specific response time, but we will get back to it at some point.
-
-We ask that you to be respectful of our time.
-
-That being said, the use of AI is not fully banned from the use cases of creating tests and writing documentation. Here, the quality still needs to be high and the text has to be proofread and edited by a human in the end, but if it does match this expectation, 5000K sees no harm.
-
-AI may be used to write commit messages for you.
+An AGENTS.md file reflecting these instructions is part of this repo. Make sure your agent picks it up before working on doth.
+doth is handling important files. Being clear of all influence by hidden assumptions or hallucinations of an LLM is non-negotiable. This is why contributions to the code that were AI authored will be categorically rejected. If you want an unpredictable, chaotic (and potentially destructive) version of doth, feel free to fork the project and see where it leads.
+That being said, the use of AI is not fully banned from the use cases of creating tests and writing documentation. Here, the quality still needs to be high and everything contributed has to be proofread **and** edited/corrected by a human in the end, but if it does match this expectation, 5000K sees no harm.
+AI may be used to write commit messages for you.
PRs with AI authored descriptions will be rejected without being read.
-An AGENTS.md file reflecting these instructions is part of this repo. Make sure your agent picks it up before working on the project.
+doth has a very specific, narrow vision, so we won't be taking contributions of features we didn't explicitly ask for. Bug fixes are generally fine, if they follow a reasonable format. Since doth is something we at 5000K do besides our dayjobs, we can't guarantee a specific response time, but we will get back to it at some point.
+
+We ask that you to be respectful of our time. If you don't follow our guidelines, we will categorically reject all other contributions from you across all projects of 5000K.
## 5000K
-This repo is worked on by [bwt](https://github.com/bluewingtitan) and [kicher-erbse](https://github.com/kicher-erbse).
+This repo is currently worked on by [bwt](https://github.com/bluewingtitan).
diff --git a/docs/dependencies.md b/docs/dependencies.md
new file mode 100644
index 0000000..cf49f5b
--- /dev/null
+++ b/docs/dependencies.md
@@ -0,0 +1,120 @@
+---
+title: Dependencies
+description: Declaring dependencies and installing them with built-in or custom sources.
+author: 5000K
+---
+
+# Dependencies
+
+doth can install the packages your configuration depends on. Dependencies are declared in the `doth.yaml` and in each `module.yaml`. `doth install` reads the declarations and runs the install commands.
+
+## Declaring dependencies
+
+A dependency has a name and a map of source names to package names. The source names are matched against the package sources you pass to `doth install`. The same shape is used in `doth.yaml` and in `module.yaml`.
+
+```yaml
+# doth.yaml
+deps:
+ - name: curl
+ packages:
+ pacman: curl
+ apt: curl
+ brew: curl
+```
+
+The name is used to deduplicate dependencies across the project. When the same name appears in multiple modules, the last declaration wins. See [YML Reference](./yml.md) for the full dependency format.
+
+## Built-in sources
+
+`doth install` knows a few common package managers out of the box. Pass one or more of the flags below to enable a built-in source. The command is run with `/bin/sh -c`. The `{package}` placeholder is replaced with the package name from the dependency declaration.
+
+| Flag | Command |
+|-------------|----------------------------------------|
+| `--apt` | `sudo apt install --yes {package}` |
+| `--apt-get` | `sudo apt-get install --yes {package}` |
+| `--dnf` | `sudo dnf install --assumeyes {package}` |
+| `--pacman` | `sudo pacman -S --noconfirm {package}` |
+| `--yay` | `yay -S --noconfirm {package}` |
+| `--paru` | `paru -S --noconfirm {package}` |
+| `--go` | `go install {package}` |
+| `--npm` | `npm install -g {package}` |
+| `--brew` | `yes \| brew install {package}` |
+
+```sh
+doth install --pacman --yay
+```
+
+## Custom sources
+
+Define custom sources in a configuration file. Pass the file to `doth install` with `--config`. The same file can also be passed to `doth deploy`. Multiple configuration files are merged in order. Later files take precedence over earlier files for the same key.
+
+```yaml
+# pacman.yaml
+packageSources:
+ - name: pacman
+ command: sudo pacman -S {package}
+```
+
+The `{package}` placeholder is replaced with the package name from the dependency. The command runs unescaped. Be careful when adding custom sources. Built-in sources and custom sources can be mixed.
+
+```sh
+doth install --config pacman.yaml
+```
+
+The `packageSources` list is itself merged across files. Sources with the same `name` are deduplicated.
+
+## Matching
+
+`doth install` walks the sources in order. The first source that has an entry in the dependency's `packages` map wins. Sources without a matching entry are skipped. Dependencies with no matching source are skipped and a message is printed.
+
+Custom sources from configuration files are tried first. Built-in sources come after them. The built-in sources follow a fixed order. The order is `apt`, `apt-get`, `dnf`, `pacman`, `yay`, `paru`, `go`, `npm`, `brew`. The order of built-in flags on the command line does not matter.
+
+## Confirmation
+
+`doth install` always asks for confirmation before running commands. The shell is given access to your environment. Pass `--dry` to print the commands that would run without executing them.
+
+The `--silent` flag runs each command without piping its output. Use it for unattended scripts.
+
+## Examples
+
+A `doth.yaml` declaring two dependencies.
+
+```yaml
+deps:
+ - name: ripgrep
+ packages:
+ pacman: ripgrep
+ apt: ripgrep
+ brew: ripgrep
+ - name: i3
+ packages:
+ pacman: i3-wm
+```
+
+Install both on a pacman system.
+
+```sh
+doth install --pacman
+```
+
+A `pacman.yaml` that defines a custom source. It is useful when you want a non default install command.
+
+```yaml
+packageSources:
+ - name: pacman
+ command: sudo pacman -S --needed {package}
+```
+
+A `module.yaml` that declares its own dependencies. Module dependencies are merged with the top level dependencies of `doth.yaml`.
+
+```yaml
+# modules/i3/module.yaml
+target: ~/.config/i3/
+files:
+ - name: config
+ strategy: render
+deps:
+ - name: i3-gaps
+ packages:
+ aur: i3-gaps
+```
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..13aca76
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,68 @@
+---
+title: Overview
+description: An introduction to doth, what it solves, and who it is for.
+author: 5000K
+---
+
+# Overview
+
+doth is a tool that manages configuration files on Linux. It treats your dotfiles as a modular repository. The repository is version controlled. The same repository can be deployed onto many machines with different settings.
+
+## The problem
+
+Configuration files are scattered across the home directory. They are tedious to back up. They are tedious to set up on a new machine. The usual approach is a single git repository and a few symlinks. This works for one machine. It breaks down when you have several machines with different needs.
+
+## What doth does
+
+doth reads a project that contains a `doth.yaml` and a folder of modules. Each module declares where its files go. `doth deploy` then places the files.
+
+Three placement strategies are available. The strategy is chosen per file.
+
+- **copy** writes a copy of the file into the target location.
+- **link** creates a symbolic link from the target location into the repository.
+- **render** runs the file through a template engine and writes the result.
+
+The template engine is the Go standard library `text/template`. The values come from one or more configuration files. The user passes those files to `doth deploy`. This lets one repository serve many machines.
+
+doth can also install the packages your configuration depends on. See [Dependencies](./dependencies.md) for details.
+
+## Who it is for
+
+doth is for people who want to manage their dotfiles flexibly. It is for people who run more than one machine. It is for people who want different flavours of the same configuration files ready to deploy - on different machines, or on a single one. It is for people who want to switch themes or layouts by passing a different configuration file.
+
+doth is not for people with a handful of static dotfiles on a single machine. A plain git repository with symlinks is enough for that.
+
+## The shape of a project
+
+A doth project is a directory. It contains a `doth.yaml` and a `modules` directory. The `doth.yaml` holds top level settings and top level dependencies. Each subfolder of `modules/` is a module. The module folder contains a `module.yaml` and the files the module deploys.
+
+`doth.yaml` and the module files are the definition files of a doth project. They define how to deploy what.
+
+```
+my-dotfiles/
+├── doth.yaml
+├── modules/
+│ ├── bash/
+│ │ ├── module.yaml
+│ │ └── .bashrc
+│ └── nvim/
+│ ├── module.yaml
+│ └── init.lua
+```
+
+Configuration files are kept seperately to the definition files. They hold the values that vary between unique configurations. They are passed to `doth deploy` with the `--config` flag.
+
+```
+my-dotfiles/
+├── configs/
+│ ├── laptop.yaml
+│ └── server.yaml
+```
+
+## Where to go next
+
+- [Setup](./setup.md) covers installing `doth` and creating a new project.
+- [The Wrapper Script](./wrapper.md) explains the self contained workflow for deploying a project on a new machine.
+- [Modules](./modules.md) covers writing modules.
+- [Dependencies](./dependencies.md) covers the package install system.
+- [YML Reference](./yml.md) covers the full format of the YAML files.
diff --git a/docs/modules.md b/docs/modules.md
new file mode 100644
index 0000000..d3555cd
--- /dev/null
+++ b/docs/modules.md
@@ -0,0 +1,141 @@
+---
+title: Modules
+description: Writing modules, choosing file strategies, and using templates.
+author: 5000K
+---
+
+# Modules
+
+A module is a folder inside the `modules` directory of your project. The folder contains a `module.yaml` and the files and folders the module deploys. The `module.yaml` describes where the files go and how. The `modules` directory is configurable through the top level `modulePath` field of `doth.yaml`.
+
+## The shape of a module
+
+A module is a folder with a `module.yaml` and the files it owns.
+
+```
+modules/
+└── bash/
+ ├── module.yaml
+ └── .bashrc
+```
+
+The folder name is the module name. It must be a valid directory name. Empty names, `.` and `..`, and names containing path separators are rejected. You may use `~` for paths relative to the current users home directory.
+
+## Adding a module
+
+`doth add` creates a new module from an existing folder of files. It copies the files into the new module folder. It writes a `module.yaml` that uses the `copy` strategy for each imported file.
+
+```sh
+doth add --name bash --target ~/.config/bash
+```
+
+The `--name` flag is the module's folder name. The folder appears under `modulePath`. The `--target` flag is the source path to import from. It should be a directory of files. The `module.yaml` is set to deploy into that same directory by default.
+
+The flags are listed below.
+
+| Flag | Description |
+| ----------------- | --------------------------------------------------------------------------- |
+| `--name` | Internal name of the module. Required. |
+| `--target` | Source path of the files to import. Required. |
+| `--glob` | Glob pattern of files to include. Relative to the target path. |
+| `--skip-existing` | Do not copy files from the target into the module. |
+| `--destructive` | Delete and recreate the module if it already exists. Asks for confirmation. |
+| `--dry` | Print the actions that would be taken. Do not perform them. |
+| `--verbose` | Print verbose output. |
+| `--autoconfirm` | Answer all prompts with yes. |
+
+The `--target` path is also written to the module's `target` field. Files in the module are deployed into that path. Edit the field when you want the module's deploy target to differ from its import source.
+
+### Manual module creation
+
+You can also create a module by hand. Make the folder, drop your files in, and write a `module.yaml`.
+
+```
+mkdir -p modules/bash
+cp ~/.bashrc modules/bash/.bashrc
+```
+
+```yaml
+# modules/bash/module.yaml
+target: ~/
+files:
+ - name: .bashrc
+ strategy: copy
+```
+
+## The module file
+
+A `module.yaml` has four fields.
+
+| Field | Type | Description |
+| -------- | ------ | ------------------------------------------------------------------------------------------ |
+| `target` | string | Base path the module's files are deployed into. Each file lands at `target/`. |
+| `skip` | bool | When `true`, the module is ignored by `doth deploy`. |
+| `files` | list | The file entries to deploy. See below. |
+| `deps` | list | Dependencies required by the module. See [Dependencies](./dependencies.md) for the format. |
+
+A file entry under `files` has three fields.
+
+| Field | Type | Description |
+| ---------- | ------ | --------------------------------------------------------------------------------------------------------------- |
+| `name` | string | The file name or relative path inside the module folder. May contain glob patterns. |
+| `strategy` | string | Required. One of `copy`, `link`, or `render`. |
+| `target` | string | Optional override for the deployment path. The file lands at this exact path instead of `module.target/`. |
+
+## File strategies
+
+### copy
+
+`copy` reads the file from the module folder and writes a copy to the target location. Existing files at the target are replaced. Directories are walked recursively. Each file inside the directory is copied individually.
+
+### link
+
+`link` creates a symbolic link at the target location. The link points into the module folder. Edits made in the module folder are visible at the target immediately. Existing files at the target are removed and replaced with a link.
+
+### render
+
+`render` reads the file as a Go `text/template`. It executes the template with the values from the configuration files. It writes the result to the target location. Existing files at the target are replaced.
+
+Templates use Go's `text/template` syntax. Map values are accessed through the `index` function. Use `default` to provide a fallback.
+
+```jsonc
+// modules/waybar/config.jsonc
+{
+ // ...
+ "battery": {{ index . "show-battery" | default false }},
+ "theme": "{{ index . "theme" | default "light" }}" // could also be simplified to {{ .theme | default "light" }}, since the key 'theme' does not contain a dash -
+}
+```
+
+### Configuration
+
+The configuration file is a YAML or JSON object. Its top level keys become the template's data context.
+
+For the waybar example above, imagine this configuration file:
+
+```yaml
+# laptop.yaml
+show-battery: true
+theme: dark
+```
+
+Pass a configuration file to `doth deploy`.
+
+```sh
+doth deploy --config laptop.yaml
+```
+
+See [the Go template documentation](https://pkg.go.dev/text/template) for the full template syntax.
+
+## Skipping a module
+
+Set `skip: true` in a `module.yaml` to keep the module in the repository without deploying it. This is useful for keeping alternate configurations around.
+
+```yaml
+# modules/work-laptop/module.yaml
+skip: true
+target: ~/
+files:
+ - name: .bashrc
+ strategy: copy
+```
diff --git a/docs/setup.md b/docs/setup.md
new file mode 100644
index 0000000..24683c4
--- /dev/null
+++ b/docs/setup.md
@@ -0,0 +1,126 @@
+---
+title: Setup
+description: Installing the doth binary and initializing a new project.
+author: 5000K
+---
+
+# Setup
+
+A new doth project needs the `doth` binary and a project directory. Each release of doth publishes two ways to get a working installation. The pre-built binaries cover the common platforms. The init script handles the full bootstrap on any supported system.
+
+## Pre-built binaries
+
+Each release attaches binaries for five platforms. The supported targets are `linux/amd64`, `linux/arm64`, `linux/arm`, `darwin/amd64`, and `darwin/arm64`. The file name is `doth---`. Place the binary on your `PATH` and make it executable.
+
+```sh
+curl -Ls -O https://github.com/5000K/doth/releases/latest/download/doth-v0.0.13-linux-amd64
+chmod +x doth-v0.0.13-linux-amd64
+sudo mv doth-v0.0.13-linux-amd64 /usr/local/bin/doth
+```
+
+The release also contains four extra files. They are used by the init script and the wrapper script.
+
+| File | Purpose |
+|----------------|----------------------------------------------------------|
+| `version.txt` | The latest version number. Used to detect updates. |
+| `doth.sh` | The wrapper script. See below. |
+| `doth-init.sh` | The bootstrap script. See below. |
+| `LICENSE` | The license of the project. |
+
+## The init script
+
+The `doth-init.sh` script is published with every release. It bootstraps a self-contained doth installation in the current directory. It downloads Go into `./.doth/go/`. It installs the latest `doth` binary into `./.doth/gopath/bin/`. It then runs `doth init --modules ./modules --verbose` to create the project.
+
+Run the script in an empty directory.
+
+```sh
+curl -Ls -O https://github.com/5000K/doth/releases/latest/download/doth-init.sh
+chmod +x doth-init.sh
+./doth-init.sh
+```
+
+The script puts the Go toolchain and the `doth` binary inside the project's `.doth/` directory. Both are out of the way of your files. The `doth` binary is on the `PATH` only for the duration of the script.
+
+The init script also generates a [doth wrapper](./wrapper.md).
+
+## The wrapper script
+
+The wrapper script installs and updates `doth` on demand. It keeps the Go toolchain and the `doth` binary inside the project's `.doth/` directory. It is a portable way to run `doth` without installing it system wide.
+
+There are two ways to create a wrapper script.
+
+The `doth init` command can write one for you. Pass `--wrapper` on init.
+
+```sh
+doth init --wrapper
+```
+
+The `doth wrapper` command prints the wrapper to stdout. Redirect it into a file.
+
+```sh
+doth wrapper > doth.sh
+chmod +x doth.sh
+```
+
+The wrapper behaves as follows.
+
+- It checks for a `doth` binary on the `PATH`. It runs the binary if it is there.
+- It downloads Go and installs the latest `doth` when no binary is on the `PATH`.
+- It updates the installed `doth` when the latest version is newer than the running version. It rewrites itself with `doth wrapper` after the update.
+- It reads a `doth.lock` file in the project root. It uses the version inside the file when present. It does not auto-update locked projects.
+
+Run `doth` through the wrapper from the project root.
+
+```sh
+./doth.sh deploy
+```
+
+See [The Wrapper Script](./wrapper.md) for the value the wrapper adds to the workflow.
+
+## Locking the version
+
+A locked project does not auto-update. This is useful for production machines. Use `doth lock` to write a `doth.lock` file. The file contains the version of the running `doth` binary by default. Pass `--version` to pin to a different version.
+
+```sh
+doth lock
+doth lock --version v0.0.13
+```
+
+Use `doth unlock` to remove the lock file. The wrapper resumes auto-updating.
+
+The lock only affects the wrapper. It has no effect on `doth` binaries installed by other means.
+
+## `doth init`
+
+`doth init` creates a new project in the current directory. It writes a `doth.yaml`, a `modules` directory, a `.doth/` directory for local state, and a `.gitignore`. Pass `--wrapper` to also write a `doth.sh` wrapper script.
+
+The flags are listed below.
+
+| Flag | Description |
+|-----------------|----------------------------------------------------------------------|
+| `--modules` | Directory for modules. Defaults to `./modules`. |
+| `--wrapper` | Also write a `doth.sh` wrapper script. |
+| `--destructive` | Delete and recreate an existing project. Asks for confirmation. |
+| `--dry` | Print the actions that would be taken. Do not perform them. |
+| `--verbose` | Print verbose output. |
+| `--autoconfirm` | Answer all prompts with yes. |
+
+The default `doth.yaml` looks like this.
+
+```yaml
+modulePath: "./modules"
+requireConfig: false
+deps:
+ - name: curl
+ packages:
+ pacman: curl
+ apt: curl
+ brew: curl
+dothVersionDoNotEditManually: 1
+```
+
+The default `.gitignore` ignores the `.doth/` directory.
+
+## Running as root
+
+`doth deploy` and `doth add` warn and ask for confirmation when run as root. The recommended approach is to run `doth` as the user that owns the target files. The confirmation can be bypassed with `--autoconfirm`.
diff --git a/docs/wrapper.md b/docs/wrapper.md
new file mode 100644
index 0000000..2c96e42
--- /dev/null
+++ b/docs/wrapper.md
@@ -0,0 +1,35 @@
+---
+title: The Wrapper Script
+description: A self-contained way to run doth without installing Go or doth globally.
+author: 5000K
+---
+
+# The Wrapper Script
+
+The wrapper is a small shell script that ships with a doth project. It manages the project's own `doth` installation. The `doth` binary and it's dependencies live inside the project's `.doth/` directory. Nothing is installed system wide.
+
+## What it solves
+
+Setting up a new machine needs `doth` to deploy the project. Installing it by hand means installing Go first, then `doth`, then keeping both up to date. The wrapper skips all of that.
+
+## Who it is for
+
+The wrapper is for anyone who wants to set up a new computer without installing Go or `doth` globally before doing the setup. This way, doth is self-contained and your working environment is not influenced by you using doth in any way.
+
+## The workflow
+
+Setting up a new computer has two steps.
+
+1. Clone the project.
+2. Run `./doth.sh deploy` and/or `./doth.sh install {package manager flags/configs}`.
+
+```sh
+git clone https://github.com/you/dotfiles.git ~/dotfiles
+cd ~/dotfiles
+./doth.sh deploy # deploy config files
+./doth.sh install --pacman # install deps
+```
+
+The wrapper downloads Go and `doth` on the first run. Both stay inside the project folder. The wrapper updates both in place when new versions appear. You can lock the used version using the `doth lock` command, and unlock it (=> "use newest version") using the `doth unlock` command. The wrapper itself does not touch anything (files or environment) outside the project.
+
+See [Setup](./setup.md) for the lock workflow and how to create the wrapper.
diff --git a/docs/yml.md b/docs/yml.md
new file mode 100644
index 0000000..c5c085c
--- /dev/null
+++ b/docs/yml.md
@@ -0,0 +1,97 @@
+---
+title: YML Reference
+description: The full format reference for doth.yaml, module.yaml, and configuration files.
+author: 5000K
+---
+
+# YML Reference
+
+This page documents the YAML formats used by doth. It is a reference. See [Setup](./setup.md), [Modules](./modules.md), and [Dependencies](./dependencies.md) for usage guides.
+
+## `doth.yaml`
+
+The top level configuration of a project. Located at the project root as `doth.yaml` or `doth.yml`.
+
+| Field | Type | Description |
+|--------------------------------|--------|------------------------------------------------------------------------------------------------------|
+| `modulePath` | string | Path to the directory that contains modules. Relative to the project root. Defaults to `./modules`. |
+| `deps` | list | Top level dependencies. See [Dependencies](./dependencies.md) for the format. |
+| `requireConfig` | bool | When `true`, `doth deploy` should refuse to run without configuration files. Currently not enforced. |
+| `dothVersionDoNotEditManually` | int | The format version of the file. Do not edit by hand. Currently not checked. |
+
+## `module.yaml`
+
+Located inside each module folder as `module.yaml` or `module.yml`.
+
+| Field | Type | Description |
+|----------|--------|-----------------------------------------------------------------------------------|
+| `target` | string | Base path the module's files are deployed into. Each file lands at `target/`. |
+| `skip` | bool | When `true`, the module is ignored by `doth deploy`. |
+| `files` | list | File entries that describe what to deploy. See below. |
+| `deps` | list | Module level dependencies. Merged with the top level `deps`. |
+
+## File entry
+
+Each entry under `files` describes a single file or glob pattern.
+
+| Field | Type | Description |
+|------------|--------|------------------------------------------------------------------------------------------------------|
+| `name` | string | The file name or relative path inside the module folder. May contain glob patterns. |
+| `strategy` | string | Required. One of `copy`, `link`, or `render`. Empty or unknown values cause an error on deploy. |
+| `target` | string | Override for the deployment path. The file lands at this path instead of `module.target/`. |
+
+## Strategies
+
+| Value | Behavior |
+|----------|-------------------------------------------------------------------------------------------------------|
+| `copy` | Copies the file from the module folder to the target location. Directories are walked recursively. |
+| `link` | Creates a symbolic link at the target location that points into the module folder. |
+| `render` | Executes the file as a Go `text/template` with the values from the deployment config files. |
+
+## Dependency
+
+A single entry in a `deps` list.
+
+| Field | Type | Description |
+|------------|--------|----------------------------------------------------------------------------------------------|
+| `name` | string | A human readable name. Used to deduplicate dependencies across the project. |
+| `packages` | map | A map from source name to the package name in that source. The first matching source wins. |
+
+## Configuration file
+
+A configuration file is passed to `doth deploy` or `doth install` with the `--config` flag. The file is YAML or JSON. Multiple files are merged in order. Later files take precedence over earlier files for the same key. Maps are merged recursively. Lists are concatenated and deduplicated. Scalars are replaced.
+
+The full list of recognized fields follows. Unrecognized fields are preserved in the merged result. They are not used by `doth` directly.
+
+### `packageSources`
+
+Used by `doth install`. A list of package source definitions.
+
+| Field | Type | Description |
+|-----------|--------|----------------------------------------------------------------------------------------------|
+| `name` | string | The source name. Referenced from a dependency's `packages` map. |
+| `command` | string | The shell command. The `{package}` placeholder is replaced with the package name from the dependency. |
+
+### Template data
+
+Used by `doth deploy`. The top level keys of the configuration file become the template's data context. Templates access them through Go's `index` function.
+
+```yaml
+# laptop.yaml
+show-battery: true
+theme: dark
+```
+
+```jsonc
+// modules/waybar/config.jsonc
+{
+ "battery": {{ index . "show-battery" | default false }},
+ "theme": "{{ index . "theme" | default "light" }}"
+}
+```
+
+```sh
+doth deploy --config laptop.yaml
+```
+
+The dot is the data context. To access a nested value, walk the structure with `index`. Use `default` to provide a fallback. See [the Go template documentation](https://pkg.go.dev/text/template) for the full syntax.
diff --git a/model/constants/doth-init.sh b/model/constants/doth-init.sh
index c3b206b..299a368 100755
--- a/model/constants/doth-init.sh
+++ b/model/constants/doth-init.sh
@@ -64,7 +64,7 @@ if ! command -v doth &> /dev/null; then
fi
# initialize a fresh doth project
-doth init --modules ./modules --verbose
+doth init --modules ./modules --verbose --wrapper
echo "doth is initialized and ready to use!"
echo "instead of installing doth globally, use ./doth.sh to run the current version of doth."
diff --git a/template/template.go b/template/template.go
index 00c28a0..2e95e72 100644
--- a/template/template.go
+++ b/template/template.go
@@ -7,7 +7,15 @@ import (
)
func RenderTemplate(templateStr string, data any) (string, error) {
- tmpl, err := template.New("template").Parse(templateStr)
+ tmpl, err := template.New("").Funcs(template.FuncMap{
+ "default": func(def, val any) any {
+ if val == nil {
+ return def
+ }
+ return val
+ },
+ }).Parse(templateStr)
+
if err != nil {
return "", fmt.Errorf("parsing template: %w", err)
}