astral-env is the declarative environment and system configuration layer that sits on top of Astral. Describe your entire system packages, services, dotfiles, hostname, timezone, file snapshots in a single .stars file. Then apply it. Reality matches the file.
Think of it as NixOS without the functional language that makes your brain hurt, or Ansible without the YAML sprawl and Python dependency.
gcc or clang)cmake >= 3.20openssl >= 3.0 (for SHA-256 store hashing)zstd (for snapshots astral -S zstd)git clone https://github.com/Astaraxia-Linux/Astral-env
cd Astral-env
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
sudo cmake --install build
# Enable in /etc/astral/astral.stars
sudo astral-env system init
# Create your user config
sudo astral-env system init-user yourusername
Add to /etc/astral/astral.stars:
$AST.core: {
astral-env = "enabled"
astral-env-system = "enabled"
};
astral-env uses the same .stars syntax as Astral recipes. If you've written a recipe, you already know this.
/etc/astral/env/env.stars$ENV.Version = "3"
$ENV.System: {
hostname = "izumi"
timezone = "Asia/Kuala_Lumpur"
};
$ENV.Packages: {
neovim
htop
git
zsh
};
$ENV.Services: {
sshd = "enabled"
cronie = "enabled"
NetworkManager = "enabled"
};
/etc/astral/env/username.stars$ENV.Version = "3"
$ENV.User: {
name = "izumi"
shell = "/bin/zsh"
};
$ENV.Packages: {
firefox
mpv
thunderbird
};
$ENV.Dotfiles: {
"/home/izumi/.config/nvim" = "nvim"
"/home/izumi/.zshrc" = "zshrc"
"/home/izumi/.gitconfig" = "gitconfig"
};
$ENV.Vars: {
EDITOR = "nvim"
BROWSER = "firefox"
};
Dotfiles in $ENV.Dotfiles are symlinked from /etc/astral/env/dotfiles/username/. Manage all your configs in one place.
astral-env.starsPer-project dev environments, like nix-shell but readable:
$ENV.Version = "3"
$ENV.Metadata: {
Name = "my-project"
Description = "My cool project environment"
};
$ENV.Packages: {
python >= 3.11
nodejs >= 20.0
git
};
$ENV.Vars: {
DEBUG = "true"
NODE_ENV = "development"
};
$ENV.Shell: {
echo "Welcome to my-project"
export PATH="$PWD/bin:$PATH"
};
$ENV.Snap$ENV.Snap: {
on_interval = "true"
default_interval = "1" "H" # S=seconds M=minutes H=hours D=days
path: {
"/home/izumi/.config/hyprland"
"/home/izumi/.zshrc": {
interval = "autosave" # snapshot on every file change
};
"/etc/astral": {
interval = "6" "H"
};
};
};
| Command | Description |
|---|---|
| astral-env system diff | Preview what would change safe, read-only |
| astral-env system apply | Apply changes to match the config files |
| astral-env system apply --dry-run | Show changes without making them |
| astral-env system apply --yes | Skip confirmation prompts |
| astral-env system apply --user <u> | Apply only a specific user's config |
| astral-env system apply --global-only | Apply only env.stars, skip user configs |
| astral-env system rollback | Roll back to the last snapshot |
| astral-env system rollback --list | List available rollback points |
| astral-env system rollback --to <id> | Roll back to a specific snapshot |
| astral-env system check | Validate all .stars files in /etc/astral/env/ |
| astral-env system init | Create /etc/astral/env/env.stars |
| astral-env system init-user <u> | Create per-user config + dotfiles directory |
The system diff output uses these markers:
| Marker | Meaning |
|---|---|
| [+] | Will install / create |
| [-] | Will remove / unlink |
| [~] | Will change |
| [!] | Conflict needs manual resolution |
astral-env automatically saves a rollback snapshot before applying anything. Rollback restores service states, symlinks, hostname, and timezone but not packages. Use astral -R to remove packages manually.
| Command | Description |
|---|---|
| astral-env init | Scaffold an astral-env.stars in current directory |
| astral-env lock | Generate lockfile (resolves and pins versions) |
| astral-env lock --update <pkg> | Update a specific package in the lockfile |
| astral-env lock --update | Update all packages in the lockfile |
| astral-env build | Build environment from lockfile |
| astral-env shell | Enter interactive shell with environment active |
| astral-env run <cmd> | Run a command inside the environment |
| astral-env status | Show installed vs missing packages |
Packages live in a content-addressed store under /astral-env/store/. Multiple projects sharing the same version of a package pay zero extra disk cost.
Content-addressed, zstd-compressed, deduplicated file snapshots. If a file hasn't changed since the last snapshot, the new snapshot is free zero bytes stored.
| Command | Description |
|---|---|
| astral-env snap <path> | Snapshot a file or directory |
| astral-env snap list | List all snapshots |
| astral-env snap list <path> | List snapshots for a specific path |
| astral-env snap restore <id> | Restore a snapshot |
| astral-env snap restore <id> --dest <path> | Restore to a different location |
| astral-env snap prune --keep-last 5 | Keep only last 5 snapshots per path |
| astral-env snap prune --older-than 14d | Remove snapshots older than 14 days |
astral-env-snapd handles scheduled and autosave snapshots in the background. Supports systemd, OpenRC, runit, s6, dinit, SysVinit.
| Command | Description |
|---|---|
| astral-env snapd start | Start daemon and enable at boot |
| astral-env snapd stop | Stop daemon |
| astral-env snapd restart | Restart daemon |
| astral-env snapd status | Show daemon status |
autosave uses inotify and waits 5 seconds after the last file change before snapshotting (debounce). Configurable per-path:
$ENV.Snap: {
path: {
"/home/izumi/.zshrc": {
interval = "autosave"
autosave_debounce = "5" "S"
};
};
};
| Command | Description |
|---|---|
| astral-env store list | List all store entries |
| astral-env store size | Show total store disk usage |
| astral-env gc --dry-run | Show what would be collected |
| astral-env gc | Collect entries unused for 30+ days |
| astral-env gc --max-age 7 | More aggressive collection (7 days) |
The GC skips entries referenced by any lockfile, entries newer than --max-age days, entries without a .complete marker, and the snap/ subdirectory entirely (snapshots have their own pruning via snap prune).
| Error | Fix |
|---|---|
| astral-env is not enabled | Add astral-env = "enabled" to $AST.core in /etc/astral/astral.stars |
| astral-env-system is not enabled | Add astral-env-system = "enabled" to the same block |
| zstd is required for snapshots | sudo astral -S zstd |
| No lockfile found | Run astral-env lock before astral-env build |
| Snapshot blob not found | The blob was GC'd before the index entry was pruned. Run astral-env snap prune to clean up dangling entries. |
| Rollback only partially worked | Rollback doesn't undo package installs. Use astral -R <pkg> manually, or astral-env snap restore for specific files. |
Why C++ instead of POSIX sh like Astral?
astral-env needs concurrent file hashing, inotify event loops, content-addressed storage, and a GC. Doing that in sh would be a crime against humanity. C++20 with std::filesystem hits the sweet spot.
Does astral-env replace Astral?
No. It's a layer on top. It uses astral -S to install packages it doesn't reimplement the package manager.
Can I use it without system management?
Yes. The project environment features (init, lock, build, shell, run) work completely independently without astral-env-system = "enabled".
Can I track my configs in git?
That's the whole point. Put your .stars files and /etc/astral/env/dotfiles/ in a repo. Fresh install → clone → system apply → done.
Who maintains this?
Same One Maniac™. Two projects, one maniac. The math is concerning.