Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
f240be8
Adding anvil glue code
aaron70 Aug 31, 2026
c194484
Move old modules
aaron70 Aug 31, 2026
c895e95
Add configurations features
aaron70 Aug 31, 2026
8dda693
Add aaronv user
aaron70 Aug 31, 2026
f5ebdfc
Add pc host
aaron70 Aug 31, 2026
96345bf
Add support for features and programs list recive the ctx
aaron70 Aug 31, 2026
ccb9eef
Add support for users list recive ctx
aaron70 Sep 1, 2026
e678df3
Adding the editor program
aaron70 Sep 1, 2026
5bc9c17
Add support for secrets
aaron70 Sep 1, 2026
7d274c4
Add support for shell and dependencies
aaron70 Sep 1, 2026
436eb97
Add git-crypt rule for moved modules_old/profiles path
aaron70 Sep 1, 2026
add1fac
fixes
aaron70 Sep 1, 2026
36115d2
Remove profiles folder and git-crypt rules
aaron70 Sep 1, 2026
904e318
Add desktop program
aaron70 Sep 1, 2026
a4c694f
Nix flake update and fix boot package
aaron70 Sep 2, 2026
63f5b4b
Fix sops secrets
aaron70 Sep 2, 2026
4ebdd90
Add terminal program + shell configurations
aaron70 Sep 2, 2026
775b2c4
Add the laptop host
aaron70 Sep 2, 2026
adb8c63
Adding the laptop age key and setting the password of the aaronv user
aaron70 Sep 2, 2026
16ddd2d
Add TODO.md to keep track of missing features and some improvements
aaron70 Sep 2, 2026
a861e96
Improve and simplify shell program modules
aaron70 Sep 3, 2026
9be4f32
Format all files
aaron70 Sep 3, 2026
ddaa9b4
Add desktop program improvements
aaron70 Sep 6, 2026
1e74d76
Add noctalia dotfiles
aaron70 Sep 6, 2026
166f254
Setting desktop for the laptop
aaron70 Sep 7, 2026
d533b1a
Creating installPackages and forUser modules
aaron70 Sep 7, 2026
ac4f531
Move gaming features
aaron70 Sep 7, 2026
80cb0e2
lastest improvements
aaron70 Sep 8, 2026
946694a
Fix jovian issue
aaron70 Sep 8, 2026
e1bd723
Install go
aaron70 Sep 8, 2026
f39c920
Add fixes and some scripts
aaron70 Sep 9, 2026
c413f75
Change README
aaron70 Sep 9, 2026
dfe319c
Add termporal regression of the xwayland-satellite due to issue with …
aaron70 Sep 16, 2026
6d4ad19
Programs and features autowire home modules
aaron70 Sep 17, 2026
9e1a84d
Update nix flake
aaron70 Sep 18, 2026
89f45b8
Update TODO.md
aaron70 Sep 18, 2026
4fbd4f0
Add nix checks
aaron70 Sep 19, 2026
99291a4
Add fixes, checks and darwin modules
aaron70 Sep 20, 2026
d4d04a7
Add checks GH CI
aaron70 Sep 20, 2026
efe8476
Fix CI failing because of xwayland-satellite
aaron70 Sep 20, 2026
04a06b4
Comment the eval build
aaron70 Sep 20, 2026
21364e2
Add update flake CI
aaron70 Sep 20, 2026
3633c39
Make CI checks only run on PRs
aaron70 Sep 20, 2026
ce7022c
Creating a test CI
aaron70 Sep 20, 2026
683b9bf
Delete old modules
aaron70 Sep 20, 2026
9d69454
Remove .gitattributes
aaron70 Sep 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .gitattributes

This file was deleted.

47 changes: 47 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Checks

on:
pull_request:
workflow_dispatch:

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
format:
name: Format (alejandra)
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
- run: nix fmt -- --check .

build:
name: Build (${{ matrix.system }})
needs: format
strategy:
fail-fast: false
matrix:
include:
- system: x86_64-linux
runner: ubuntu-latest
- system: aarch64-darwin
runner: macos-14
runs-on: ${{ matrix.runner }}
timeout-minutes: 180
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
- uses: nix-community/cache-nix-action@v6
with:
primary-key: nix-${{ matrix.system }}-${{ hashFiles('flake.lock') }}
restore-prefixes-first-match: nix-${{ matrix.system }}-
gc-max-store-size-linux: 10G
gc-max-store-size-macos: 10G
- name: Build checks
run: nix flake check -L --keep-going
15 changes: 15 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Test

on:
workflow_dispatch:

jobs:
update:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
- uses: DeterminateSystems/determinate-nix-action@v3

- name: Test
run: nix build .#nixosConfigurations.gpd.config.system.build.toplevel --dry-run
46 changes: 46 additions & 0 deletions .github/workflows/update-flake-lock.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Update flake.lock

on:
schedule:
- cron: "0 21 * * 1"
workflow_dispatch:

concurrency:
group: update-flake-lock
cancel-in-progress: false

permissions:
contents: write
pull-requests: write
issues: write

jobs:
update:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
- uses: DeterminateSystems/determinate-nix-action@v3
- id: update
uses: DeterminateSystems/update-flake-lock@v28
with:
token: ${{ secrets.GH_TOKEN_FOR_UPDATES }}
base: main
branch: bot/flake-update
commit-msg: "chore: update flake.lock"
pr-title: "chore: update flake.lock"
pr-labels: |
dependencies
chore
pr-body: |
Automated changes by the [update-flake-lock](https://github.com/DeterminateSystems/update-flake-lock) GitHub Action.

```
{{ env.GIT_COMMIT_MESSAGE }}
```

- name: Enable auto-merge
if: steps.update.outputs.pull-request-number != ''
env:
GH_TOKEN: ${{ secrets.GH_TOKEN_FOR_UPDATES }}
run: gh pr merge --repo "$GITHUB_REPOSITORY" --auto --squash "${{ steps.update.outputs.pull-request-number }}"
20 changes: 20 additions & 0 deletions .sops.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
keys:
- &personal_admin age13vyme78jmvjv499t7dzl2ju4epy90792nje0mvyh6ar93zae75kqyzsr2j
- &pc age146xlkyvdxgjqjt3fnawtvqgzuk0fwgjsj9gf3c0z3q5n02r49vgsz3nk4s
- &laptop age1sjlg4s9jq2qlevlkhylguul7ztxr6cassnj7xle7patzlgmy5syqan5vpz
- &gpd age1mjqu5xzvtgcpvy5tg9cnv7hk8vd9vmmgxmk8xtj3wudj2qf7ugcq53weqr

creation_rules:
- path_regex: secrets/personal\.yaml$
key_groups:
- age:
- *personal_admin
- *pc
- *laptop
- *gpd

- path_regex: secrets/work\.yaml$
key_groups:
- age:
- *personal_admin
- *pc
168 changes: 107 additions & 61 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,103 +4,149 @@

Personal NixOS and nix-darwin configuration by [Aaron Vargas](https://github.com/aaron70).


## Features

- **Cross-platform** — shared module system for both NixOS and macOS via nix-darwin
- **Three-tier architecture** — profiles (who you are) → features (capabilities) → programs (tools)
- **Wrapper module system** — programs ship with auto-generated configs via `nix-wrapper-modules`
- **Tokyo Night theme** — consistent look across shell prompt (oh-my-posh), WM (niri), terminal (kitty), and desktop shell (Noctalia)
- **niri + Noctalia** on Linux — scrollable-tiling Wayland compositor with a full-featured desktop shell
- **AeroSpace** on macOS — native tiling window manager
- **GPD Win Max 2** — Steam Deck / handheld optimizations via Jovian-NixOS, fingerprint driver
- **Secrets management** — git-crypt encrypted profiles for credentials

## Hosts

| Host | Arch | OS | Profile | GPU | Desktop |
|------|------|----|---------|-----|----------|
| `pc` | x86_64 | NixOS | personal | NVIDIA | niri + Noctalia |
| `laptop` | x86_64 | NixOS | personal | Intel | niri + Noctalia |
| `gpd` | x86_64 | NixOS (Jovian) | personal | AMD | niri + Noctalia |
| `mac` | aarch64 | macOS | work | Apple Silicon | AeroSpace |
| Host | Arch | OS | User | GPU | Desktop | Notes |
|------|------|----|---------|-----|----------|-----|
| `pc` | x86_64 | NixOS | aaronv | NVIDIA | niri + Noctalia | Personal computer, for gaming and development. |
| `laptop` | x86_64 | NixOS | aaronv | Intel | niri + Noctalia | Personal laptop, for development. |
| `gpd` | x86_64 | NixOS (Jovian) | aaronv | AMD | niri + Noctalia | Handheld console, for gaming and occasionally development. |
| `mac` | aarch64 | macOS | aaronvargas | Apple Silicon | AeroSpace | Work computer. (Not implemented yet) |


## Architecture

The flake is wired with `flake-parts` + `import-tree`. The framework lives in `anvil/`, the concrete configuration lives in `modules/` (each subdirectory is auto-imported as a flake module).

```
flake.nix — flake-parts + import-tree
└── modules/
├── configurations/ system-level config (boot, audio, networking, etc.)
├── hosts/ machine definitions (hardware + preferences)
├── profiles/ user identities (personal, work, vmtest)
├── features/ capability toggles (development, gaming)
├── programs/ program definitions + wrappers + scripts
└── wrapperModules/ low-level wrapper templates (kitty, ghostty, oh-my-posh)
flake.nix
├── anvil/ The framework, as glue code for nixos, home and darwin modules
│ ├── declarations/ Option schemas of the framework's entities
│ ├── lib/ Helper Functions
│ └── options/ The anvil namespace where the entities are defined: anvil.hosts / anvil.users / anvil.features / anvil.programs
└── modules/ My nixos configuration modules
├── hosts/
├── users/
├── features/
├── programs/
├── declarations/ shared option schemas
├── dotfiles/ config templates, scripts and configuration functions
└── secrets/ sops-encrypted secrets + wiring
```

**Key insight**: Profiles control *who you are* (which features and programs are active), features control *what you can do*, and programs control *what tools you have* — all wired through a shared `preferences` option.
**Key concepts**

- **Entities** — hosts, users, features and programs. Each has `name`, free-form `metadata`, optional lists of children (`features`, `programs`, `users`) and per-platform fragments `nixos` / `darwin` / `home`.
- **Fragments** — a fragment is a NixOS/nix-darwin/home-manager module merged into every target that enables its entity. Hosts without a fragment just aggregate the fragments of their features/programs/users.
- **Refkeys** — children can be referenced by a plain string (`"gaming"`) or a refkey submodule `{ ref, variant, merge, override }` to select a variant or tweak an entity for a single consumer. Lists (and fragments) can also be *functions* of the context `{ host, user, program, feature }`, so a feature can, for example, read `host.metadata.mainUser`.
- **Generation** — each host's `systems.<platform>` entry (a system string or `{ system = alias; }`) yields an output. `self.lib.mkHosts` collects the host's own fragment plus the fragments of every enabled feature, program and user (deduplicated by `name` / `name@variant`) and builds `nixosConfigurations`, `darwinConfigurations` and `homeConfigurations`.
- **Context injection** — `self.lib.withContext ctx` injects the entity context into fragments, so configs stay generic and adapt to who's running them.
- **Wrappers & dotfiles** — `self.wrappers.<name>.wrap` produces a configured package from `nix-wrapper-modules`; `self.dotfiles.<name>.default` returns the config text used by those wrappers (and home-manager). The shared theme comes from `self.lib.getColors`.

The split mirrors intent: **host/user** define *who you are*, **features** what *you can do*, **programs** what *tools you have*.

## Quick Start

```sh
# Clone and enter
git clone https://github.com/aaron70/nix && cd nix
> Note: `.envrc` (direnv) ships with the repo but the flake currently defines no `devShell`, so `direnv allow` has no effect for now.

# Unlock encrypted profiles (if you have the key)
git-crypt unlock /path/to/key
The shell installs host-specific aliases (baked for the host you're on, pointing at `host.metadata.nixPath`):

# Build and switch for a specific host (NixOS)
sudo nixos-rebuild switch --flake .#pc
| Alias | Runs |
|-------|------|
| `nswitch` | `nh os switch <nixPath> -H <this-host>` |
| `ntest` | `nh os test <nixPath> -H <this-host>` |
| `nboot` | `nh os boot <nixPath> -H <this-host>` |
| `nbuild-vm` | `nh os build-vm <nixPath> -H <this-host>` |
| `nclean` | `nh clean all --optimise -k <configurationLimit>` |
| `nshell` | `nix-shell --command <shell> -p` |

# Or using nh (recommended)
nh os switch --host pc .
Manually (e.g. from a machine without the aliases):

# For macOS
darwin-rebuild switch --flake .#mac
```sh
# Rebuild and switch (daily driver)
sudo nixos-rebuild switch --flake .#pc # pc | laptop | gpd
darwin-rebuild switch --flake .#mac # once implemented

# Update flake inputs
nix flake update
# Update inputs
nix flake update # all inputs
nix flake lock --update-input nixpkgs # a single input

# Clean old generations
nh clean all --keep 3
# Format all files (alejandra)
nix fmt
```

## Workflows

**Test a change in a VM**

`nbuild-vm` boots the host in a QEMU VM. It uses `virtualisation.vmVariant`, which forces the password secret off and sets a fixed `initialPassword = "anvil"` (see `modules/users/aaronv.nix`).

**Add a new host**

1. Create `modules/hosts/<name>.nix` declaring `anvil.hosts.<name>` — `systems.nixos`, `users`, `features`, `programs`, `metadata` and the `nixos` fragment — plus a `<name>-hardware` nixosModule.
2. Register the machine's age key in `.sops.yaml` (see [Secrets](#security)).
3. Build: `sudo nixos-rebuild switch --flake .#<name>`.

**Add a feature or program**

- `modules/features/<name>.nix` → `anvil.features.<name>` with `nixos` / `darwin` / `home` fragments; it can pull in programs and other features.
- `modules/programs/<name>.nix` → `anvil.programs.<name>` with `getPackage` and fragments.

Reference it by name (or a refkey) from any entity. The `modules/` import-tree automatically registers it with the flake options.

**Add a user**

Create `modules/users/<name>.nix` → `anvil.users.<name>` with its `nixos` / `darwin` / `home` fragments, then attach it on the host via `users = [...]`.

## Security

### Git Crypt
### Secrets

Secrets use [sops-nix](https://github.com/Mic92/sops-nix) with age.

To keep some sensitive files protected, **git-crypt** is used to encrypt and decrypt the files.
- On first activation each host generates its own age keypair at `/path/to/key.txt` (`sops.age.generateKey = true`); `SOPS_AGE_KEY_FILE` points there.
- `.sops.yaml` lists an admin key (`personal_admin`) plus one key per device (`pc`, `laptop`, `gpd`). A device can only *decrypt* (and edit) secrets if its public key appears in that file.
- `modules/secrets/personal.yaml` holds the encrypted secrets (`email`, `password`), wired by the `personal-secrets` feature:
- `password` → the user's `hashedPasswordFile`.
- git's `user.name` / `user.email` are rendered through `sops.templates."gitconfig-personal"` (see `modules/programs/git.nix`).
`age` and `sops` are installed on every host and the `sops` feature already exports `SOPS_AGE_KEY_FILE=/path/to/key.txt`, so these commands work out of the box on a managed host. The prefix is only needed when running outside a host that doesn't set it.

[git-crypt](https://github.com/AGWA/git-crypt) transparently encrypts and decrypts files when pushed or checked out.
**Commands**

```sh
# Export the private key
git-crypt export-key /path/to/key
# Edit a secret (opens your $EDITOR on the decrypted file)
SOPS_AGE_KEY_FILE=/path/to/key.txt sops modules/secrets/personal.yaml

# Unlock encrypted files with the exported key
git-crypt unlock /path/to/key
# Set a single value (no editor)
SOPS_AGE_KEY_FILE=/path/to/key.txt sops set modules/secrets/personal.yaml '["password"]' '<value>'

# Encrypt the files again
git-crypt lock
```
# Read/verify without touching the file
SOPS_AGE_KEY_FILE=/path/to/key.txt sops -d modules/secrets/personal.yaml
SOPS_AGE_KEY_FILE=/path/to/key.txt sops -d --extract '["email"]' modules/secrets/personal.yaml

## Maintenance
# Give a new device access:
age-keygen -o /tmp/key.txt # 1. generate a keypair
age-keygen -y /tmp/key.txt # 2. prints its public key -> age1...
# 3. add `- &<name> age1...` under `keys:` and `- *<name>` in the
# creation rule of `.sops.yaml`
SOPS_AGE_KEY_FILE=/path/to/key.txt sops updatekeys modules/secrets/personal.yaml # 4. re-encrypt with the new recipient set

### Update Noctalia Plugins
# Rotate the data key (re-encrypts in place)
SOPS_AGE_KEY_FILE=/path/to/key.txt sops -r -i modules/secrets/personal.yaml
```

Get the commit hash from the latest commit on the [Plugins Repository](https://github.com/noctalia-dev/noctalia-plugins/commits/main) and replace it on the `fetchgit` function, then use `sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=` as the **sha256** and run the configuration — it will fail and give you the real **sha256**.
If a machine can't decrypt (e.g. git user/email missing), its key likely isn't in `.sops.yaml` yet — see [Troubleshooting](#troubleshooting).

## Troubleshooting

### No git user and email

Git's `user.name` / `user.email` come from the sops-rendered `gitconfig-personal` template. If they're missing, the secrets are not being decrypted — make sure this device's age key is registered in `.sops.yaml` (see [Secrets](#security)) and rebuild.

### No audio on headsets

Open `pavucontrol` or `Bluetooth Manager` and change the audio profile. Currently works with `High Fidelity Playback (A2DP Sink, codec AAC)`.

### Setup the monitors position

`wdisplays` is installed for setting up monitor positions. Set the positions within the application and then copy the values into the niri configuration.

### Git Credentials broken

If NixOS rebuilds `gh`, the git credentials configuration might break since it may still point to the old `gh` path.
To fix it, run `gh auth setup-git`.
`wdisplays` is installed for setting up monitor positions. Set the positions within the application and then copy the values into `anvil.desktop.preferences.monitors` in the host's module (`modules/hosts/<host>.nix`).
29 changes: 0 additions & 29 deletions TODO.md

This file was deleted.

Loading
Loading