Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f9dbe01
docs: make AGENTS.md the master agent doc; CLAUDE.md imports it
ulises-c Jul 12, 2026
824fe28
docs: preserve macOS benchmark review findings in docs/
ulises-c Jul 12, 2026
51d29bf
docs: extract packages.json schema to docs/PACKAGES.md; slim AGENTS.md
ulises-c Jul 12, 2026
56a2e17
moving MD files to docs/
ulises-c Jul 12, 2026
1db2503
docs: fix cross-references after moving TODO/CHANGELOG into docs/
ulises-c Jul 12, 2026
e6c1814
railguard documentation update
ulises-c Jul 12, 2026
29a2675
remove graphify reference
ulises-c Jul 12, 2026
27e79aa
Merge origin/main: port CLAUDE.md Pi-node edits into AGENTS.md
ulises-c Jul 12, 2026
d15c3e7
docs(claude): extract railguard reference into docs/RAILGUARD.md; sli…
ulises-c Jul 12, 2026
472a722
feat(claude): deploy and validate the docs/ symlink in ~/.claude
ulises-c Jul 12, 2026
8e33e80
docs: add max-effort code-review reports (agents docs + repo-wide PRs…
ulises-c Jul 12, 2026
20877a7
fix(claude): align installer and validation behavior
ulises-c Jul 12, 2026
3c35f14
docs(agents): correct shared repository guidance
ulises-c Jul 12, 2026
634ec69
docs(claude): refresh Railguard setup guidance
ulises-c Jul 12, 2026
34a9359
fix(pi): harden backup failure handling
ulises-c Jul 12, 2026
5bcfab6
fix(server): activate and verify UPS monitoring
ulises-c Jul 12, 2026
9cf5b95
fix(pi): restore resilient local service access
ulises-c Jul 12, 2026
398c8ab
fix(glances): stabilize disk aliases across reshuffles
ulises-c Jul 12, 2026
c1e3f28
fix(server): render host systemd unit paths
ulises-c Jul 12, 2026
704f865
docs: scrub private machine details and stale pointers
ulises-c Jul 12, 2026
ecd2644
fix(server): persist UPS driver service
ulises-c Jul 12, 2026
346917b
fix(glances): preserve local access during migration
ulises-c Jul 12, 2026
433ed89
fix(pi): enforce backup oneshot timeout
ulises-c Jul 13, 2026
10251bf
fix(server): verify persistent NUT services
ulises-c Jul 13, 2026
3ff8624
docs: add live host remediation reply chains
ulises-c Jul 13, 2026
e0f4651
fix(pi): pin cups sidecar subnet for cupsd allow-list
ulises-c Jul 13, 2026
dd982cf
docs(cups): document + parameterize host CUPS access policy
ulises-c Jul 13, 2026
bed07ee
fix(ups): correct UPS model to CyberPower CST135UC2
ulises-c Jul 13, 2026
50ea008
docs(pr61): record main server Round 1 verification reply
ulises-c Jul 13, 2026
bbd935a
fix(homepage): parameterize Pi card hostname via env var
ulises-c Jul 13, 2026
fb199c5
fix(cups): secure family printing policy deployment
ulises-c Jul 13, 2026
a395ce1
fix(backup): keep Pi failure status current
ulises-c Jul 13, 2026
2a8788c
fix(homepage): parameterize main server identity
ulises-c Jul 13, 2026
eab4a16
test(pi): run policy regressions in CI
ulises-c Jul 13, 2026
b9bcb6e
docs(pr61): coordinate final host remediation
ulises-c Jul 13, 2026
cf23033
driftcheck: honor a repo-root .driftcheckignore
ulises-c Jul 13, 2026
63d5162
fix(hooks): suppress SC2053 on driftcheck's intentional glob match
ulises-c Jul 23, 2026
3f6d9dc
Merge origin/main into docs/agents-md-master
ulises-c Jul 23, 2026
5a5d485
feat(hooks): make driftcheck ignore lookup repo-root-aware
ulises-c Jul 23, 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
25 changes: 25 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ jobs:
- name: Validate packages.json schema
run: bash scripts/validate-packages.sh

python:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Check Python syntax
run: |
mapfile -d '' files < <(find . -name '*.py' -not -path './.git/*' -print0)
bash scripts/check-python-syntax.sh "${files[@]}"

- name: Test Glances disk aliases
working-directory: linux-server/glances
run: python3 -m unittest -v test_rename_disks.py

dryrun-smoke:
runs-on: ubuntu-latest
steps:
Expand All @@ -51,6 +65,17 @@ jobs:
- name: Root setup.sh dry-run on every platform
run: bash scripts/dryrun-smoke.sh

shell-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Test Pi backup failure status
run: bash linux-pi/backup/test-backup-failure.sh

- name: Test CUPS policy rendering
run: bash linux-pi/cups/test-setup.sh

zsh-syntax:
runs-on: ubuntu-latest
steps:
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ linux-pi/*/ts-state/
linux-pi/homepage/config/logs/
linux-pi/homepage/config/custom.css
linux-pi/homepage/config/custom.js
linux-pi/homepage/config/docker.yaml
linux-pi/homepage/config/kubernetes.yaml
linux-pi/homepage/config/proxmox.yaml

Expand Down
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ repos:
files: ^packages\.json$
pass_filenames: false

- id: python-syntax-check
name: python syntax check
language: system
entry: bash scripts/check-python-syntax.sh
types: [python]

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
Expand Down
118 changes: 118 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Computer-Setup

Personal machine-provisioning repo: one unified installer for macOS, Linux
desktop (Ubuntu/Arch), and an Ubuntu Server LTS home server, plus per-platform
configs and docs. A Raspberry Pi (`<pi-hostname>`, Debian) node lives in `linux-pi/`
— currently its own Docker Compose service stacks (a secondary AdGuard resolver);
folding it into the `setup.sh` base provisioning is still tracked in docs/TODO.md.

This file is the shared instruction set for **every** coding agent working in
this repo (Claude Code, opencode, Codex, …). `CLAUDE.md` imports it and adds
Claude-Code-only notes on top; keep cross-agent guidance here, not there.

## Entrypoints

- `setup.sh` — installs everything for the detected platform.
Flags: `--optional --work --personal --base --tags <csv> --dry-run --platform <macos|ubuntu|arch|server> --profile <desktop|server>`.
The server platform is never auto-detected (`--profile server` or `--platform server` required).
`--base` installs only the high-priority base set; `--tags development,terminal`
installs base + those `packages.json` tag categories; a bare TTY run with no
selection flag prompts interactively except on the server profile. Selection
mechanics and the custom-step gating: `docs/PACKAGES.md`.
- `verify.sh` — read-only health check mirroring `setup.sh`'s selection logic.
Flags: `--optional --work --personal --all --platform <macos|ubuntu|arch|server>`
(no `--dry-run`). The server profile additionally checks NUT configuration and
services. Unknown flags warn and are ignored, they don't abort.

## Architecture

- `packages.json` — single source of truth for all package data. Managers are keyed
by platform (`{macos, ubuntu, arch, server}`); `<platform>_name` overrides the
install token; `environment` gates on `--work`/`--personal`; `custom` managers
carry an `install_command` (auto-run when `handled_by_setup`, else a reminder);
`tags` is a required category array. The tier/gating fields `priority`, `optional`,
`environment`, and `install_command` can be a scalar or a per-platform object.
**Full schema, per-platform resolution, the `environment` caveat, and the tag
filter live in `docs/PACKAGES.md` — read it before editing `packages.json`.**
- `lib/core.sh` — shared engine: arg parsing, platform detection, env filter,
jq selection, install loops, config deploys. `lib/verify.sh` — check engine.
- `platforms/<platform>.sh` — per-platform quirks only (bootstrap, manager
invocations).
- `dotfiles/` — configs shared across platforms (`tmux.conf`,
`ghostty.config`, `zshrc.example`, `zsh_plugins.txt`); the engine deploys
them from here. One zshrc base serves every platform including the headless
server — macOS-specific bits guard on `/opt/homebrew` or `$OSTYPE`, and the
desktop-only bits self-disable headless (notify hook no-ops without
`$DISPLAY`/`$WAYLAND_DISPLAY`, fastfetch keys off Ghostty or `$SSH_CONNECTION`,
version managers/zoxide are command-guarded). The override system
(`deploy_zshrc`) still lets a platform folder ship its own `zshrc.example` to
win over the base, but no platform currently does.
- `macOS/`, `linux-desktop/`, `linux-server/` — platform-specific configs,
docs, and thin shim scripts that exec the root entrypoints. `linux-pi/` holds
the Raspberry Pi node's Docker Compose service stacks (same
`<service>/{docker-compose.yml,.env.example,ts-serve.json}` layout as
`linux-server/`), not yet wired into `setup.sh`.
- `scripts/dryrun-smoke.sh` — runs `setup.sh --dry-run` for every platform and
asserts it exits clean with install actions; also run in CI.

`docs/UNIFICATION.md` is the design doc for this layout; `docs/CHANGELOG.md` records
what shipped and `docs/TODO.md` tracks remaining work.

## Conventions

- Pre-commit runs `shellcheck --severity=warning` on all shell scripts;
`zsh -n` checks `.zsh` files and `zshrc.example`; `scripts/validate-packages.sh`
enforces the `packages.json` schema (platform vocabulary, controlled tag set,
and the "no silent drop" rule — every platform a package targets must resolve a
valid priority tier and a boolean optional). All three also run in CI.
- Probe semantics in `lib/verify.sh` are platform-faithful ports — macOS has no
`command -v` fallback for casks/pipx/app-store, Linux falls back everywhere.
Don't "fix" the asymmetry without checking `docs/UNIFICATION.md` history.
- `--dry-run` must print every command without executing anything. Before committing
changes to `setup.sh`, `lib/`, `platforms/`, or `packages.json`, exercise it across
all four platforms; only one platform can run live.
- Before committing changes to `.env` handling, `custom` `install_command` shell
execution, or path/network code, perform a security review.
- App-store packages and `priority: "none"` entries are reminders only — never
auto-installed.

## Coding conventions

These apply to every agent (the repo is almost entirely Bash).

- **Comments:** none by default. Add one only when the *why* is non-obvious — a
hidden constraint, a bug workaround, a subtle invariant. Never narrate *what*
the code does; well-named identifiers cover that.
- **No speculative design.** Don't build for hypothetical future requirements;
three similar lines beat a premature abstraction. Don't add features, refactors,
or abstractions beyond what the task needs.
- **Validate only at boundaries** (user input, external APIs). Trust internal code
and framework guarantees — no error handling or fallbacks for cases that can't
happen.
- **Prefer editing existing files** to creating new ones; delete removed code
cleanly rather than leaving back-compat shims.
- **Bash style:**
- `#!/usr/bin/env bash` shebang; `set -euo pipefail` at the top of every
non-trivial script.
- `[[ ]]` not `[ ]`; quote all expansions (`"$var"`, `"${arr[@]}"`).
- `printf` not `echo`; declare function-local vars with `local`; write errors to
stderr (`printf 'error: %s\n' "$msg" >&2`).
- `command -v foo` over `which foo`; herestring (`<<< "$var"`) over `echo "$var" |`.

## Privacy & Security

This repo is **public**. Never commit identifying or secret information.

- Keep these out of tracked files entirely: tailnet names / MagicDNS suffixes
(`tailXXXXXX.ts.net`), real hostnames, server IPs, usernames, emails, tokens,
auth keys, and personal absolute paths.
- Put any machine-specific or private value in a `.env` file (gitignored
repo-wide) and ship a committed `.env.example` with placeholders instead —
e.g. `linux-server/forgejo/.env.example`, `macOS/forgejo-runner/.env.example`.
Scripts read these via `${VAR:-<placeholder>}` and source a local `.env` when
present; they never hardcode the real value.
- In docs and configs use placeholders: `<tailnet>`, `<server-ip>`,
`<username>`, `<hostname>`. Default to `.env` whenever a value is
identifying — prefer one more env var over leaking a real value.
- When editing, scan the diff for accidentally introduced real identifiers
before committing.
111 changes: 9 additions & 102 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,105 +1,12 @@
# Computer-Setup
@AGENTS.md

Personal machine-provisioning repo: one unified installer for macOS, Linux
desktop (Ubuntu/Arch), and an Ubuntu Server LTS home server, plus per-platform
configs and docs. A Raspberry Pi (`ollie-pi4`, Debian) node lives in `linux-pi/`
— currently its own Docker Compose service stacks (a secondary AdGuard resolver);
folding it into the `setup.sh` base provisioning is still tracked in TODO.md.
## Claude Code

## Entrypoints
The section above is imported from `AGENTS.md` — the shared instruction set for
all agents. Everything there applies. Below are Claude-Code-only notes for this
repo:

- `setup.sh` — installs everything for the detected platform.
Flags: `--optional --work --personal --base --tags <csv> --dry-run --platform <macos|ubuntu|arch|server> --profile <desktop|server>`.
The server platform is never auto-detected (`--profile server` or `--platform server` required).
Category selection: `--base` installs only the high-priority base set; `--tags
development,terminal` installs base + those `packages.json` tag categories
(enabled work/personal apps install regardless of category). Run bare on a TTY
with no selection flag and `core_maybe_prompt_selection` prompts interactively;
it is skipped on the server profile and in non-interactive/CI runs. The filter
is implemented by `tagok()` in `CORE_JQ_DEFS`, which reads `TAG_FILTER_ACTIVE`
and `SELECTED_TAGS` from the environment — inactive by default, so flag-driven
and CI runs are unchanged. The dedicated custom-install steps (tailscale,
claude-code, docker) gate on `pkg_selected` so they honor the selection too;
the server profile keeps the filter inactive, so they install as before there.
- `verify.sh` — read-only health check mirroring `setup.sh`'s selection logic.
Flags: `--optional --work --personal --all --platform <macos|ubuntu|arch>`
(no `--dry-run`; `--platform server`/`--profile server` is rejected — nothing
legacy existed to port). Unknown flags warn and are ignored, they don't abort.

## Architecture

- `packages.json` — single source of truth for all package data. Managers are
keyed by platform (`{macos, ubuntu, arch, server}`); `<platform>_name`
overrides the install token; `environment` gates on `--work`/`--personal`;
`custom` managers carry `install_command` (string or per-platform object) —
run by the engine when `handled_by_setup` is true, otherwise printed as a
manual-install reminder.
- `priority`, `optional`, `environment`, and `install_command` each accept a
**scalar** (applies to every platform) **or a per-platform object** keyed by
platform (e.g. `"priority": { "macos": "medium", "ubuntu": "none" }`). The
engine resolves them via the `prfor`/`optfor`/`envfor`/`icfor` jq defs in
`lib/core.sh`. This is what lets one entry serve platforms that differ in
tier/optionality/gating, instead of splitting into duplicate entries.
- **`environment` caveat:** its scalar form is itself an *array* (`["work"]`),
so the per-platform form is detected as an *object* (`{ "ubuntu": ["work"] }`)
— array means legacy/all-platforms, object means per-platform. Keep the
per-platform value an object-of-arrays.
- `tags` — required non-empty array of descriptive categories from the
controlled vocabulary in `scripts/validate-packages.sh`. Metadata only
(grouping/docs); the install engine ignores them.
- `lib/core.sh` — shared engine: arg parsing, platform detection, env filter,
jq selection, install loops, config deploys. `lib/verify.sh` — check engine.
- `platforms/<platform>.sh` — per-platform quirks only (bootstrap, manager
invocations).
- `dotfiles/` — configs shared across platforms (`tmux.conf`,
`ghostty.config`, `zshrc.example`, `zsh_plugins.txt`); the engine deploys
them from here. One zshrc base serves every platform including the headless
server — macOS-specific bits guard on `/opt/homebrew` or `$OSTYPE`, and the
desktop-only bits self-disable headless (notify hook no-ops without
`$DISPLAY`/`$WAYLAND_DISPLAY`, fastfetch keys off Ghostty or `$SSH_CONNECTION`,
version managers/zoxide are command-guarded). The override system
(`deploy_zshrc`) still lets a platform folder ship its own `zshrc.example` to
win over the base, but no platform currently does.
- `macOS/`, `linux-desktop/`, `linux-server/` — platform-specific configs,
docs, and thin shim scripts that exec the root entrypoints. `linux-pi/` holds
the Raspberry Pi node's Docker Compose service stacks (same
`<service>/{docker-compose.yml,.env.example,ts-serve.json}` layout as
`linux-server/`), not yet wired into `setup.sh`.
- `scripts/dryrun-smoke.sh` — runs `setup.sh --dry-run` for every platform and
asserts it exits clean with install actions; also run in CI.

`docs/UNIFICATION.md` is the design doc for this layout; `CHANGELOG.md` records
what shipped and `TODO.md` tracks remaining work.

## Conventions

- Pre-commit runs `shellcheck --severity=warning` on all shell scripts;
`zsh -n` checks `.zsh` files and `zshrc.example`; `scripts/validate-packages.sh`
enforces the `packages.json` schema (platform vocabulary, controlled tag set,
and the "no silent drop" rule — every platform a package targets must resolve a
valid priority tier and a boolean optional). All three also run in CI.
- Probe semantics in `lib/verify.sh` are platform-faithful ports — macOS has no
`command -v` fallback for casks/pipx/app-store, Linux falls back everywhere.
Don't "fix" the asymmetry without checking `docs/UNIFICATION.md` history.
- `--dry-run` must print every command without executing anything; it is the
primary cross-platform test mechanism (only one platform can run live).
- App-store packages and `priority: "none"` entries are reminders only — never
auto-installed.

## Privacy & Security

This repo is **public**. Never commit identifying or secret information.

- Keep these out of tracked files entirely: tailnet names / MagicDNS suffixes
(`tailXXXXXX.ts.net`), real hostnames, server IPs, usernames, emails, tokens,
auth keys, and personal absolute paths.
- Put any machine-specific or private value in a `.env` file (gitignored
repo-wide) and ship a committed `.env.example` with placeholders instead —
e.g. `linux-server/forgejo/.env.example`, `macOS/forgejo-runner/.env.example`.
Scripts read these via `${VAR:-<placeholder>}` and source a local `.env` when
present; they never hardcode the real value.
- In docs and configs use placeholders: `<tailnet>`, `<server-ip>`,
`<username>`, `<hostname>`. Default to `.env` whenever a value is
identifying — prefer one more env var over leaking a real value.
- When editing, scan the diff for accidentally introduced real identifiers
before committing.
- Use **`/verify`** to satisfy the cross-agent verification requirement in
`AGENTS.md`.
- Use **`/security-review`** to satisfy the cross-agent security-review requirement
in `AGENTS.md`.
44 changes: 0 additions & 44 deletions HANDOFF.md

This file was deleted.

2 changes: 1 addition & 1 deletion SSH_and_GPG/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Creates an Ed25519 SSH key for a remote machine (e.g. a home server or Tailscale
```bash
bash add_remote_host.sh
# or pre-fill inputs via env vars:
HOST_ALIAS="homepc" REMOTE_HOST="192.168.1.100" REMOTE_USER="jane" PORT="22" bash add_remote_host.sh
HOST_ALIAS="homepc" REMOTE_HOST="<server-ip>" REMOTE_USER="<username>" PORT="22" bash add_remote_host.sh
```

---
Expand Down
2 changes: 1 addition & 1 deletion SSH_and_GPG/add_remote_host.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ prompt() {
}

prompt HOST_ALIAS "SSH alias (friendly name, e.g. homepc)"
prompt REMOTE_HOST "Remote hostname or IP (e.g. 192.168.1.100 or mypc.local)"
prompt REMOTE_HOST "Remote hostname or IP (e.g. <server-ip> or <hostname>.local)"
prompt REMOTE_USER "Remote username"
prompt PORT "SSH port" "22"
prompt KEY_NAME "Key file name (no path)" "$HOST_ALIAS"
Expand Down
Loading
Loading