haoshoku
v12.5.2
Published
<p align="center"> <img src="icons/haoshoku-readme.gif" alt="Haoshoku" width="100%"> </p>
Readme
Haoshoku: Color of the Supreme King
Haoshoku is a personal, modular setup toolkit for Arch-family desktops and Debian servers. Its desktop path is designed around Omarchy: Haoshoku installs applications and portable developer configuration while Omarchy remains the owner of the desktop experience. The desktop path requires Omarchy 4 (Quattro) or newer; Omarchy 3 is no longer supported.
Install
On Linux x64 or arm64, install the compiled CLI and its runtime assets:
curl -fsSL https://axatbhardwaj.xyz/haoshoku | bash
haoshoku --os archBun is not required to install or run this CLI. The installer needs Bash,
curl, tar, and standard Linux coreutils. It stores each version beside
~/.local/share/haoshoku, swaps that symlink atomically on updates, and links
~/.local/bin/haoshoku. Add ~/.local/bin to PATH if the installer warns.
Run the same one-liner again to update. Individual setup commands may install
tools that have their own runtime requirements, including Bun for Axstack.
Set HAOSHOKU_HOME to override the install location. For offline installation,
use HAOSHOKU_TARBALL=file:///absolute/path/haoshoku-linux-x64.tar.gz bash install.sh.
HAOSHOKU_BASE_URL overrides the release download directory.
For development, clone the repository and use Bun:
git clone https://github.com/axatbhardwaj/haoshoku.git
cd haoshoku
bun install
bun link
haoshoku --os archbun haoshoku.js --os arch works without creating a global link. The legacy
--os cachyos spelling is accepted with a deprecation warning.
Install the latest Axstack release from npm, verified against its registry
SHA-512 integrity, and configure its Claude and Codex harness targets with
haoshoku --axstack. Axstack requires Bun >= 1.3.14 and
t3 stable >= 0.1.0 on PATH. Haoshoku enforces that floor independently of
Axstack. On Arch/Omarchy, desktop and iobox setup explicitly selects
aur/t3code-bin, so an older Omarchy repository package cannot win the lookup.
An already installed desktop below 0.1.0, prerelease or unknown version is held;
Haoshoku prints yay -S aur/t3code-bin (or the selected helper) for a backed-up,
operator-controlled replacement. Unrelated packages continue.
The stable desktop package can ship only t3code; setup does not assume it also
provides /usr/bin/t3. Axstack harness installation reuses an already runnable
stable CLI >=0.1.0 on the caller's PATH, including unmanaged and fleet archive
CLIs, without a T3 registry lookup or Node/npm prerequisite checks. When CLI
preparation is needed, Haoshoku installs supported t3@latest under ~/.local
or reuses a genuinely available stable package-owned CLI. Standalone
--tailscale-t3 retains its strict origin/version/runtime guards. Arch lists
include nodejs and npm; the selected Node runtime must meet ^22.16, ^23.11 or
=24.10 on the service PATH, including mise shims. Arch never runs the Debian Node bootstrap. A missing or incompatible prerequisite reports incomplete setup.
Only the exact old managed /usr/bin/t3-nightly alias or a verified local npm
install can migrate after origin/version checks. npm replacement is staged and
verified before switching the alias; a failed installation preserves it.
Unmanaged executables, custom links and fleet archive aliases remain unchanged;
CLI preparation and service migration hold them until explicitly selected by
the operator. Unknown/newer CLI
versions and unsupported runtimes hold migration, and installing stable over an existing nightly
package or unmanaged desktop remains a bounded T3 package hold. Desktop Local
environment, single-server preflight and provider settings retain their existing
policy; no theme synchronization is added.
Run haoshoku --axstack-check to report the shim/version, each harness check,
and roles.json presence at ~/.claude/skills/axstack/roles.json and
~/.agents/skills/axstack/roles.json separately. The role-file readback
does not affect check success.
Claude and Codex setup skip config synchronization when their CLI installation fails.
Arch and Omarchy behavior
The Arch setup:
- skips git configuration on Omarchy, which sets your identity at install,
leaving existing
~/.gitconfigand git profile files untouched; other Arch hosts still get the "Configure git?" prompt; - authenticates sudo once up front and keeps that authorization alive with silent, non-interactive refreshes until setup finishes or aborts. Later Haoshoku sudo calls are non-interactive and fail instead of prompting again;
- performs a full system upgrade before package installation through
omarchy update -yon Omarchy orpacman -Syuon other Arch-family systems, aborting setup if that preflight fails; - prefers Omarchy's
yay, falls back toparu, and bootstraps an AUR helper only when neither is available; - uses
pacmanfor repository packages and the AUR helper only for AUR packages; - batches repository and AUR packages, filters missing targets, and retries only still-uninstalled packages individually when a batch fails;
- installs only JetBrains Mono Nerd Font when neither its full nor Omarchy's
ttf-jetbrains-mono-nerd-basicpackage is installed; keeps either existing package; - binds
Super+Tto launch or focus T3 Code stable witht3code, matching only the anchored^com\.t3tools\.T3Code$window class; - installs Tailscale, enables
tailscaled.service, and configures the T3 user service for tailnet HTTPS phone access after T3 is installed. Logged-out nodes print a browser login URL and wait; logged-in nodes skip login. Matching service drop-ins are left untouched. The single-backend preflight must pass before T3 service changes. Failures warn and setup continues; - prints the verified HTTPS URL and
t3 pair --tailscalephone pairing hint. Rerun this step alone withhaoshoku --tailscale-t3on Arch. System changes usesudo -n; standalone runs require an existing sudo authorization (sudo -v). Tailnet HTTPS certificates must be enabled; - keeps Bash as the account shell and adds portable aliases and tool
initialization through
~/.config/haoshoku/bashrc; - preserves Omarchy's
.bashrc, lock screen, and core Quickshell/Hyprland configuration. It asks Omarchy to apply the pinned Aurora theme, selected background, and font fromconfigs/omarchy/appearance.json; it does not copy generatedcurrent/themestate between machines. Displaced Omarchy keybindings are relocated or explicitly superseded and documented inconfigs/omarchy/keybinding-swaps.json, the canonical swap record; - deploys
~/.config/hypr/haoshoku/{bindings,workspaces}.luaand appends exactly tworequirelines to~/.config/hypr/hyprland.lua; Omarchy 4 loads user config viarequire()and no longer sources.conffiles; - selects
pc,laptoporioboxin order: explicit flag, stored choice, hostname inconfigs/fleet.json, Linux DMI/battery detection, then prompt; saves the selected type to~/.haoshoku.jsonbefore device-routed setup. A stored choice that disagrees with the fleet hostname warns and stays unchanged;haoshoku --device-type pc|laptop|ioboxis the explicit override. Skip persists nothing and leaves device-specific audio unset; - without interactive confirmation—including piped stdin—Haoshoku declines real user decisions immediately and does not treat input as answers;
- adds a device-routed behavior-only Lua workspace overlay. The hyprmoncfg
plugin owns the generated
~/.config/hypr/monitors.lua; Haoshoku owns only~/.config/hyprmoncfg/profiles/*.json, the source profile JSON that hyprmoncfg reads to generate that Lua file, and never writesmonitors.luadirectly. Monitor-bound workspace rules live in the hyprmoncfg PC profile, matched by hardware identity instead of connector name so they survive DP connector swaps; laptop workspace rules are monitor-independent and remain in the Lua overlay; - adds two-key special-workspace toggles under
Super: A Haki (the tagged Warphakitab), I AI assistants (Claude Desktop and Codex Desktop) on ordinary workspace 1, M music, O 1Password, G communication, B Flux Brave Origin, D DeFi Brave Origin, S Steam, andSuper+Shift+XX (Super+Shift+Sstashes the focused window,Super+Alt+Stoggles the stash workspace); see the canonical swaps JSON above for every Omarchy default relocated or superseded to make room; - starts Steam silently in special:steam at login and binds
Super+2to Omakade on demand, keepingSuper+Shift+Gas the gaming-workspace toggle that ensures Omakade. Steam login autostart is configurable (~/.config/haoshoku/gaming.jsondefaults to Steam on, Omakade off):haoshoku --gaming-steam-autostart enabled|disabledandhaoshoku --gaming-omakade-autostart enabled|disabled. Usehaoshoku-gaming-workspace place -- %command%as a Steam launch option to move the launched game's process-tree windows there. The same wrapper lifts the kernel split-lock penalty while the game runs and restores it on exit oncehaoshoku --gaming-split-lockhas installed its narrow sudoers rule (Division 2 otherwise sits near 50 fps with an idle CPU and GPU); - starts Flux, DeFi, WhatsApp, and Notion with empty Brave Origin profiles
below
~/.config/brave-haoshoku/; existing Chromium profile data remains untouched at~/.config/chromium-haoshoku/for manual import; - installs packaged Omazed and safely points Zed at its generated theme so Zed follows the active Omarchy palette. Haoshoku never runs Omazed's manual installer or deletes unrelated Zed themes;
- adds a non-locking
Screens Offcommand to every paired KDE Connect device. It turns off all Hyprland displays through DPMS; keyboard or mouse input, or an existing phone-side Wake command, turns them back on. Pair new phones first, then runhaoshoku --kde-connect-commandsto add the command; - continues after individual optional application failures and reports them.
Haoshoku deliberately does not configure Fish, KDE Plasma, KWin, SDDM, or arbitrary application themes. Omarchy remains responsible for generating and applying all files downstream of the declared appearance.
Omarchy appearance
configs/omarchy/appearance.json declares a
public theme repository at an immutable Git commit, one background filename
inside that theme, and the font family. Full Arch setup and
haoshoku --omarchy-appearance reconcile the theme under
~/.config/omarchy/themes/, then call Omarchy's own theme, background, and font
commands. haoshoku --discord-theme then deploys that theme's Vencord CSS from
configs/discord/theme.json into Vesktop and Vencord, setting enabledThemes
while preserving every other settings key.
An existing checkout from another repository is never replaced. A matching checkout with local changes is preserved and applied as-is; a clean matching checkout advances to the declared commit. The manifest may identify exact legacy revisions from older local-origin installs. Those recognized checkouts are moved to a timestamped sibling backup before Haoshoku installs the clean pinned theme, and a failed install restores the original checkout. This keeps custom work recoverable while making a fresh laptop reproduce the tracked appearance.
Omarchy plugins
On Arch-family desktops, Haoshoku installs the plugins listed in
common/omarchy-plugins.json by running
omarchy plugin add <url> --enable --yes for each one. Each repository is
cloned and enabled at its current default branch HEAD — there is no commit
or tag pinning. An entry may declare disableOnInstall to switch off a stock
widget it replaces. That list is applied only when Haoshoku first creates the
plugin and is never re-applied, so a user can re-enable a displaced widget
without a later Haoshoku run overriding the choice. Every run otherwise
reconciles manifest plugins back to installed and enabled. If
omarchy plugin list --json cannot provide a trustworthy snapshot, the helper
performs no plugin work and returns snapshotUnavailable: true; the
side-effect-free manual-auth checklist is still printed.
The manifest includes the Speech Orb dictation HUD. Setup turns off voxtype's
own OSD (voxtype config set osd.enabled false) and restarts a running
voxtype.service, so only the orb appears while you dictate.
These plugins run as arbitrary, unsandboxed code inside the long-lived
omarchy-shell process — the same risk Omarchy's own CLI warns about when
adding plugins manually. The --yes flag is passed deliberately so setup
stays unattended and non-interactive; this also suppresses Omarchy's
per-plugin confirmation/warning prompt, so only add this manifest to
machines where you trust and have reviewed those repositories. Plugins
that need manual setup afterwards (API tokens, OAuth, device pairing) are
printed as a manual-auth checklist after installation.
The Galaxy Buds plugin is installed from aislandener/galaxy-buds-control and
reports Bluetooth pairing in that checklist; it requires no credentials.
Pullbar (io.github.ciryon.pullbar) replaces both the former GitHub widget
(robzolkos.github) and Agent Usage Plus on the right side of the bar. The full Arch setup deploys its required
~/.local/bin/omarchy-agent-usage-update wrapper before installing plugins. If
running only selected steps, run haoshoku --scripts before
haoshoku --omarchy-plugins. That wrapper also scopes the Omarchy 4.0.0 Codex
collector's retired -a untrusted compatibility translation to usage refreshes;
ordinary Codex commands still execute the user's normal binary unchanged.
Omarchy bar
Use haoshoku --omarchy-bar to deploy configs/omarchy/bar.json, and use
haoshoku --omarchy-bar-backup to capture the live bar back into the repo.
The same commands deploy and back up Haoshoku's bundled xzat.tray plugin at
~/.config/omarchy/plugins/xzat.tray. Its overflow drawer combines status
notifier items with Galaxy Buds, hyprmoncfg, Display, and No Sleep controls.
The full Arch setup restores the plugin and layout automatically; no separate
clone step is needed. Feishin and Omarchy's generic MPRIS widget remain in the
left section, while Pullbar and the stock Agents widget stay visible on the right.
Haoshoku claims the bar key of ~/.config/omarchy/shell.json wholesale,
including bar-widget enablement. Disabling a bar widget through Omarchy's UI is
therefore reverted on the next deploy. Every other top-level key — including
idle, plugins, disabledPlugins, version, and unknown keys — is preserved.
Agent and orchestration policy
Haoshoku deploys the compact Claude/Codex instructions. Routing and review workflow policy live in Axstack. Independent skills are managed separately. Back up live instruction edits with:
haoshoku --claude-backup
haoshoku --codex-backupThe shared profile lives at configs/agent-profile/PROFILE.md. Apply it to
Claude, Codex, Opencode, and Antigravity with haoshoku --agents, or capture
the live Claude copy with haoshoku --agents-backup. The bundled policy routes
both Notion accounts and Linear through the configured Executor MCP while T3 Code
remains responsible for orchestration. Profile sync preserves the live Axstack-owned
routing block; shared, Claude and Codex profile backups strip it so Axstack can
own it on fresh hosts. The shared profile names axnet roles: io is the control
center, iobook and the phone are access points, and iobox and axat-vps are
workers that do the work locally and never hand work to another host. T3
subagents and reviewers on the same host are fine.
T3 Code owns agent orchestration, and Axstack supplies routing policy.
Claude/Codex runtime state and settings.json remain machine-local.
Current reconciliation and authority
The approved setup reconciliation r2 and Claude/Codex addendum remain the implementation baseline for #63 and #65/#66. The subsequent instruction to complete the work and release authorizes the T3 driver to complete reviewed merges and the GitHub, npm and Linux binary release after T1–T7. It supersedes the historical human-only merge and no-release gates in dated specs and plans. Their original bodies and approvals remain historical. Release authorization does not authorize installation, migration or configuration on live hosts. See the runbook's current gate.
T1–T7 are merged for 12.3.0. Repository tests and package checks establish source behavior. They do not prove publication, live Debian compatibility, desktop pairing, Executor authentication or future T3 session inheritance. The 12.3.0 changelog records merged changes; the driver verifies tag, npm and Linux binary publication separately. Live installation and migration remain deferred.
Existing-host migration
Haoshoku no longer installs, configures, or launches Paseo. Existing hosts keep these artifacts until you remove them manually:
paseo-daemon.serviceand~/.paseo;- the
@getpaseo/cliinstall and thepaseo-bindesktop package; - upstream Paseo skills;
~/.hermes/plugins/paseo-review-relayand its data at~/.hermes/plugin-data/paseo-review-relay;~/.local/bin/hermes-relay;~/.config/haoshoku/{hermes-relay,paseo-tasks,paseo-schedules}.json.
Retiring these host artifacts needs a separate manual migration after checking active consumers and preserving private recovery data. Normal and repeated setup leaves existing skill directories, managed links, and theme preferences untouched. Any existing-host skill migration is manual and separately scoped.
Retired skill commands
Haoshoku no longer installs Matt Pocock skills or visual-explainer, and no
longer provides skill inventory, agent-skill sync, or theme configuration.
--skills, --skills-update, --skills-list, --agent-skills, and
--explainer-theme <theme> exit nonzero with guidance before setup runs.
The whole legacy --agent-skills route is retired, including archive/unlink
and sync behavior. Boolean, value-bearing --flag=value, malformed and combined
retired forms are refused before logging or setup; they cannot fall through to
full OS setup. Manage independent skills separately; use haoshoku --axstack
for Axstack workflows. Existing user-owned skills and configuration remain in place.
Retired service and watcher commands
Claude Remote Control, Claude stay-awake, and PR watch are retired from future
Arch and Debian setup. --claude-remote-control,
--claude-remote-control-backup, --claude-stay-awake,
--claude-stay-awake-backup, --pr-watch, and --pr-watch-backup exit nonzero
with guidance before logging or setup runs. Manage existing services and
watchers separately; normal and repeated setup leaves their scripts, units,
enablement links, state, and Claude acceptance settings untouched.
Haki launcher
On Omarchy, haoshoku-special-workspace haki opens the tagged Ghostty haki
split on its special workspace, with Claude above a fresh Codex pane below;
it has no default keybinding. KDE uses its own Warp agents route.
Set claudeSessionName in
~/.haoshoku.json only to resume a named Haki Claude session. A missing or null
value starts plain Claude; a
syntactically invalid value is preserved, reported, and ignored. A valid name is
passed as one literal argument to claude -r, but it resumes directly only when
the name resolves uniquely; otherwise Claude may open its picker. This Haki
launcher uses local Claude/Codex sessions without the retired Remote Control
services.
Gaming
Accepting the gaming prompt installs a portable Arch gaming base:
- Steam
- GameMode and its 32-bit library
- Gamescope
- MangoHud and its 32-bit library
- ProtonUp-RS
On Omarchy, Haoshoku also invokes Omarchy's GPU-aware helper for the correct 32-bit Vulkan/NVIDIA libraries. Other Arch distributions are not given guessed GPU packages.
Bash additions
The managed fragment exposes both package-installed Bun and a user installation
under ~/.bun/bin, and provides guarded initialization for Starship, direnv,
zoxide, thefuck, pyenv, and Conda, plus the aliases formerly kept in the Fish
configuration. Machine-local secrets can be stored in
~/.config/haoshoku/secrets.bash; that file is never copied into this repo.
One-shot configuration
haoshoku --claude
haoshoku --claude-backup
haoshoku --claude-update
haoshoku --codex
haoshoku --codex-backup
haoshoku --server-t3-code
haoshoku --server-executor https://axat-vps.tail140c22.ts.net
# With EXECUTOR_AUTHORIZATION supplied securely in the environment:
haoshoku --executor-clients https://executor.example.net/mcp
haoshoku --device-type laptop
haoshoku --device-type iobox
haoshoku --fleet-ssh
haoshoku --agent-accounts
haoshoku --kde-connect-commands
haoshoku --audio
haoshoku --audio-backup
haoshoku --mimeapps
haoshoku --mimeapps-backup
haoshoku --gh-stack
haoshoku --worktree-cleanup
haoshoku --workspaces
haoshoku --gaming
haoshoku --gaming-split-lock
haoshoku --gaming-steam-autostart disabled
haoshoku --gaming-omakade-autostart enabled
haoshoku --monitors
haoshoku --hyprmoncfg-backup
haoshoku --omarchy-plugins
haoshoku --omarchy-bar
haoshoku --omarchy-bar-backup
haoshoku --omarchy-appearance
haoshoku --discord-theme
haoshoku --3-4-migrateOn Omarchy, the primary desktop app starts on workspace 1. Meta+1 brings its
window to workspace 1 and focuses it; Meta+6 brings it to workspace 6 and
focuses it. If the app is closed, either key launches it before placing it on
the selected workspace. The default is T3 Code stable (t3code).
~/.config/haoshoku/primary-app holds the executable on line 1 and an optional
window class on line 2; the managed T3 default uses com.t3tools.T3Code for
reliable window matching. Without line 2, the desktop entry's
StartupWMClass identifies the window. Haoshoku creates this file on the first
--workspaces setup and migrates the exact old two-line nightly default
(t3code-nightly / com.t3tools.T3Code) or retired Orca default. Custom
executables and custom class overrides stay unchanged; reruns keep stable.
Run hyprctl reload after editing it.
Use haoshoku --device-type pc, laptop or iobox to override automatic detection.
Later full Arch-family setups honor that stored explicit value.
haoshoku --3-4-migrate is a re-runnable, idempotent migration from an
Omarchy 3 layout to Omarchy 4: it strips dead source = lines, removes
orphaned overlay .conf files, repoints theme paths, backs up and clears
Omarchy's stock monitors.lua, deploys the Lua overlay and hyprmoncfg
profiles, installs plugins, and validates — and if Omarchy's legacy config
shim is still present it reports validation deferred and asks for a reboot
and re-run instead of claiming success.
Run haoshoku --help for the complete current list.
iobox profile and fleet
The axnet fleet in configs/fleet.json is io (PC, control),
iobook (laptop, access), iobox (always-on Omarchy agent box running T3, worker),
and axat-vps (Debian VPS running Executor and Hermes, worker). Everything is reached over Tailscale only.
Use MagicDNS short names (ssh io, ssh iobook, ssh iobox, ssh axat-vps);
Arch hosts use Tailscale SSH, and axat-vps keeps OpenSSH. Agents may SSH to any
fleet host and act there. Accepted risk: one compromised agent reaches every host.
--device-type iobox stores the profile; a later full setup uses
common/paru_applist_iobox.txt, including T3,
Tailscale, Claude/Codex dependencies, Git/gh and 1Password desktop plus op CLI.
It keeps Axstack, gh-stack, shared agent instructions, user scripts and Omarchy
appearance. It skips Flatpaks, gaming/Steam, uosc/MPV, the Bluetooth prompt,
Brave/Chromium profiles, KDE Connect, voxtype, bar/plugins, workspaces,
hyprmoncfg and audio. Standalone --workspaces, --monitors,
--hyprmoncfg-backup, --audio and --audio-backup refuse iobox. Reruns keep
previously installed apps; switching profiles does not uninstall anything.
The Arch T3 step enables and verifies user linger. On iobox it also masks and
verifies sleep.target, suspend.target, hibernate.target,
hybrid-sleep.target and suspend-then-hibernate.target. T3, Tailscale, linger
or sleep-mask failures stop iobox setup with the failed step named.
--fleet-ssh runs only fleet SSH setup on an Arch fleet host; Debian and
non-fleet hosts are refused without changes. Tailscale must report BackendState
Running and MagicDNSSuffix matching the manifest tailnet (tail140c22.ts.net)
before fleet actions. Otherwise standalone mode exits non-zero without writing
files; full Arch setup skips fleet SSH with a message.
It enables/verifies Tailscale SSH, writes ~/.ssh/config.d/haoshoku-fleet, and
puts its Include first in ~/.ssh/config, preserving user Host/Match blocks.
Arch peer keys from Tailscale go to ~/.ssh/known_hosts_fleet with strict host
checking; offline peers retain their cached keys. VPS connections use
~/.ssh/id_ed25519_vps and accept-new, recording the first key in ordinary
known_hosts. Haoshoku does not create SSH keys, authorize them or edit ACLs.
--agent-accounts is opt-in elsewhere and runs by default on iobox after
Claude/Codex and shared-profile setup. It creates ~/.claude-alt and
~/.codex-alt, sharing every other primary-home top-level entry by symlink
except these private entries:
| Home | Private entries (never linked) |
| --- | --- |
| Claude | .credentials.json, .claude.json, history.jsonl, sessions, session-env, shell-snapshots, cache, backups, security, mcp-needs-auth-cache.json, .last-cleanup |
| Codex | auth.json, config.toml, models_cache.json, log, memories, tmp |
Codex config.toml is copied once when the alt file is missing. Other runtime
state stays shared, including Claude projects and Codex history.jsonl,
sessions and SQLite files. Existing real entries and wrong/dangling links
are reported and preserved; private symlinks report incomplete account isolation.
Missing primary homes are skipped. Logins and T3 provider settings remain manual.
Bring-up checklist
Run each host's setup locally. Until 12.4.0 ships, use a git checkout with Bun for the new commands below; the installed 12.3.x package does not include them.
Set the hostname to
iobox(sudo hostnamectl set-hostname iobox).Install Bun (
yay -S --needed bun-bin; Bun >= 1.3.14), thengit clone https://github.com/axatbhardwaj/Haoshoku.gitandcd Haoshoku.Install T3 desktop (
yay -S aur/t3code-bin; verify the proposed stable version is >=0.1.0), open it once witht3code, choose the Stable update channel, and disable Local environment in its settings. This creates the desktop settings file required by preflight; do this before the service step.Install Tailscale (
sudo pacman -S --needed tailscale), enable it withsudo systemctl enable --now tailscaled.service, then runsudo tailscale upand finish tailnet login. Haoshoku verifies it is installed, enabled and logged in.In the checkout, run
bun install,bun haoshoku.js --device-type iobox, thenbun haoshoku.jsas the ordinary setup user. If the T3 step stops, fix the reported condition (including desktop settings), then rerunbun haoshoku.js.Complete two Claude paste-code logins: launch
claude, use/login, then repeat withCLAUDE_CONFIG_DIR=~/.claude-alt claude.Complete two Codex device logins:
codex login --device-auth, thenCODEX_HOME=~/.codex-alt codex login --device-auth. After completing the primary logins, rerunbun haoshoku.js --axstackif the first setup reported incomplete Axstack harness setup.Supply
EXECUTOR_AUTHORIZATIONsecurely as described in Executor clients, then register once per home:bun haoshoku.js --executor-clients https://axat-vps.tail140c22.ts.net/mcp CLAUDE_CONFIG_DIR=~/.claude-alt CODEX_HOME=~/.codex-alt bun haoshoku.js --executor-clients https://axat-vps.tail140c22.ts.net/mcpIn T3, manually map provider instance
claudeAlttoCLAUDE_CONFIG_DIR=~/.claude-altandcodexAlttoCODEX_HOME=~/.codex-alt(use absolute home paths in settings).Run
gh auth loginand set your Git name/email; Omarchy skips Git identity setup. Then rerunbun haoshoku.js --gh-stackif the first setup skipped gh-stack. The end-of-run Next steps block lists incomplete gh-stack and Axstack setup, the reason, and the exacthaoshokucommand to rerun.Create the iobox key with
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_vpsand authorize its public key on axat-vps by hand. Keep the private key local.Create
~/.config/op/service-account.envas a regular file owned by your user, mode 0600, containing exactly one non-emptyOP_SERVICE_ACCOUNT_TOKEN=assignment (one trailing newline allowed). Never log or commit the value. Retrybun haoshoku.js --tailscale-t3to install the managedhaoshoku-op.confdrop-in; an absent file only prints guidance. After token rotation or later creation with an existing drop-in, runsystemctl --user restart t3code.Complete T3 pairing using the service's pairing flow.
Ensure tailnet policy allows port 22 and a fleet SSH rule with
action: accept. An overlappingcheckrule wins and breaks unattended SSH; policy edits are manual.Smoke-test
ssh -o BatchMode=yes <peer> truefor each peer (from iobox: io, iobook and axat-vps). Check authenticated Executor tool discovery separately in both primary and alt T3 instances; registration alone does not prove access.
Debian Server
haoshoku --os debian-serverThe Debian path remains deliberately headless. In addition to server hardening,
it installs the portable Claude/Codex policy, Axstack, and Hermes
Telegram transport.
T3 Code is required and runs without a prompt; an incomplete T3 setup fails
Debian setup. haoshoku --server-t3-code runs the same step on its own.
T3 single-backend preflight
Both --tailscale-t3 on Arch and --server-t3-code on Debian check desktop
Local environment before writing T3 service configuration, installing, starting,
or restarting the service. This check also runs on an already configured rerun.
Runtime and CLI installation can precede this guard; refusal does not mean that
no prerequisite work occurred. The intended workstation topology is one service
backend shared by the desktop and phone, with desktop Local environment disabled.
The operator pairs each client separately; source checks do not prove pairing.
Haoshoku reads desktop-settings.json under the effective T3 base directory's
userdata folder, normally ~/.t3/userdata. It checks the service's effective
T3CODE_HOME, pending unit configuration, and desktop installation, unit and
process evidence. Only an explicit JSON localEnvironmentEnabled: false proves
Local environment is disabled. A missing key means enabled. When desktop
settings are missing and a desktop installation, unit or process is detected,
Haoshoku creates them with Local environment off after checking the directories.
You do not need to open the desktop first. Creation is exclusive: an existing
settings file is never overwritten. Headless setups can proceed without settings;
an unused listening port does not prove a headless setup.
Enabled, malformed, unreadable or conflicting directory/settings evidence, and failed probes, return incomplete setup before T3 service changes. Disable Local environment in the T3 Code desktop, then pair the desktop to the existing service. Resolve directory conflicts or unreadable evidence and retry. Haoshoku never rewrites existing desktop settings or reads or changes existing pairing state. Explicitly disabled desktops and headless setups can proceed; service installation/restart uses the checked base directory. Environment-file and unsupported launch-directory overrides require manual reconciliation.
At the end of a successful Arch T3 step on a host in configs/fleet.json,
Haoshoku mints one t3 pair --tailscale link per other fleet client (roles
control or access). Workers do not pair outbound. Each link is labeled with
its target host and expires after 15 minutes; paste it in that client's
Add environment dialog. The local desktop can pair to its own service with
t3 pair --tailscale; the phone pairing hint is also printed. Haoshoku leaves
the encrypted desktop connection catalog unchanged. Link minting requires
Tailscale Running with the fleet's MagicDNSSuffix; a mismatched tailnet or
failed mint produces a warning and does not fail service setup.
Debian tailnet SSH firewall
Debian setup keeps OpenSSH and its existing key/deploy behavior. Before any UFW
change, it requires logged-in, running Tailscale with kernel networking and a
working, UP tailscale0 carrying every reported valid IPv4/IPv6 tailnet address.
Missing addresses, a userspace-only interface or unreadable state fail preflight. Install Tailscale,
log in and verify tailscale status --json and
ip -j address show dev tailscale0, then retry Debian setup.
Setup adds ufw allow in on tailscale0 to any app OpenSSH before changing
defaults, retains HTTP/HTTPS rules, and keeps the Enable UFW now? confirmation.
UFW IPv6 support must already be enabled (IPV6=yes in /etc/default/ufw);
Haoshoku verifies active IPv4 and IPv6 tailnet SSH rules. Active UFW follows the
same checks and ordering without reset. Declining enable on inactive UFW,
skipping or failing configuration, or failing verification makes overall Debian
setup incomplete with retry guidance.
Existing public SSH allow/limit rules are preserved and reported as incomplete
hardening, including IPv6 rules and saved rules on inactive UFW. Confirm working
tailnet access, then separately inspect and migrate/remove broad rules as the
operator before retrying. Named application profiles are resolved through
read-only ufw app info port inspection, so web/mail profiles can coexist with
tailnet SSH. Failed, malformed or unresolved profile inspection remains
incomplete; correct the profile or inspection prerequisite and retry. Haoshoku
does not guess that an unknown profile is safe.
It never enables Tailscale SSH, disables sshd, changes tailnet ACLs, or deletes
existing firewall rules. Custom rules outside UFW's managed rules require a
separate operator audit.
Debian T3 service setup
Before setup, install Tailscale yourself and log in to your tailnet. Confirm
that tailscale status succeeds. Enable HTTPS certificates in the tailnet
admin console. For a non-root service account, Haoshoku sets the Tailscale
operator with sudo -n tailscale set --operator=$USER when unset or different,
then re-reads the preferences to verify it so T3 can manage its Serve route. Your
phone must also be logged in to the same tailnet. Haoshoku does not install
Tailscale or log in for you.
Haoshoku prepares a compatible Node.js runtime and requires stable >= 0.1.0.
It verifies npm's current stable version before accepting an existing CLI.
On Debian, it reuses the supported ~/.local npm installation when stable;
for a missing CLI or a known older managed nightly, it runs
npm --global --prefix ~/.local install t3@latest and verifies the result.
Unmanaged CLI origins, unknown versions, or versions newer than verified stable
hold setup without replacing the executable or downgrading it.
Upstream's stable CLI
derives its default update channel from its installed version; t3 update --channel stable
is the supported explicit channel command. Setup installs the stable npm CLI
and reconciles the service through that CLI rather than invoking an updater or
passing guessed channel flags to service install. Existing newer services,
unknown launcher state, pending updates/restarts and unmanaged server records
hold setup. Inspect CLI (t3 --version) and running-server versions separately
before migration; an HTTPS response alone does not prove the server version.
The desktop has a separate update preference. If it still selects nightly,
setup holds: choose Stable in T3 desktop Settings (stored as latest),
quit/reopen the stable desktop, keep Local environment disabled and retry.
Haoshoku preserves desktop/provider preferences and never changes providers.
Setup reads t3 connect status --json, unlinks enabled Connect exposure,
and confirms it is disabled before changing the service.
Before t3 service install, Haoshoku writes axstack-path.conf and
axstack-tailscale.conf in ~/.config/systemd/user/t3code.service.d/.
They set T3CODE_TAILSCALE_SERVE=true and keep the user's Grok CLI on the
service PATH:
PATH=%h/.local/bin:%h/.bun/bin:%h/.grok/bin:/usr/local/bin:/usr/bin:/binOnly root gets
axstack-sandbox.conf with IS_SANDBOX=1. The service belongs to the account
that runs Haoshoku, including root when root ownership is intentional.
Setup requires an active t3code.service, a tailscale serve status --json
HTTPS mapping to http://127.0.0.1:3773, and a successful response from the
tailnet HTTPS URL. Readiness retries are bounded to 30 attempts with a
five-second HTTPS timeout and two seconds between attempts. Failure reports
what to inspect and returns incomplete setup. T3 stays bound to localhost.
After setup, pair the phone using t3 pair --tailscale. If the CLI was freshly
installed and ~/.local/bin is not on PATH yet, run
~/.local/bin/t3 pair --tailscale. T3 Connect authorization is no longer part
of this flow. If the service has advanced past verified stable, setup holds.
Inspect the CLI, service state and running-server version; do not force a downgrade.
The full Debian path asks about Git and automatic worktree cleanup.
One-time current Omarchy palette in T3 desktop
The importable Aurora snapshot is an unchanged
copy of io's Omarchy-generated ~/.t3/userdata/themes/omarchy.json, read on
2026-10-16. It already uses T3's version-1 custom-theme format; no converter is
needed. To take a newer snapshot, use the current generated file on that desktop.
Manual import and visual verification remain pending. In the stable desktop:
- Keep Local environment disabled and connect to the existing standalone service. Do not start another server for appearance setup.
- Open Settings → Appearance, open the theme import dialog, choose the JSON file above (or paste its contents), and import it. If Aurora is already saved, keep that copy unless you explicitly intend to replace its colors.
- Select Aurora for the desired appearance preview (the snapshot is dark) and confirm the desktop renders it. This is a client-local choice; do not perform these steps in Android or another browser.
Upstream documents custom-theme import and environment-theme limits.
With Local environment disabled, the desktop cannot receive this palette from
its main local environment. The supported importer saves custom themes in
client storage; Haoshoku does not edit Electron storage or pretend that
t3 theme set omarchy imported a theme into this client. Re-running setup does
not import, select or overwrite themes. There is no automatic palette sync,
daemon or change to the current Omarchy theme. Providers and other preferences
stay unchanged. Live import, rendering and fleet/phone verification are separate
rollout checkpoints.
VPS Hermes Telegram transport
haoshoku --server-hermes-relay verifies Hermes Telegram transport on a
Debian-family server. Full Debian setup fails if Hermes readiness is incomplete.
Arch/Omarchy setup never calls it.
An existing usable Hermes installation is kept without upgrading or replacing
it. If Hermes is absent, Haoshoku downloads the official installer and installs
the pinned commit from
configs/hermes-relay/hermes-runtime.json
with --skip-setup --skip-browser --skip-computer-use --non-interactive.
Credentials and gateway setup remain manual. Complete these private steps
without placing secrets in this repository:
- Run
hermes setupso$HERMES_HOME/config.yamlexists (HERMES_HOMEdefaults to~/.hermes). ConfigureTELEGRAM_BOT_TOKENin its.env. - Set exactly one owner in
TELEGRAM_ALLOWED_USERS. Set the private Telegram home channel inTELEGRAM_HOME_CHANNELorplatforms.telegram.home_channel.chat_idinconfig.yamlto that same ID. - Install startup with
hermes gateway install --no-start-now --start-on-login, start or verify the gateway manually, then rerunhaoshoku --server-hermes-relay.
The command succeeds only when Hermes is usable, its config exists, the bot
token is configured, the private home channel resolves, and the gateway reports
running. Missing or unverifiable readiness returns failure with retry guidance.
A busy running gateway passes. Haoshoku never prompts for activation, restarts
the gateway, or deploys or enables a relay plugin.
It preserves existing Hermes config, credentials, plugins, plugin data, and
legacy relay markers. Telegram notifications use hermes send.
Debian Server does not ask for deviceType: that value only selects desktop
audio and Hyprland/Omarchy variants. For the same reason the Debian path does
not deploy audio, browser/MIME integration, the desktop-oriented user-script
bundle, Brave managed policies, Hyprland monitors/workspaces, or Omazed. Those
steps remain on the Arch/Omarchy path instead of being installed onto a
headless server for superficial symmetry.
Logs and troubleshooting
Setup and configuration runs save a private log under
${XDG_STATE_HOME:-~/.local/state}/haoshoku/logs/ (directory 0700, files
0600). The last 20 logs are kept. Logs include device/version metadata,
log.* messages, command exits and durations, and bounded failure output.
Command output is forwarded live while stdin stays inherited for prompts.
Some programs omit colours or progress bars when their output is captured.
Known credential patterns and Tailscale login URLs are redacted before writing.
The final line shows the log path; failures also show their count and names.
If logging is unavailable, Haoshoku warns once and continues.
--help, --version, and --share-log leave existing logs untouched.
Normal runs upload nothing. To share the latest completed run, or a specific log:
haoshoku --share-log
haoshoku --share-log /path/to/run.logHaoshoku tries Taildrop to the online device io first. Override the receiver
with HAOSHOKU_LOG_TARGET=other-device. On io, collect the file with:
tailscale file get ~/DownloadsTell your agent the filename shown by --share-log. If Taildrop is unavailable
and GitHub CLI is authenticated, Haoshoku creates a secret gist and prints its
URL. Otherwise it prints the local path for copying or attaching to your agent
conversation. A failed upload keeps the file and reports failure.
Development
Run bun test from the repository root; the bunfig.toml preload that isolates HOME for tests only applies there.
bun install
bun test
bun run lintOpt-in Executor server
haoshoku --server-executor https://axat-vps.tail140c22.ts.netRun this command as root on Debian with Docker, Docker Compose v2 and ss
already available. Executor is never provisioned by default Arch or Debian
setup. Supply an explicit HTTPS origin (host and optional port, optionally a
trailing /). Credentials, other paths, queries and fragments are rejected.
The origin is omitted from Haoshoku run logs; no credentials are requested or
written by this installer.
The default is a tailnet-only HTTPS origin via operator-managed Tailscale Serve:
https://axat-vps.<tailnet>.ts.net (fleet: https://axat-vps.tail140c22.ts.net).
Forward it to 127.0.0.1:4788, including /api, /mcp and /.well-known;
run setup and clients with tailnet access. Public reachability is not required.
Only OAuth providers that fetch callbacks or client metadata
server-side need temporary public exposure via operator-managed Funnel; arrange
that exception for their flow and restore tailnet-only access afterwards.
Haoshoku never changes nginx, DNS, TLS, firewall rules, Tailscale Serve/Funnel or
OAuth client credentials. The hand-edited VPS compose remains operator-managed:
a rerun refuses a differing compose rather than undoing the move to Serve.
A fresh deployment pulls ghcr.io/usefulsoftwareco/executor-selfhost:latest,
resolves its digest, checks the image's 65532:65532 runtime user and /data
volume, and pins that digest in /srv/executor/docker-compose.yml. The only
published port is 127.0.0.1:4788:4788. EXECUTOR_WEB_BASE_URL supplies the
explicit HTTPS origin. Haoshoku creates a private empty data/ directory and
changes ownership of that directory once; it never recursively changes data
ownership. Upstream persists its database and generated encryption/session keys
there. haoshoku-executor.json records the managed format and pulled digest.
An existing manually configured deployment, differing compose file, unknown
path, symlink, partial deployment, incompatible data ownership or conflicting
container/port stops before pulling or starting anything. Existing files remain
intact. Inspect and manage such deployments manually; there is no force,
automatic adoption, migration, cleanup or upgrade path. A matching managed rerun
checks the running executor-selfhost container and application without pulling,
recreating, restarting, updating or rewriting it. Supported managed data files
are data.db (plus SQLite -wal/-shm files), secret.key and auth-secret.key;
additional files require manual inspection.
Readiness checks require /api/health JSON with status: "ok" and Executor's
OAuth authorization-server metadata advertising the configured origin and
/api/auth/mcp endpoints, on both loopback and the configured HTTPS origin. Each
request, including its body, has a five-second deadline and a 16 KiB limit;
verification makes at most five attempts, with one second between failed attempts
(at most four retries). Redirects, proxy HTML, generic error
responses and wrong metadata fail. A failed step exits nonzero and preserves
partial state for manual inspection; rerunning never tries destructive repair.
“Container ready” does not prove owner onboarding, an authenticated MCP handshake or healthy integrations. Complete first-owner signup yourself in the tailnet web UI, then configure authentication, policies and integrations there. Haoshoku does not create owners, API keys, policies or OAuth connections.
Opt-in Executor clients
For an ordinary agent user, run this standalone command after making the existing Executor HTTPS MCP endpoint and its authentication available:
# Supply EXECUTOR_AUTHORIZATION securely in this process environment first.
haoshoku --executor-clients https://executor.example.net/mcpEXECUTOR_AUTHORIZATION must contain the complete Authorization header value:
Bearer followed by the API key/token accepted by that endpoint. Do not put the
secret in CLI arguments, a tracked file, or shell history. Missing, empty, raw-token,
and newline-bearing values are rejected without printing them. The command stores
the full header inline in both private client configs (mode 0600). Future harness
sessions do not need this environment variable. HTTPS is required;
ports and endpoint paths such as /mcp are supported, credentials/query/fragment
are rejected. This endpoint differs from --server-executor's origin-only input.
The command adds an executor HTTP entry for both Claude Code and Codex.
It requires no root privileges or harness subprocess and can run from any directory.
It is separate from server provisioning and never creates accounts, keys, policy
rules, or integrations. It is not part of default OS setup and cannot be combined
with another mode or --os.
Supported configuration locations:
- Claude Code:
$HOME/.claude.json, or$CLAUDE_CONFIG_DIR/.claude.jsonwhen that override is nonempty. The entry lives in the top-levelmcpServersmap (user scope), withheaders.Authorizationset to the complete supplied header. - Codex:
$CODEX_HOME/config.toml, defaulting to$HOME/.codex/config.toml. The[mcp_servers.executor]table useshttp_headers = { Authorization = "Bearer <API key/token>" }.
Claude's legacy $HOME/.claude/.config.json or
$CLAUDE_CONFIG_DIR/.config.json takes precedence over the standard file.
If that path exists (including a link), or its layout cannot be safely inspected,
setup refuses before writing either client. A default $HOME/.claude directory
with group write permissions or a resolvable symlink is supported when no legacy
file exists and lookup succeeds; it is only a lookup path. Actual write-target ownership, permissions
and link checks still apply, including to an explicit CLAUDE_CONFIG_DIR.
It never reads, adopts or rewrites the legacy file. Use manual user-scope MCP
setup in the intended Claude/Codex environment for these layouts. An explicitly empty CLAUDE_CONFIG_DIR is
ambiguous for legacy lookup; unset it for the default home or set an absolute home.
Nonempty CLAUDE_CODE_CUSTOM_OAUTH_URL, USE_STAGING_OAUTH or USE_LOCAL_OAUTH
also causes refusal before either write; use manual setup with that environment.
Custom OAuth selects .claude-custom-oauth.json. The installed production Claude
2.1.292 fixes its staging/local filename selector to production; other builds'
.claude-staging-oauth.json / .claude-local-oauth.json targets are unsupported.
The switches are refused conservatively for any nonempty value, including 0
or false; setup does not interpret their value semantics or change them.
Use absolute homes for the intended non-root user; XDG_CONFIG_HOME does not
redirect these MCP files. Both clients use the saved header verbatim, including
Bearer . The file locations were checked against Claude Code 2.1.292,
Codex 0.160.1, and the
Claude MCP documentation,
Claude environment settings, and
Codex MCP /
config-home documentation.
Earlier native offline readback verified the previous environment-reference entries;
the inline entries and connectivity check are covered by local stub-server tests.
Live native client and T3 session access, older versions and other Claude builds
remain unverified.
Both files are inspected before writes. A matching entry already at mode 0600 is
a config no-op, including timestamps. Readable existing configs are restricted
to mode 0600. A differing or unknown executor
entry stops: reconcile it manually in the effective user config, then retry.
Matching inline headers are reused; differing credentials and old environment-reference
entries require manual reconciliation. They are never printed or migrated automatically.
Unrelated JSON bytes, settings, TOML sections, and comments are retained. Duplicate
JSON keys, malformed configs, linked files/directories, foreign ownership, and
shared write permissions are refused. The additive TOML path conservatively refuses
multiline strings and inline/dotted mcp_servers parent definitions; use regular
MCP tables or configure the entry manually for those shapes.
Config files use mode 0600 and new directories 0700. Existing files are updated in place, with changed-input checks before writing and restoration from memory after caught write failures. No secret-bearing backups or temporary config copies are created. Run with other config writers stopped: these two file updates are not an atomic transaction across crashes or power loss. An incomplete/recovery warning requires manual inspection of both files before retrying; empty directories may remain after a failed fresh setup.
For T3-launched sessions, run setup with the homes of the user that runs the
future T3 backend/harness. That backend and its future child sessions must use
the same HOME, CODEX_HOME, and/or CLAUDE_CONFIG_DIR. The inline header is
read from those files; no service or login-shell environment setup is needed.
This command does not alter T3 provider settings, services, launch environments, or sessions. Managed/project config and explicit launch overrides
can take precedence over these user entries.
“Configuration written” or “already matches” reports local registration. The command
then sends an authenticated MCP initialize request, accepting JSON or event-stream
responses, and an initialized notification. The check has a five-second total
timeout and refuses redirects. “Executor connected” confirms that check only;
OAuth, tool discovery and account/integration access remain unverified. A failed
check prints “Executor connection failed”, exits nonzero, and keeps the private
configs for inspection and retry. Headers, server responses and transport errors
are never printed. Verify expected tool access separately in the future harness session.
