Skip to content

Install-Script Discipline

Every host, VM, and container Hinata stands up must commit a runnable install script to this directory at the moment of standing it up. Not retrofitted. Not screenshotted. Not "I'll remember." If it's not a script, it doesn't exist.

This is the operational embodiment of the three-tier documentation axis in self-hosted-architecture §2.

The contract

A script in this directory must satisfy all five:

  1. Idempotent — running it twice produces the same end state, never duplicates or breaks

  2. Clean-slate runnable — assumes a freshly installed base OS at a stated version; bootstraps everything from there

  3. Self-documenting — header block declares: target host class, base OS, what it installs, expected runtime, prerequisites

  4. No interactive prompts — every value either hardcoded, env-var driven, or read from a stated source

  5. Named per naming register — once the register is published, scripts adopt its host/role naming

File layout

install-scripts/
├── understanding_install-script-discipline.md       ← this file
├── <hostname>-<role>.sh             ← top-level: build a named host
├── <hostname>-<role>.md              ← optional: design notes for that host
└── components/                       ← reusable fragments sourced by host scripts
    ├── bootstrap-debian-12.sh        ← OS-level setup
    ├── install-tailscale.sh
    ├── install-docker.sh
    └── …

Rule: if the same setup appears in two host scripts, extract it into components/ and source it. Don't copy-paste.

Required header for every script

#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────────────────────
# Host:        <hostname-from-naming-register>
# Role:        <one-line description>
# Base OS:     <distro + version>
# Provider:    <e.g. "GCP europe-west2-b e2-micro" or "Raspberry Pi 5 / 8GB">
# Built:       <YYYY-MM-DD when first run>
# Last edit:   <YYYY-MM-DD>
# Runtime:     <expected wall-clock for fresh run>
# Prereqs:
#   - <e.g. "Vaultwarden CLI session token in $BW_SESSION">
# Idempotent:  yes
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail

What does NOT belong here

  • Vault scripts (Telegram bots, normalisers, audit runners) — those live in /opt/jimmy-brain-ops/scripts/

  • One-off commands run interactively

  • LaunchAgent plists — those live in ~/Library/LaunchAgents/

When to write the script

At build time. Not after.

Concretely: the first time Jimmy Neutron stands up a host, the build session produces (a) the running host and (b) the committed script as parallel deliverables. A completed build that does not commit a script is not a completed build — it's a debt.

Credential handling

* Scripts must not hardcode secrets. Reference them by name.

* Read from `$ENV_VAR_NAME` at invocation time

* Header documents the expected env vars under Prereqs

* Never `echo` a secret value, even in dry-run mode