A template is a Dockerfile + skeleton/ + aetherion-src/ bundle that
create namespace forks into a namespace's build dir. Two layers exist, and a
user template shadows a baked-in one of the same name:
- Baked-in — ship inside the package at
src/aetherion/data/templates/<name>/. Read-only. - User-defined — live at
~/.aetherion/templates/<name>/. Yours to edit.
This guide covers forking, editing, the template.yaml schema, sharing via git,
and choosing a base.
aetherion create template my-stack # fork from 'default'
aetherion create template my-stack nvim # fork from a specific base
aetherion edit template my-stack # open its Dockerfile in $EDITOR
aetherion create namespace work my-stack # spawn a namespace from it
aetherion list templates # see baked-in + user templates
aetherion delete template my-stack # remove the user copyThe base is an optional positional.
aetherion create template NAME [BASE]andaetherion create namespace NAME [TEMPLATE]both default todefaultwhen the second argument is omitted. (The older--template SPECflag still works as a deprecated alias; the positional wins if both are given.)
Fork from the tier closest to what you want, then add the rest:
| Want… | Fork from | Then add |
|---|---|---|
| A clean room for one tool | base |
just your tool |
| A multi-language build env | default |
your deps |
| An editor in the ecosystem | nvim |
your deps / config |
| Agents + an editor together | cli-agents |
layer nvim's editor bits |
| A GUI IDE variant | cursor-ide / vscode-ide / zed-ide / antigravity-ide |
extensions, settings |
If you want an editor inside an aetherion namespace, build on the bundled
nvim template. It already wires Neovim 0.11.x with a complete LSP/DAP stack
(rust-analyzer, gopls, pyright, typescript-language-server, lua-language-server,
debugpy, delve, codelldb, js-debug-adapter) on top of the language toolchains,
and it bootstraps plugins on first launch. Forking nvim gets you a fully
working editor for free; you only add your project-specific dependencies. Reach
for the GUI IDE templates only when you specifically want a GUI editor (Electron
for cursor-ide / vscode-ide, native Rust + Vulkan for zed-ide).
aetherion edit template my-stackOpens ~/.aetherion/templates/my-stack/Dockerfile in $EDITOR (falls back to
vi). Editing a baked-in template auto-forks it into your user templates
first, so you always edit a writable copy:
aetherion edit template nvim # auto-forks baked-in 'nvim', then opens itmy-stack/
├── Dockerfile # required
├── skeleton/ # baked into the image at the matching path
│ ├── etc/... # system files
│ └── home/aetherion/... # dotfiles that seed each namespace's $HOME
└── aetherion-src/ # COPY'd into the build; .keep placeholder when empty
Follow the conventions in src/aetherion/data/templates/STYLE.md:
- Identity: user
aetherion, UID 1000, GID 1000,$HOME=/home/aetherion, login shell/bin/bash,WORKDIR /home/aetherion, defaultCMD ["bash"]. - Build arg: declare
ARG AETHERION_SPEC=aetherion. The launcher passes eitheraetherion==<version>(released) or a path to your livesrc/checkout (development) so the in-containeraetherion/conduitstay in lockstep. - Prompt: install starship into
/usr/local/bin, shipskeleton/home/aetherion/.config/starship.toml, andeval "$(starship init bash)"from.bashrc. - Where things live: system-wide tooling in
/usr/local/binor/optso image rebuilds reach every namespace; per-user state stays under$HOME.
Skeleton dotfiles are frozen at seed. Files under
skeleton/home/aetherion/are copied into the namespace's$HOMEonce, at create time. Editing them later (or shipping new ones in a rebuilt image) won't update existing namespaces — onlyaetherion reset namespace <name>re-seeds, and that drops all of that namespace's$HOMEcustomizations. Keep things you want to update via rebuild in/usr/local/binor/opt.
A template may ship a template.yaml declaring supported host platforms and
per-namespace defaults. Omit it entirely and the template is treated as
universally portable with no defaults (its command falls through to the image's
CMD).
description: "My stack: nvim + extra language servers." # shown in `list templates`
platforms: # host tuples this template supports
- { os: linux, arch: amd64, runtime: podman }
- { os: linux, arch: arm64, runtime: docker }
- { os: darwin, arch: arm64, runtime: docker }
# `*` wildcards any field: { os: "*", arch: "*", runtime: "*" }
defaults: # written into the namespace's config.yaml at create time
display: x11 # x11 | wayland | auto | none
command: nvim # string (shlex-split) or a list of stringsdescription— human-readable; appears inaetherion list templates.platforms—create namespacevalidates the current host against this list and fails with a clear error if the combo isn't supported. Each field accepts*as a wildcard. Omitplatformsto skip the check.defaults.display— initial display mode for namespaces created from this template. See display forwarding.defaults.command— what bareaetherion <ns>runs instead ofbash. A string isshlex-split into argv (e.g.cursor .→["cursor", "."]); a list is used verbatim (the way to spell argv elements containing whitespace). Notecommand:is not shell-expanded — use.(the launcher sets the container's working directory to your mounted project), not${PWD}.
Defaults only apply at create time and only when the user hasn't already set the field; explicit CLI values always win. Unknown keys are accepted and ignored for forward compatibility.
create namespace and create template accept a git URL (with an optional
#REF — tag, branch, or commit) anywhere a template name is accepted:
aetherion create namespace experimental https://github.com/me/aetherion-template.git
aetherion create namespace pinned https://github.com/me/aetherion-template.git#v1.2.0The clone is cached at ~/.aetherion/template-cache/<hash>/, keyed by URL, so
later uses just git fetch and re-checkout. Each namespace records the template
name or URL it was forked from, so list namespaces shows it and a refresh
knows what to re-resolve:
aetherion rebuild namespace pinned --refresh-template # re-fetch + re-fork + rebuildA git-hosted template repo is just the template directory contents at its root
(Dockerfile, skeleton/, optional template.yaml, optional aetherion-src/).
aetherion rebuild namespace work # rebuild from the current buildDir as-is
aetherion rebuild namespace work --no-cache # force every layer to re-fetch
aetherion rebuild namespace work --refresh-template # re-fork buildDir from the recorded template (discards local buildDir edits)
aetherion rebuild namespace work --template other # swap to a different template and re-fork- Plain
rebuildleaves yourDockerfile/skeleton/edits in the namespace's build dir untouched and only refreshes the bundledaetherion-src/overlay. --refresh-template/--templatere-fork the build dir from a template, discarding local edits to that build dir.
- One-off — edit a single namespace's build dir in place and rebuild:
$EDITOR ~/.aetherion/containers/work/Dockerfile aetherion rebuild namespace work
- Reusable — make a template you can spawn many namespaces from (the rest of this guide).
Template names follow the same character rules as namespace names: letters, digits, dot, underscore, dash; no leading dot. Unlike namespaces, template names have no reserved-word restriction (they never appear in a verb slot).
aetherion delete template my-stack # remove the user copyDeleting a user template that shadowed a baked-in one unshadows the baked-in
version. Baked-in templates can't be deleted (there's nothing to remove on the
host side); delete warns if any namespace still records the template name.