Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use flake "github:JacobPEvans/nix-devenv#nix-bare"
14 changes: 14 additions & 0 deletions .github/scripts/render-mermaid.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
# Re-render every docs/architecture/*.mmd to a sibling .svg using the
# minlag/mermaid-cli docker image. Same image is used in CI; same image is
# used locally; SVG output stays byte-identical so the diff gate is trustable.
set -euo pipefail

IMAGE="${MERMAID_CLI_IMAGE:-minlag/mermaid-cli:latest}"

for f in docs/architecture/*.mmd; do
[ -f "$f" ] || continue
echo "rendering $f"
docker run --rm -u "$(id -u):$(id -g)" -v "$PWD:/data" "$IMAGE" \
-i "/data/$f" -o "/data/${f%.mmd}.svg" --quiet
done
40 changes: 40 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# CI — runs on every PR and on pushes to main.
# Validates the flake (nixfmt-rfc-style/statix/deadnix/flake-check).
name: CI

on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
nix-check:
name: Nix flake check
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install Nix
uses: DeterminateSystems/determinate-nix-action@v3

- name: Cache Nix store
uses: nix-community/cache-nix-action@v7
with:
primary-key: nix-${{ runner.os }}-${{ hashFiles('flake.lock') }}
restore-prefixes-first-match: nix-${{ runner.os }}-
gc-max-store-size: 5000000000
save: ${{ github.ref == 'refs/heads/main' }}

- name: nix flake check
run: nix flake check --no-build --show-trace
48 changes: 48 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# CodeQL static analysis.
# Skeleton: Nix is not a CodeQL-supported language, but we scan our GitHub
# Actions workflows. When scripts/code in supported languages land later,
# add the language to the matrix below.
name: CodeQL

on:
pull_request:
branches: [main]
push:
branches: [main]
schedule:
- cron: "13 5 * * 1"

permissions:
contents: read

jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: ubuntu-latest
permissions:
security-events: write
packages: read
actions: read
contents: read

strategy:
fail-fast: false
matrix:
include:
- language: actions
build-mode: none

steps:
- name: Checkout
uses: actions/checkout@v6

- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{ matrix.language }}"
47 changes: 47 additions & 0 deletions .github/workflows/deps-update-flake.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Weekly flake input refresh.
# Renovate handles most updates; this workflow exists for the occasional
# `nix flake update` of all inputs at once.
name: Update flake dependencies

on:
schedule:
- cron: "0 12 * * 1"
workflow_dispatch: {}

permissions:
contents: read

jobs:
update:
name: nix flake update
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install Nix
uses: DeterminateSystems/determinate-nix-action@v3

- name: Update flake inputs
run: nix flake update

- name: Validate flake
run: nix flake check --no-build --show-trace

- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
env:
EVENT_NAME: ${{ github.event_name }}
with:
branch: chore/flake-update
delete-branch: true
commit-message: "chore(deps): update flake inputs"
title: "chore(deps): update flake inputs"
body: |
Automated weekly `nix flake update`.

Triggered by ${{ env.EVENT_NAME }}.
labels: dependencies
35 changes: 35 additions & 0 deletions .github/workflows/mermaid-render-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Mermaid render-diff gate.
# Re-renders every docs/architecture/*.mmd to .svg and fails the PR if the
# committed .svg does not match the freshly-rendered output. Per dryvist
# convention (PR #620 — Mermaid sources committed alongside rendered SVGs).
#
# Both local renders and CI renders use the same minlag/mermaid-cli docker
# image, so the byte-level diff is meaningful (no nix-vs-npm version skew).
name: Mermaid render check

on:
pull_request:
paths:
- "docs/architecture/**.mmd"
- "docs/architecture/**.svg"
- ".github/scripts/render-mermaid.sh"
- ".github/workflows/mermaid-render-check.yml"

permissions:
contents: read

jobs:
render:
name: Re-render mermaid sources
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Re-render every .mmd source
run: bash .github/scripts/render-mermaid.sh

- name: Fail on diff
run: git diff --exit-code -- 'docs/architecture/*.svg'
19 changes: 19 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Release Please

on:
push:
branches: [main]

permissions: {}

jobs:
release-please:
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: googleapis/release-please-action@v4
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.direnv/
result
result-*
*.qcow2
*.img
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
".": "0.0.0"
}
24 changes: 24 additions & 0 deletions .sops.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# SOPS rules for nix-pxe-bootstrap.
#
# Maintainer + Bitwarden recipients are PLACEHOLDERS. Real public keys land
# when the host age key is materialized via ssh-to-age (after first boot via
# nixos-anywhere).
#
# To generate the host age key from a host SSH key:
# ssh-keyscan <pxe-host-ip> | ssh-to-age
keys:
# Maintainer (operator) age key. Real value lives in ~/.config/sops/age/keys.txt;
# public form below is a placeholder until it is exported and committed.
- &operator age1placeholderoperatorkeyaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
# Bitwarden-backed escrow recipient (offsite recovery copy).
- &bitwarden_escrow age1placeholderbitwardenkeyaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
# Host age key (derived from the pxe-host SSH host key after first boot).
- &host_pxe age1placeholderhostkeyaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

creation_rules:
- path_regex: ^secrets/.*\.(enc\.)?yaml$
key_groups:
- age:
- *operator
- *bitwarden_escrow
- *host_pxe
39 changes: 39 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# nix-pxe-bootstrap — AI Agent Instructions

@github:JacobPEvans/ai-assistant-instructions

## Repo-specific notes

- **Status: skeleton only.** Module bodies are intentionally `mkEnableOption`
stubs — do not fill them in unless the implementation epic in the dryvist
Project explicitly asks for it.
- **Target hardware is unbound.** `hosts/pxe-host/disko.nix`,
`hosts/pxe-host/networking.nix`, and `hardware-configuration.nix.example`
carry placeholders. Real `by-id` disk paths and static IPs land in a later
PR after the device exists.
- **First boot uses `nixos-anywhere`** from an operator Mac to materialize
`hardware-configuration.nix`. Do not check that file in until after the
initial bootstrap.
- **Answer files in `answer-files/proxmox-{b,c,d}.toml`** use placeholder
hashes / NICs / hostnames. Real values come from the dryvist secrets store
(SOPS-encrypted) and never get committed in plaintext.
- **No custom shell scripts.** Declarative NixOS modules only. If a problem
feels like it needs a script, restate it as a NixOS module option.
- **Answer-file format:** Proxmox auto-installer TOML. ADR-0003 documents
why TOML over JSON or YAML.
- **CI:** mirrors the nix-darwin pattern — `nixfmt-rfc-style`, `statix`,
`deadnix`, `nix flake check`, plus mermaid render-diff gate.

## Related repos

See `README.md` ecosystem table.

## Bring-up workflow (operator)

1. Acquire hardware (Pi 4/5 or N100/N305 mini-PC).
2. Fill `hosts/pxe-host/{disko,networking}.nix` with real values via PR.
3. Boot the device into a NixOS installer or any Linux live env with SSH.
4. From operator Mac:
`nix run github:nix-community/nixos-anywhere -- --flake .#pxe-host root@<ip>`
5. Commit the generated `hardware-configuration.nix` (drop `.example` suffix).
6. Subsequent updates: `nixos-rebuild switch --flake .#pxe-host --target-host root@<ip>`.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changelog

All notable changes to this project will be documented in this file.

This file is managed automatically by
[release-please](https://github.com/googleapis/release-please).
Do not edit manually — make conventional-commit PRs and let the bot update
this file on the release PR.
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# AI Agents Configuration

@AGENTS.md
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
MIT License

Copyright (c) 2026 dryvist
Copyright (c) 2026 Jacob P. Evans

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
Loading
Loading