docs: add three LiveCD/embedded patterns from bee project
- alpine-livecd: mkimage profile rules, apkovl mechanics, workdir caching, squashfs compression, NIC firmware, long build survival via screen - vendor-installer-verification: checksum-before-download, cache validation, version URL verification before writing build scripts - unattended-boot-services: OpenRC invariants for headless environments, network-independent SSH, persistent DHCP, graceful degradation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
142
rules/patterns/alpine-livecd/contract.md
Normal file
142
rules/patterns/alpine-livecd/contract.md
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
# Contract: Alpine LiveCD Build
|
||||||
|
|
||||||
|
Version: 1.0
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Rules for building bootable Alpine Linux ISO images with custom overlays using `mkimage.sh`.
|
||||||
|
Applies to any project that needs a LiveCD: hardware audit, rescue environments, kiosks.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## mkimage Profile
|
||||||
|
|
||||||
|
Every project must have a profile file `mkimg.<name>.sh` defining:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
profile_<name>() {
|
||||||
|
arch="x86_64" # REQUIRED — without this mkimage silently skips the profile
|
||||||
|
hostname="<hostname>"
|
||||||
|
apkovl="genapkovl-<name>.sh"
|
||||||
|
image_ext="iso"
|
||||||
|
output_format="iso"
|
||||||
|
kernel_flavors="lts"
|
||||||
|
initfs_cmdline="modules=loop,squashfs,sd-mod,usb-storage quiet"
|
||||||
|
initfs_features="ata base cdrom ext4 mmc nvme raid scsi squashfs usb virtio"
|
||||||
|
grub_mod="all_video disk part_gpt part_msdos linux normal configfile search search_label efi_gop fat iso9660 cat echo ls test true help gzio"
|
||||||
|
apks="alpine-base linux-lts linux-firmware-none ..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**`arch` is mandatory.** If missing, mkimage silently builds nothing and exits 0.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## apkovl Mechanism
|
||||||
|
|
||||||
|
The apkovl is a `.tar.gz` overlay extracted by initramfs at boot, overlaying `/etc`, `/usr`, `/root`.
|
||||||
|
|
||||||
|
`genapkovl-<name>.sh` generates the tarball:
|
||||||
|
- Must be in the **CWD** when mkimage runs — not only in `~/.mkimage/`
|
||||||
|
- `~/.mkimage/` is searched for mkimg profiles only, not genapkovl scripts
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Copy both scripts to ~/.mkimage AND to CWD (typically /var/tmp)
|
||||||
|
cp "genapkovl-<name>.sh" ~/.mkimage/
|
||||||
|
cp "genapkovl-<name>.sh" /var/tmp/
|
||||||
|
cd /var/tmp
|
||||||
|
sh mkimage.sh --workdir /var/tmp/work ...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Build Environment
|
||||||
|
|
||||||
|
**Always use `/var/tmp`, not `/tmp`:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export TMPDIR=/var/tmp
|
||||||
|
cd /var/tmp
|
||||||
|
sh mkimage.sh ...
|
||||||
|
```
|
||||||
|
|
||||||
|
`/tmp` on Alpine builder VMs is typically a 1GB tmpfs. Kernel firmware squashfs alone exceeds this.
|
||||||
|
`/var/tmp` uses actual disk space.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workdir Caching
|
||||||
|
|
||||||
|
mkimage stores each ISO section in a hash-named subdirectory. Preserve expensive sections across builds:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Delete everything EXCEPT cached sections
|
||||||
|
if [ -d /var/tmp/bee-iso-work ]; then
|
||||||
|
find /var/tmp/bee-iso-work -maxdepth 1 -mindepth 1 \
|
||||||
|
-not -name 'apks_*' \ # downloaded packages
|
||||||
|
-not -name 'kernel_*' \ # modloop squashfs
|
||||||
|
-not -name 'syslinux_*' \ # syslinux bootloader
|
||||||
|
-not -name 'grub_*' \ # grub EFI
|
||||||
|
-exec rm -rf {} +
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
The apkovl section is always regenerated (contains project-specific config that changes per build).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Squashfs Compression
|
||||||
|
|
||||||
|
Default compression is `xz` — slow but small. For RAM-loaded modloops, size rarely matters.
|
||||||
|
Use `lz4` for faster builds:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p /etc/mkinitfs
|
||||||
|
grep -q 'MKSQUASHFS_OPTS' /etc/mkinitfs/mkinitfs.conf 2>/dev/null || \
|
||||||
|
echo 'MKSQUASHFS_OPTS="-comp lz4 -Xhc"' >> /etc/mkinitfs/mkinitfs.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Apply before running mkimage. Rebuilds modloop only when kernel version changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Long Builds
|
||||||
|
|
||||||
|
NVIDIA driver downloads, kernel compiles, and package fetches can take 10–30 minutes.
|
||||||
|
Run in a `screen` session so builds survive SSH disconnects:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
apk add screen
|
||||||
|
screen -dmS build sh -c "sh build.sh > /var/log/build.log 2>&1"
|
||||||
|
tail -f /var/log/build.log
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## NIC Firmware
|
||||||
|
|
||||||
|
`linux-firmware-none` (default) contains zero firmware files. Real hardware NICs often require firmware.
|
||||||
|
Include firmware packages matching expected hardware:
|
||||||
|
|
||||||
|
```
|
||||||
|
linux-firmware-intel # Intel NICs (X710, E810, etc.)
|
||||||
|
linux-firmware-mellanox # Mellanox/NVIDIA ConnectX
|
||||||
|
linux-firmware-bnx2x # Broadcom NetXtreme
|
||||||
|
linux-firmware-rtl_nic # Realtek
|
||||||
|
linux-firmware-other # catch-all
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
Pin all versions in a single `VERSIONS` file sourced by all build scripts:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ALPINE_VERSION=3.21
|
||||||
|
KERNEL_VERSION=6.12
|
||||||
|
GO_VERSION=1.23.6
|
||||||
|
NVIDIA_DRIVER_VERSION=590.48.01
|
||||||
|
```
|
||||||
|
|
||||||
|
Never hardcode versions inside build scripts.
|
||||||
133
rules/patterns/unattended-boot-services/contract.md
Normal file
133
rules/patterns/unattended-boot-services/contract.md
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# Contract: Unattended Boot Services (OpenRC)
|
||||||
|
|
||||||
|
Version: 1.0
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Rules for OpenRC services that run in unattended environments: LiveCDs, kiosks, embedded systems.
|
||||||
|
No user is present. No TTY prompts. Every failure path must have a silent fallback.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core Invariants
|
||||||
|
|
||||||
|
- **Never block boot.** A service failure must not prevent other services from starting.
|
||||||
|
- **Never prompt.** No `read`, no `pause`, no interactive input of any kind.
|
||||||
|
- **Always exit 0.** Use `eend 0` at the end of `start()` regardless of the operation result.
|
||||||
|
- **Log everything.** Write results to `/var/log/` so SSH inspection is possible after boot.
|
||||||
|
- **Fail silently, degrade gracefully.** Missing tool → skip that collector. No network → skip network-dependent steps.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Service Dependencies
|
||||||
|
|
||||||
|
Use the minimum necessary dependencies:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
depend() {
|
||||||
|
need localmount # almost always needed
|
||||||
|
after some-service # ordering without hard dependency
|
||||||
|
use logger # optional soft dependency
|
||||||
|
# DO NOT add: need net, need networking, need network-online
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Never use `need net` or `need networking`** unless the service is genuinely useless without
|
||||||
|
network and you want it to fail loudly when no cable is connected.
|
||||||
|
For services that work with or without network, use `after` instead.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Network-Independent SSH
|
||||||
|
|
||||||
|
Dropbear (and any SSH server) must start without network being available.
|
||||||
|
Common mistake: installing dropbear-openrc which adds `need net` in its default init.
|
||||||
|
|
||||||
|
Override with a custom init:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/sbin/openrc-run
|
||||||
|
description="SSH server"
|
||||||
|
|
||||||
|
depend() {
|
||||||
|
need localmount
|
||||||
|
after bee-sshsetup # key/user setup, not networking
|
||||||
|
use logger
|
||||||
|
# NO need net
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
check_config || return 1
|
||||||
|
ebegin "Starting dropbear"
|
||||||
|
/usr/sbin/dropbear ${DROPBEAR_OPTS}
|
||||||
|
eend $?
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Place this file in the overlay at `etc/init.d/dropbear` — it overrides the package-installed version.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Persistent DHCP
|
||||||
|
|
||||||
|
Do not use blocking DHCP (`-n` flag exits if no offer). Use background mode so the client
|
||||||
|
retries automatically when a cable is connected after boot:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Wrong — exits immediately if no DHCP offer
|
||||||
|
udhcpc -i "$iface" -t 3 -T 5 -n -q
|
||||||
|
|
||||||
|
# Correct — background daemon, retries indefinitely
|
||||||
|
udhcpc -i "$iface" -b -t 0 -T 3 -q
|
||||||
|
```
|
||||||
|
|
||||||
|
The network service itself should complete immediately (exit 0) — udhcpc daemons run in background.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Service Start Order (typical LiveCD)
|
||||||
|
|
||||||
|
```
|
||||||
|
localmount
|
||||||
|
└── sshsetup (user creation, key injection — before dropbear)
|
||||||
|
└── dropbear (SSH — independent of network)
|
||||||
|
└── network (DHCP on all interfaces — does not block anything)
|
||||||
|
└── nvidia (or other hardware init — after network in case firmware needs it)
|
||||||
|
└── audit (main workload — after all hardware ready)
|
||||||
|
```
|
||||||
|
|
||||||
|
Services at the same level can start in parallel. Use `after` not `need` for ordering without hard dependency.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error Handling in start()
|
||||||
|
|
||||||
|
```sh
|
||||||
|
start() {
|
||||||
|
ebegin "Running audit"
|
||||||
|
/usr/local/bin/audit --output /var/log/audit.json >> /var/log/audit.log 2>&1
|
||||||
|
local rc=$?
|
||||||
|
if [ $rc -eq 0 ]; then
|
||||||
|
einfo "Audit complete"
|
||||||
|
else
|
||||||
|
ewarn "Audit finished with errors — check /var/log/audit.log"
|
||||||
|
fi
|
||||||
|
eend 0 # always 0 — never fail the runlevel
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Capture exit code into a local variable.
|
||||||
|
- Log the result with `einfo` or `ewarn`.
|
||||||
|
- Always `eend 0` — a failed audit is not a reason to block the boot runlevel.
|
||||||
|
- The exception: services whose failure makes SSH impossible (e.g. key setup) may `return 1`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- Every `start()` ends with `eend 0` unless failure makes the entire environment unusable.
|
||||||
|
- Network is always best-effort. Test for it, don't depend on it.
|
||||||
|
- Proprietary drivers (NVIDIA, etc.): load failure → log warning → continue without enrichment.
|
||||||
|
- External tools (ipmitool, smartctl, etc.): not-found → skip that data source → do not abort.
|
||||||
|
- Timeout all external commands: `timeout 30 smartctl ...` prevents infinite hangs.
|
||||||
|
- Write all output to `/var/log/` — TTY output is secondary.
|
||||||
82
rules/patterns/vendor-installer-verification/contract.md
Normal file
82
rules/patterns/vendor-installer-verification/contract.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# Contract: Vendor Installer Verification
|
||||||
|
|
||||||
|
Version: 1.0
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Rules for downloading and verifying proprietary vendor installers (`.run`, `.exe`, `.tar.gz`)
|
||||||
|
where the vendor publishes a checksum alongside the binary.
|
||||||
|
Applies to: NVIDIA drivers, vendor CLI tools, firmware packages.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Download Order
|
||||||
|
|
||||||
|
Always download the checksum file **before** the installer:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
BASE_URL="https://vendor.example.com/downloads/${VERSION}"
|
||||||
|
BIN_FILE="/var/cache/vendor-${VERSION}.run"
|
||||||
|
SHA_FILE="/var/cache/vendor-${VERSION}.run.sha256sum"
|
||||||
|
|
||||||
|
# 1. Download checksum first
|
||||||
|
wget -q -O "$SHA_FILE" "${BASE_URL}/vendor-${VERSION}.run.sha256sum"
|
||||||
|
|
||||||
|
# 2. Download installer
|
||||||
|
wget --show-progress -O "$BIN_FILE" "${BASE_URL}/vendor-${VERSION}.run"
|
||||||
|
|
||||||
|
# 3. Verify
|
||||||
|
cd /var/cache
|
||||||
|
sha256sum -c "$SHA_FILE" || { echo "ERROR: sha256 mismatch"; rm -f "$BIN_FILE"; exit 1; }
|
||||||
|
```
|
||||||
|
|
||||||
|
Reason: if the download is interrupted, you have the expected checksum to verify against on retry.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cache with Verification
|
||||||
|
|
||||||
|
Never assume a cached file is valid — a previous download may have been interrupted (0-byte file):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
verify_cached() {
|
||||||
|
[ -s "$SHA_FILE" ] || return 1 # sha256 file missing or empty
|
||||||
|
[ -s "$BIN_FILE" ] || return 1 # binary missing or empty
|
||||||
|
cd "$(dirname "$BIN_FILE")"
|
||||||
|
sha256sum -c "$SHA_FILE" --status 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! verify_cached; then
|
||||||
|
rm -f "$BIN_FILE" "$SHA_FILE"
|
||||||
|
# ... download and verify
|
||||||
|
else
|
||||||
|
echo "verified from cache"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
**Never check only for file existence.** Check that the file is non-empty (`-s`) AND passes checksum.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version Validation
|
||||||
|
|
||||||
|
Before writing build scripts, verify the version URL actually exists:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -sIL "https://vendor.example.com/downloads/${VERSION}/installer.run" \
|
||||||
|
| grep -i 'http/\|content-length'
|
||||||
|
```
|
||||||
|
|
||||||
|
A `404` or `content-length: 0` means the version does not exist on that CDN.
|
||||||
|
Vendor version numbering may have gaps (e.g. NVIDIA skips minor versions on some CDNs).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- Download checksum before installer — never after.
|
||||||
|
- Verify checksum before extracting or executing.
|
||||||
|
- On mismatch: delete the file, exit with error. Never proceed with a bad installer.
|
||||||
|
- Cache by `version` + any secondary key (e.g. kernel version for compiled modules).
|
||||||
|
- Never commit installer files to git — always download at build time.
|
||||||
|
- Log the expected hash when downloading so failures are diagnosable.
|
||||||
Reference in New Issue
Block a user