# Core Scripts Reference Everything that runs during the bootstrap install: `install.sh`, `preinstall.sh`, `postinstall.sh`, the shared libraries, and `features/`. For the `pos` CLI tools see [POS.md](POS.md), for apps see [APPS.md](APPS.md), for services see [SYSTEMD.md](SYSTEMD.md). --- ## Table of contents - [install.sh — the orchestrator](#installsh--the-orchestrator) - [preinstall.sh — system packages](#preinstallsh--system-packages) - [postinstall.sh — user configuration](#postinstallsh--user-configuration) - [lib/common.sh — shared library](#libcommonsh--shared-library) - [lib/flags.sh — feature flags](#libflagssh--feature-flags) - [lib/notify.sh — multi-platform alerting](#libnotifysh--multi-platform-alerting) - [lib/entertainment-lib.sh — entertainment module](#libentertainmentlibsh--entertainment-module) - [lib/user-timers-lib.sh — shared systemd user timers](#libusertimerslibsh--shared-systemd-user-timers) - [lib/usb-lib.sh — shared USB-storage detection](#libusblibsh--shared-usb-storage-detection) - [lib/share-lib.sh — share-suite domain layer + compat shims](#sharelibsh--share-suite-domain-layer--compat-shims) - [lib/menu-lib.sh — category-neutral menu primitives](#libmenulibsh--category-neutral-menu-primitives) - [lib/registry.sh — tool metadata query API](#libregistrysh--tool-metadata-query-api) - [features/autostart.sh — boot-time feature](#featuresautostartsh--boot-time-feature) - [features/usb-automount.sh — USB automount feature](#featuresusb-automountsh--usb-automount-feature) - [x64_bin/ — precompiled binaries](#x64_bin--precompiled-binaries) --- ## install.sh — the orchestrator **File:** `install.sh` (run as `./install.sh`) **Purpose:** the entry point. Coordinates all four install phases and the optional apps/features installs. ### How it works 1. **Pre-parse `--no-color`** before anything else, so colors are disabled early (`TERM=dumb` is exported). 2. Source `lib/common.sh` (logging, `run`, `spawn`, …) and `lib/flags.sh` (feature flags). 3. Parse CLI options. 4. **Version gate:** derive the current version (`0.0c` via `install_version()`; empty when `.git` is absent). If the installed version (stored as the `installed_version` flag) matches and `--force` is not given, skip the install — with `--dry-run` it prints `(dry-run) Would skip install: already at version `, otherwise `Already installed (). Use --force to re-install.` and exits 0. When no `installed_version` flag exists or the current version cannot be determined (no `.git`), the gate is skipped. 5. For each phase, `should_run ` decides whether to run it: - `--skip ` removes a phase (takes precedence). - `--steps ` restricts the run to the listed phases only (`1,3,4` or `1-3`). - Phase map: `1=preinstall`, `2=scripts`, `3=postinstall`, `4=scalepoint` (+ `apps` handled separately). The phases: | # | Phase | Script/action | |---|-------|----------------| | 1 | preinstall | `preinstall.sh` — apt packages + yt-dlp | | 2 | scripts | Copies `bin/*` → `/usr/local/bin/` (755), `lib/common.sh` + `lib/flags.sh` + `lib/notify.sh` + `lib/entertainment-lib.sh` + `lib/entertainment-plugin-lib.sh` + `lib/scheduler-lib.sh` + `lib/config-ui.sh` + `lib/user-timers-lib.sh` + `lib/usb-lib.sh` + `lib/share-lib.sh` + `lib/menu-lib.sh` + `lib/registry.sh` → `/usr/local/bin/` (644). Copies precompiled arch binaries from `x64_bin/` (or `arm64_bin/`) → `/usr/local/bin/`. With `--feature`: also installs `features/*` (see below) | | 3 | postinstall | `postinstall.sh` — PATH, completion, SSH keys, systemd | | 4 | scalepoint | Shallow-clones ScaleTail templates to `/usr/local/share/linux_post_install/scale-tail` | | 5 (opt) | apps | `apps/install.sh` when `--apps` (interactive) or `--full` (all, non-interactive) | **Precompiled arch binaries (Phase 2):** `install.sh` picks the source folder from the machine architecture — `x86_64` → `x64_bin/`, `aarch64`/`arm64` → `arm64_bin/` (added later) — and copies every file in it to `/usr/local/bin/` (755). These are manually-compiled tools not available as internet builds (currently `create_ap`, `wihotspot`, `wihotspot-gui`). Dropping an `arm64_bin/` folder later needs no code change. **Feature block (Phase 2, only with `--feature`):** for every file in `features/` it copies it to `/usr/local/bin/`. If the destination already exists it asks **"Overwrite existing …? [y/N]"** (default keeps your file), then always sets the feature flag via `flag_set` (name derived as ``). ### Configuration No config file — everything is command-line: | Option | Effect | |--------|--------| | `--apps` | Run the interactive app picker after core install | | `--full` | Core install + every app (non-interactive) | | `--feature` | Install `features/` scripts to `/usr/local/bin/` (prompts on overwrite), sets their flags | | `--dry-run` | Log every action instead of executing. **Note:** applies to `install.sh` itself; `postinstall.sh` runs as a subprocess and does not inherit `DRY_RUN` | | `--force` | Re-install even if the version matches | | `--skip ` | Skip a phase (repeatable): `preinstall`, `scripts`, `postinstall`, `scalepoint`, `apps` | | `--steps ` | Run only listed phases: `1,3,4` or `1-3` | | `--no-color` | Disable colored output | | `-h`, `--help` | Show usage | --- ## preinstall.sh — system packages **File:** `preinstall.sh` **Purpose:** Phase 1 — installs the base system packages and yt-dlp. **Run:** automatically by `install.sh`, or standalone with `--dry-run`. ### How it works 1. `apt update`. 2. Installs the package list. 3. Downloads the latest `yt-dlp` binary to `/usr/local/bin/yt-dlp` and makes it executable. 4. Verifies a couple of tools (`git --version`, `yt-dlp --version`). ### Configuration The package list is the `PACKAGES` array: ```bash PACKAGES=( git curl wget vim nano tmux tree jq unzip zip rsync htop btop telnet net-tools iputils-ping traceroute tcpdump nmap openssh-client openssh-server ufw fail2ban hostapd dnsmasq iptables iw ca-certificates gnupg lsb-release python3 python3-pip rclone libqrencode4 libgtk-3-0 ) ``` Add or remove package names here. `nmap` and `fail2ban` are used later by `pos network scan` and `postinstall.sh`; `hostapd`, `dnsmasq`, `iptables`, `iw` and the GTK/Qr libs support the precompiled hotspot tools (see [x64_bin/ — precompiled binaries](#x64_bin--precompiled-binaries)). --- ## postinstall.sh — user configuration **File:** `postinstall.sh` (runs as your user) **Purpose:** Phase 3 — configures the user environment, SSH keys, and systemd services. ### How it works 1. **rclone config** — if `config/rclone.conf` exists (gitignored), installs it to `~/.config/rclone/rclone.conf` (600). 2. **PATH** — appends a `PATH` line to `~/.bashrc` if not already present. 3. **pos bash completion** — installs `completions/pos.bash` to `/usr/local/share/bash-completion/completions/` and sources it from `~/.bashrc`. 4. **SSH authorized keys** — if `config/authorized_keys` exists, appends missing keys to `~/.ssh/authorized_keys` (skips comments and duplicates, chmod 600). 5. **systemd services** — copies `systemd/*.service` to `/etc/systemd/system/`, daemon-reloads, then enables each service. **`autostart.service` is only enabled when the `autostart` feature flag is set**, and **`usb-automount.service` only when the `usb-automount` flag is set** (see [lib/flags.sh](#libflagssh--feature-flags)); otherwise they're skipped with a hint to run `./install.sh --feature`. ### Configuration - SSH keys: `config/authorized_keys` (one per line, gitignored). - rclone config: `config/rclone.conf` (gitignored). - The PATH line and completion line are embedded strings at the top of the file — edit there to change them. - The `autostart` flag (set by `./install.sh --feature`) controls whether `autostart.service` gets enabled. --- ## lib/common.sh — shared library **File:** `lib/common.sh` (installed to `/usr/local/bin/common.sh`) **Purpose:** colors, logging, timers, spinners, dry-run-aware execution, and prompts. Sourced by most scripts. ### How it works Auto-disables colors when stdout is not a TTY. The `run` helper is the dry-run hook: scripts that want `--dry-run` support run every side-effecting command through `run`. ### Configuration / API | Function | Purpose | |----------|---------| | `log "msg"` | Green `[+]` status line | | `warn "msg"` | Yellow `[!]` warning | | `err "msg"` | Red `ERROR:` line to stderr, then `exit 1` | | `ok "msg"` | Green `OK` prefix line | | `section "title"` | Cyan-bordered section header | | `step N T "msg"` | Numbered step header (`[N/T] msg`) | | `run cmd…` | Executes the command, or logs `(dry-run)` when `DRY_RUN=1` | | `spawn "msg" cmd…` | Runs with an animated spinner + elapsed time; prints captured stderr and exits on failure | | `timer_start` / `timer_stop` | Track and print elapsed time | | `confirm "prompt" [default]` | Yes/no prompt; default `y` (`[Y/n]`) unless `n` given (`[y/N]`) | --- ## lib/flags.sh — feature flags **File:** `lib/flags.sh` (installed to `/usr/local/bin/flags.sh`) **Purpose:** a system-wide, per-feature flag store. Flags mark features as installed/opted-in and gate behavior (e.g. systemd enablement) elsewhere. ### How it works One file per flag in `$FLAGS_DIR`. **Presence = set, file content = optional value.** Reads are plain file ops; writes go through `run` + `sudo` so they respect `--dry-run`. Installed by `./install.sh --feature`; also usable directly: ```bash source lib/flags.sh flag_set autostart # bare flag flag_set app "2.1" # flag with a value flag_is_set autostart # 0 if set, 1 if not flag_value app # prints "2.1" flag_list # names of all set flags flag_clear autostart ``` ### Configuration | Setting | Location | |---------|----------| | `FLAGS_DIR` (env) | Default `/usr/local/share/linux_post_install/flags` (dir 755, files 644). Overridable via environment for testing | | CLI wrappers | `flag-reader`, `flag-set`, `flag-clear` (see [POS.md](POS.md)) | --- ## lib/notify.sh — multi-platform alerting **File:** `lib/notify.sh` (installed to `/usr/local/bin/notify.sh`) **Purpose:** opt-in alerting helper for tools that announce events. Delivers to every platform in `NOTIFY_PLATFORM` via the sender contract `pos-communication- send [--markdown]` (default platform: `telegram`). Self-contained by design: defines **only** `notify_send()` + `notify_platforms()`, so sourcing it never clobbers a tool's own `log`/`warn`/`err`. **Silent-fails per platform** — a missing sender or failed send only warns and never changes the caller's exit code. ```bash source "$(dirname "$0")/../lib/notify.sh" 2>/dev/null || source "$(dirname "$0")/notify.sh" notify_send "Backup completed" notify_send "**disk full**" --markdown ``` Platform selection: `~/.config/linux_post_install/notify.env` (`NOTIFY_PLATFORM=telegram,matrix`, comma-separated = fan out; env var wins over the file). Adding a platform = drop a `bin/pos-communication-` sender + list it — no change to `lib/notify.sh`. The telegram platform key maps to tool `pos-communication-telegram-sender` via `notify_sender_name()`. --- ## lib/entertainment-lib.sh — entertainment module **File:** `lib/entertainment-lib.sh` (installed to `/usr/local/bin/entertainment-lib.sh`) **Purpose:** shared logic for the `pos entertainment` tools — config (`entertainment.env`), `ENABLED` auto-trigger list parsing (`plugin, interval` pairs), plugin lookup by `# POS_PLUGIN:` marker, per-plugin last-run state (`~/.local/share/linux_post_install/entertainment/last/`), and scheduler reconciliation. Timer machinery (interval→OnCalendar, unit pair writer, linger) comes from `lib/user-timers-lib.sh`, shared with the system scheduler. Sourced by `bin/pos-entertainment-send|config|enable|disable|status` (after `lib/common.sh`). **Plugins must not source it** — their stdout is the sent message; they may instead source `lib/entertainment-plugin-lib.sh` (message-safe helpers: config load, `plugin_have`/`plugin_require`, `plugin_http_json` with retry). --- ## lib/user-timers-lib.sh — shared systemd user timers **File:** `lib/user-timers-lib.sh` (installed to `/usr/local/bin/user-timers-lib.sh`) **Purpose:** the one copy of the systemd **user** timer machinery used by both the entertainment module and the system scheduler — `ut_interval_to_oncalendar` (interval→`OnCalendar`, incl. raw `OnCalendar=…` passthrough), `ut_interval_label`, `ut_unit_name`, `ut_write_unit_pair` (oneshot service + `Persistent=true` timer, `TimeoutStopSec=5s`, `network-online` deps), and `ut_ensure_linger`. Sourced by `lib/entertainment-lib.sh` and `lib/scheduler-lib.sh`; defines only `ut_*` so it never collides with either. --- ## lib/usb-lib.sh — shared USB-storage detection **File:** `lib/usb-lib.sh` (installed to `/usr/local/bin/usb-lib.sh`) **Purpose:** the one copy of the USB-storage machinery shared by `pos system backup`'s post-verify USB copy and `pos media sync` — `usb_detect` (lsblk JSON, TRAN + lsusb/by-id cross-check → `USB_MOUNTED` as `mp|label|size|model|fs` entries and `USB_UNMOUNTED` as `path|label|size|model`; EFI system partitions — Ventoy `VTOYEFI`, `/boot/efi` — are excluded from both), `usb_related_present`, `usb_mount_offer` (mount an unmounted stick at `/media/