From 023b9394d301b936b1376e76974f65bfac9d7fdd Mon Sep 17 00:00:00 2001 From: 28allday Date: Sun, 31 May 2026 13:29:29 +0100 Subject: [PATCH] omalocal: track upstream OMATERM installer, fix first-login tmux + Once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brings the first-login server setup back to fully working on a fresh install: - OMATERM: call install-native.sh directly. omaterm.org/install is now an interactive native/docker dispatcher that would stall the unattended first-run; the native branch is what a headless Arch box wants. Verified the closing `exec bash -l` and bundled AI agents (mise.packages) still hold. - Rebrand the user-facing "OMATERM + Once" headline to "omalocal" (banner, setup prompt, closing reboot message); body still names the installed tools. - Fix tmux "can't use /dev/tty" on first login. OMATERM's installer closes with `exec bash -l /dev/tty 2>&1`; with fds on /dev/tty, tmux's client ttyname is the literal "/dev/tty", which tmux's server_client_open refuses. Strip that redirect from the downloaded installer and run it without redirecting our stdin, so the closing shell inherits the login pts (/dev/pts/N) — which tmux accepts. (script(1) did not work: OMATERM re-redirects to /dev/tty after it.) - Fix Once not installing. Once installs before OMATERM (which used to provide Docker); Once requires Docker and can't install it on Arch (get.docker.com is unsupported there). Provision Docker in base: pacstrap docker, enable docker.service in the chroot, and add the install user to the docker group at install time, so the first-run Once install finds a running daemon and once/once-add need no sudo. OMATERM's later docker setup no-ops. - gitignore dated build-*.log files. Full chain confirmed working on a clean install: Welcome to omalocal -> Once installs -> Omarchy-Send (optional) -> OMATERM -> lands in tmux. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 1 + omalocal.sh | 92 +++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 76 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index ca8b2c8..a45c765 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ omaterm-arch-*.iso *.ppm *.png build.log +build-*.log diff --git a/omalocal.sh b/omalocal.sh index c5acdc8..ec54921 100755 --- a/omalocal.sh +++ b/omalocal.sh @@ -240,7 +240,7 @@ echo "==> Pacstrap (this is the slow bit)..." pacstrap -K /mnt \ base linux linux-firmware intel-ucode amd-ucode \ networkmanager openssh sudo git curl vim less \ - base-devel ufw avahi nss-mdns \ + base-devel ufw avahi nss-mdns docker \ kitty-terminfo foot-terminfo ghostty-terminfo rxvt-unicode-terminfo echo "==> Generating fstab..." @@ -263,6 +263,13 @@ systemctl enable sshd.service # hosts: NSS line per the Arch wiki recommendation. systemctl enable avahi-daemon.service sed -i '/^hosts:/ s/\bresolve\b/mdns_minimal [NOTFOUND=return] resolve/' /etc/nsswitch.conf +# Docker is in base (not just from OMATERM) because the first-run flow installs +# Once BEFORE OMATERM, and Once requires Docker. Its installer's fallback is +# `curl get.once.com ... | get.docker.com | sh`, but the Docker convenience +# script does NOT support Arch and aborts — so Once must find Docker already +# present. Enable it here so the daemon is up by first login. OMATERM's later +# `pacman --needed docker` + enable are then no-ops. +systemctl enable docker.service bootctl install cat > /boot/loader/loader.conf < Creating user $USERNAME..." arch-chroot /mnt useradd -m -G wheel -s /bin/bash "$USERNAME" printf 'root:%s\n%s:%s\n' "$USER_PW" "$USERNAME" "$USER_PW" | arch-chroot /mnt chpasswd +# Put the user in the docker group at install time (effective from first login), +# so the first-run Once install sees a usable daemon (docker info succeeds) and +# once/once-add work without sudo. groupadd -f is idempotent if the docker +# package already created the group. +arch-chroot /mnt groupadd -f docker +arch-chroot /mnt usermod -aG docker "$USERNAME" # ----------------------------------------------------------- ssh handoff ---- if [ -f /root/master.pub ]; then @@ -375,7 +388,7 @@ clear cat <<'BANNER' ============================================================ - === Welcome to OMATERM + Once === + === Welcome to omalocal === ============================================================ This is a fresh Arch Linux server installed by omalocal. @@ -393,20 +406,20 @@ cat <<'BANNER' and messaging (the firewall already opens 53317). Installers (run any of these later if you skip): - curl -fsSL https://omaterm.org/install | bash + curl -fsSL https://raw.githubusercontent.com/omacom-io/omaterm/refs/heads/master/install-native.sh | bash curl https://get.once.com | ONCE_INTERACTIVE=false sh curl -fsSL https://raw.githubusercontent.com/28allday/omarchy-send/main/install.sh | bash BANNER setup_server=0 -read -rp " Set up this server (OMATERM + Once) now? [Y/n] " ans /dev/tty 2>&1 +# and THAT `/dev/tty` is what breaks tmux here. With the final shell's +# fds opened on /dev/tty, tmux's client reports its terminal name (ttyname of +# STDIN, client.c) as the literal "/dev/tty" — and tmux's server_client_open +# (server-client.c) explicitly refuses a client whose ttyname is "/dev/tty", +# dying with "open terminal failed: can't use /dev/tty". (script(1)'s fresh PTY +# did NOT help: OMATERM re-redirects to /dev/tty *after* it, re-poisoning the +# name.) A relog works because the login shell's fds are a real /dev/pts/N, whose +# ttyname tmux accepts. +# +# Fix: strip that redirect from the downloaded installer so its closing +# `exec bash -l` inherits OUR fds instead — which are the login shell's real pts +# (this firstrun runs straight off .bash_profile with the pts on 0/1/2, and we do +# NOT redirect below). ttyname then resolves to /dev/pts/N and tmux starts cleanly. +# The sed is narrow and forward-compatible: if OMATERM ever drops the redirect +# itself, it no-ops and the inherited-pts behaviour still holds. if [ "$setup_server" = 1 ]; then echo echo " [2/2] Installing OMATERM (toolkit + AI agents: claude-code, codex," echo " opencode, gemini)... when it finishes you'll be dropped straight" echo " into your new shell." echo - curl -fsSL https://omaterm.org/install | bash || { - echo " OMATERM install did not complete — retry later with:" - echo " curl -fsSL https://omaterm.org/install | bash" - } + omaterm_installer="$(mktemp)" + if curl -fsSL https://raw.githubusercontent.com/omacom-io/omaterm/refs/heads/master/install-native.sh -o "$omaterm_installer"; then + # Neutralise the /dev/tty redirect on OMATERM's final `exec bash -l`. + sed -i 's@exec bash -l /dev/tty 2>&1@exec bash -l@' "$omaterm_installer" + # No .local` (try `ssh @.local`). The `once-mdns-sync.service` daemon (`/usr/local/bin/once-mdns-sync`) auto-publishes an mDNS alias for every single-label `*.local` host Once is serving — so apps deployed via `once-add` (as `.local`) are reachable LAN-wide. See the "Deploying a Once app reachable on the LAN" recipe for the deploy side. @@ -664,7 +717,7 @@ Installed by `omalocal.sh`, a single-script patcher that takes a stock Arch ISO On the user's first interactive login a self-removing `~/.bash_profile` hook offers to set up the server. Once is installed first; OMATERM is installed last because its installer ends with `exec bash -l` and drops the user straight into their new shell: - **Once** (https://once.com) — `curl https://get.once.com | ONCE_INTERACTIVE=false sh` — Basecamp's self-hosted app deployment platform. -- **Omaterm** (https://omaterm.org) — `curl -fsSL https://omaterm.org/install | bash` — terminal-first toolkit: starship, neovim, tmux, mise, docker, lazygit, yay, plus AI agents (claude-code, codex, opencode, gemini), etc. OMATERM installs the AI agents itself (they're in its `mise.packages`), so there's no separate agent step. +- **Omaterm** (https://omaterm.org) — `curl -fsSL https://raw.githubusercontent.com/omacom-io/omaterm/refs/heads/master/install-native.sh | bash` — terminal-first toolkit: starship, neovim, tmux, mise, docker, lazygit, yay, plus AI agents (claude-code, codex, opencode, gemini), etc. OMATERM installs the AI agents itself (they're in its `mise.packages`), so there's no separate agent step. We call `install-native.sh` directly because `omaterm.org/install` is now a dispatcher that interactively prompts native-vs-Docker; the native branch is what a headless Arch box wants. If the user declined, install them manually with those same two commands. @@ -1166,7 +1219,12 @@ cat > "/mnt/home/$USERNAME/.config/omarchy-send/config.json" <<'OMS_CONFIG' } OMS_CONFIG arch-chroot /mnt chmod 600 "/home/$USERNAME/.config/omarchy-send/config.json" -arch-chroot /mnt chown -R "$USERNAME:$USERNAME" "/home/$USERNAME/.local" "/home/$USERNAME/.config/omarchy-send" +# chown the WHOLE ~/.config (not just .config/omarchy-send): the host-side +# `install -d` above created the ~/.config PARENT as root, and a -R chown of the +# omarchy-send subdir alone leaves ~/.config itself root-owned. That blocks the +# user from creating anything else under ~/.config later — e.g. OMATERM's +# LazyVim step (`git clone ~/.config/nvim`) fails with "Permission denied". +arch-chroot /mnt chown -R "$USERNAME:$USERNAME" "/home/$USERNAME/.local" "/home/$USERNAME/.config" echo "==> Writing pre-login console banner (hostname + IPv4 + ssh hint)..." # agetty expands these escapes when it prints /etc/issue: @@ -1237,9 +1295,9 @@ echo echo " Then from your master machine:" echo " ssh $USERNAME@ (or ssh $USERNAME@$HOSTNAME)" echo -echo " On your first login you'll be offered OMATERM + Once. To set" +echo " On your first login you'll be offered the omalocal setup. To set" echo " them up manually at any time:" -echo " curl -fsSL https://omaterm.org/install | bash" +echo " curl -fsSL https://raw.githubusercontent.com/omacom-io/omaterm/refs/heads/master/install-native.sh | bash" echo " curl https://get.once.com | ONCE_INTERACTIVE=false sh" echo "==================================================================" echo