5SUDO_PASSWORD="__SUDO_PASSWORD__"
6PROJECT_NAME="__PROJECT_NAME__"
7PROJECT_GUEST_DIR="__PROJECT_GUEST_DIR__"
10diag() { echo "[vibebox][diag] $*" >&2; }
12# Extract default route facts (no hardcode)
13default_route_line() { ip -4 route show default 2>/dev/null | head -n 1 || true; }
15 default_route_line | awk '{for(i=1;i<=NF;i++) if($i=="dev"){print $(i+1); exit}}'
18 default_route_line | awk '{for(i=1;i<=NF;i++) if($i=="via"){print $(i+1); exit}}'
22 diag "=== default route ==="
23 default_route_line >&2 || true
25 diag "=== systemctl status ssh ==="
26 systemctl status ssh --no-pager >&2 || true
28 diag "=== journalctl -u ssh (tail) ==="
29 journalctl -u ssh -n 120 --no-pager >&2 || true
31 diag "=== sshd -t ==="
34 diag "=== sshd -T (listen/addressfamily) ==="
35 sshd -T 2>/dev/null | egrep -i '^(listenaddress|addressfamily|port|permitrootlogin|allowusers)\b' >&2 || true
37 diag "=== listeners on :22 ==="
38 ss -lntp 2>/dev/null | awk 'NR==1 || $4 ~ /:22$/' >&2 || true
40 diag "=== ip -br addr ==="
41 ip -br addr >&2 || true
43 diag "=== ip route ==="
46 gw="$(default_gw || true)"
48 diag "=== ping default gateway ($gw) ==="
49 ping -c1 -W1 "$gw" >/dev/null 2>&1 && diag "ping gw OK" || diag "ping gw FAIL"
54TARGET="${PROJECT_GUEST_DIR}/.vibebox"
55if [ -d "$TARGET" ] && ! mountpoint -q "$TARGET"; then
56 mount -t tmpfs tmpfs "$TARGET"
59# 2) user + authorized_keys
60if ! id -u "$SSH_USER" >/dev/null 2>&1; then
61 useradd -m -s /bin/bash -U "$SSH_USER"
62 usermod -aG sudo "$SSH_USER" || true
65if [ -n "$SUDO_PASSWORD" ]; then
66 echo "${SSH_USER}:${SUDO_PASSWORD}" | chpasswd
69install -d -m 700 -o "$SSH_USER" -g "$SSH_USER" "/home/${SSH_USER}/.ssh"
70install -m 600 -o "$SSH_USER" -g "$SSH_USER" "$KEY_PATH" "/home/${SSH_USER}/.ssh/authorized_keys"
72USER_HOME="$(getent passwd "$SSH_USER" | cut -d: -f6 2>/dev/null || true)"
73if [ -z "$USER_HOME" ]; then
74 USER_HOME="/home/${SSH_USER}"
77# Home mount links (config-driven)
80# Vibebox shell commands
81install -d -m 755 /etc/profile.d
82cat > /etc/profile.d/vibebox.sh <<'VIBEBOX_SHELL_EOF'
83__VIBEBOX_SHELL_SCRIPT__
85chmod 644 /etc/profile.d/vibebox.sh
87# Auto-cd into project for interactive shells
88cat > /etc/profile.d/vibebox-project.sh <<'VIBEBOX_PROJECT_EOF'
91 project_home="${HOME}/__PROJECT_NAME__"
92 if [ "$USER" = "__SSH_USER__" ] && [ -d "$project_home" ]; then
94 elif [ "$USER" = "__SSH_USER__" ] && [ -d "__PROJECT_GUEST_DIR__" ]; then
95 cd "__PROJECT_GUEST_DIR__"
100chmod 644 /etc/profile.d/vibebox-project.sh
102if ! grep -q "vibebox-aliases" "${USER_HOME}/.bashrc" 2>/dev/null; then
105 echo "# vibebox-aliases"
106 echo ". /etc/profile.d/vibebox.sh"
107 } >> "${USER_HOME}/.bashrc"
111MISE_BIN="${USER_HOME}/.local/bin/mise"
112mise_warn() { echo "[mise] $*" >&2; }
113mise_ok() { command -v mise >/dev/null 2>&1 || [ -x "$MISE_BIN" ]; }
115 if [ ! -x "$MISE_BIN" ] && ! command -v mise >/dev/null 2>&1; then
116 if ! curl https://mise.run | HOME="$USER_HOME" sh; then
117 mise_warn "mise install script failed"
121 echo 'eval "$(~/.local/bin/mise activate bash)"' >> "${USER_HOME}/.bashrc"
123 export PATH="${USER_HOME}/.local/bin:/usr/local/bin:$PATH"
125 mkdir -p "${USER_HOME}/.config/mise"
127 cat > "${USER_HOME}/.config/mise/config.toml" <<MISE
129 # Always use the venv created by uv, if available in directory
130 python.uv_venv_auto = true
132 idiomatic_version_file_enable_tools = ["rust"]
137 "npm:@openai/codex" = "latest"
138 "npm:@anthropic-ai/claude-code" = "latest"
141 touch "${USER_HOME}/.config/mise/mise.lock"
142 if [ -x "$MISE_BIN" ]; then
143 if ! HOME="$USER_HOME" "$MISE_BIN" install; then
144 mise_warn "mise install failed"
148 if ! HOME="$USER_HOME" mise install; then
149 mise_warn "mise install failed"
155if ! mise_install; then
156 diag "mise installation failed"
157 vibebox_fail "mise installation failed" 1
160# 3) start ssh (don't swallow failures)
161# If ssh is already active, don't force start/restart.
162if ! systemctl is-active --quiet ssh; then
163 if ! systemctl start ssh; then
164 diag "systemctl start ssh failed"
166 vibebox_fail "failed to start ssh service" 1
170# 4) obtain stable IPv4 on the default-route interface (wait up to ~30s)
173 ip -4 -o addr show dev "$dev" scope global 2>/dev/null \
174 | awk '{print $4}' | cut -d/ -f1 | head -n 1 || true
181while [ "$t" -lt 60 ]; do
182 dev="$(default_dev || true)"
183 gw="$(default_gw || true)"
184 if [ -n "$dev" ]; then
185 ip="$(ip_on_dev "$dev")"
190 if [ -n "$dev" ] && [ -n "$ip" ]; then
191 # optional: if a gateway exists, require it to answer to avoid "ip exists but link dead"
192 if [ -z "$gw" ] || ping -c1 -W1 "$gw" >/dev/null 2>&1; then
201if [ -z "$dev" ] || [ -z "$ip" ]; then
202 diag "no stable IPv4 on default route interface"
204 vibebox_fail "no stable ipv4 route on default interface" 1
207# 5) strong verify: ssh must listen externally (0.0.0.0:22 or $ip:22 or [::]:22)
209 ss -lnt 2>/dev/null \
210 | awk 'NR>1 {print $4}' \
211 | grep -Eq "^(0\.0\.0\.0:22|\\[::\\]:22|${ip}:22)$"
215while ! listens_ok && [ "$i" -lt 80 ]; do # ~8s
221 diag "sshd not listening on 0.0.0.0:22 / ${ip}:22"
223 vibebox_fail "sshd is not listening on the expected address" 1
228curl -s https://api.ipify.org ; echo
232echo VIBEBOX_SSH_READY
233echo "VIBEBOX_IPV4=$ip"
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list