omalocal: track upstream OMATERM installer, fix first-login tmux + Once
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 >/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) <noreply@anthropic.com>
This commit is contained in:
parent
b1af2fe02d
commit
023b9394d3
2 changed files with 76 additions and 17 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -6,3 +6,4 @@ omaterm-arch-*.iso
|
|||
*.ppm
|
||||
*.png
|
||||
build.log
|
||||
build-*.log
|
||||
|
|
|
|||
92
omalocal.sh
92
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 <<LOADER
|
||||
default arch.conf
|
||||
|
|
@ -300,6 +307,12 @@ HOSTS
|
|||
echo "==> 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
|
||||
read -rp " Set up this server (omalocal) now? [Y/n] " ans </dev/tty
|
||||
case "$ans" in
|
||||
[Nn]*)
|
||||
echo
|
||||
echo " Skipped. To set up later, run:"
|
||||
echo " curl https://get.once.com | ONCE_INTERACTIVE=false sh"
|
||||
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
|
||||
;;
|
||||
*)
|
||||
|
|
@ -415,6 +428,9 @@ case "$ans" in
|
|||
# because its installer finishes with `exec bash -l`, hijacking this shell
|
||||
# and dropping the user straight into their new tmux session — nothing after
|
||||
# that call would ever run. So Once (and Omarchy-Send below) go first.
|
||||
# Docker is provisioned in base (pacstrap + enabled in chroot) precisely so
|
||||
# Once — which runs here, before OMATERM — finds a working daemon; Once can't
|
||||
# install Docker itself on Arch (its get.docker.com fallback is unsupported).
|
||||
echo
|
||||
echo " [1/2] Installing Once..."
|
||||
if curl https://get.once.com | ONCE_INTERACTIVE=false sh; then
|
||||
|
|
@ -464,21 +480,58 @@ case "$ans" in
|
|||
;;
|
||||
esac
|
||||
|
||||
# OMATERM goes LAST. Its installer (omaterm.org/install) ends with `exec bash -l`,
|
||||
# which replaces this shell and drops the user into their configured tmux session
|
||||
# — so nothing after this call would run, which is exactly why it's the closer.
|
||||
# OMATERM goes LAST. Its installer ends with `exec bash -l`, which replaces this
|
||||
# shell and drops the user into their configured tmux session — so nothing after
|
||||
# this call would run, which is exactly why it's the closer.
|
||||
# OMATERM now also installs the AI agents itself (node, opencode, claude-code,
|
||||
# codex, gemini are in its mise.packages), so there's no separate agent step.
|
||||
#
|
||||
# We call install-native.sh directly rather than omaterm.org/install: the vanity
|
||||
# URL is now a dispatcher that interactively asks "native or Docker?" — which would
|
||||
# stall this otherwise unattended first-run flow. install-native.sh is the native
|
||||
# branch of that dispatcher, is self-contained (it clones the omaterm repo into its
|
||||
# own tmpdir), and is exactly what a headless Arch box wants. Same URL pattern the
|
||||
# dispatcher itself uses; override the branch with OMATERM_REF if needed.
|
||||
#
|
||||
# OMATERM's installer ends by exec'ing `bash -l`, whose .bashrc auto-starts tmux
|
||||
# (omadots aliases `t`='tmux attach || tmux new -s Work'). Its exact closer is
|
||||
# exec bash -l </dev/tty >/dev/tty 2>&1
|
||||
# and THAT `</dev/tty >/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 >/dev/tty 2>&1@exec bash -l@' "$omaterm_installer"
|
||||
# No </dev/tty here: let OMATERM (and its closing exec) inherit our pts fds.
|
||||
bash "$omaterm_installer" || {
|
||||
echo " OMATERM install did not complete — retry later with:"
|
||||
echo " curl -fsSL https://raw.githubusercontent.com/omacom-io/omaterm/refs/heads/master/install-native.sh | bash"
|
||||
}
|
||||
rm -f "$omaterm_installer"
|
||||
else
|
||||
rm -f "$omaterm_installer"
|
||||
echo " Could not download the OMATERM installer — retry later with:"
|
||||
echo " curl -fsSL https://raw.githubusercontent.com/omacom-io/omaterm/refs/heads/master/install-native.sh | bash"
|
||||
fi
|
||||
fi
|
||||
OMALOCAL_FIRSTRUN
|
||||
|
||||
|
|
@ -653,7 +706,7 @@ description: Context and shortcuts for an Arch Linux server provisioned by omalo
|
|||
|
||||
Installed by `omalocal.sh`, a single-script patcher that takes a stock Arch ISO and produces a headless install. The host was provisioned with:
|
||||
|
||||
- **Base packages**: `base linux linux-firmware intel-ucode amd-ucode networkmanager openssh sudo git curl vim less base-devel ufw avahi nss-mdns kitty-terminfo foot-terminfo ghostty-terminfo rxvt-unicode-terminfo`
|
||||
- **Base packages**: `base linux linux-firmware intel-ucode amd-ucode networkmanager openssh sudo git curl vim less base-devel ufw avahi nss-mdns docker kitty-terminfo foot-terminfo ghostty-terminfo rxvt-unicode-terminfo` (Docker is in base + enabled so the first-run Once install — which runs before OMATERM — has a working daemon; the install user is in the `docker` group).
|
||||
- **SSH**: enabled, **key-only** (`/etc/ssh/sshd_config.d/10-key-only.conf` sets `PasswordAuthentication no`, `KbdInteractiveAuthentication no`, `PermitRootLogin no`). The master machine's public key was baked into `~/.ssh/authorized_keys` at install time.
|
||||
- **Firewall**: ufw enabled with default deny incoming, allow outgoing. Open: `22/tcp` (SSH), `80/tcp` + `443/tcp` (web / Once), `5353/udp` (mDNS), `53317/tcp` + `53317/udp` (LocalSend).
|
||||
- **mDNS**: avahi-daemon enabled and `mdns_minimal` wired into `/etc/nsswitch.conf`, so the box is reachable LAN-wide as `<hostname>.local` (try `ssh <user>@<hostname>.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 `<name>.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@<this-host-ip> (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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue