Linux from zero

For a Linux machine that has never had KernelHub. Every step can be copied, and every step says what success looks like: install Node, install KernelHub with one line, log in a Runtime, pair your phone, send a real task, watch it deliver.

How to use this page
Work from top to bottom, one step at a time. Every step tells you which line to copy, what success looks like, and what to do if you don't see it. Each command block has a Copy button: click it, paste into your terminal (or SSH window), press Enter. With internet access you only need sections 1 and 2. Blocked network: section 5.

1 · Before you start: what this Linux machine is missing

You have a Linux machine that has never had KernelHub on it (x86-64 or arm64; desktop, server or VM), and you want to get to "send work from my phone, have this machine do it". First, what it ships with and what it lacks:

ThingOn a fresh system?Notes
bash / tar / sha256sumYesCommands on this page are written for bash; sha256sum comes with coreutils.
curlNot alwaysMany minimal systems and container images don't have it. Section 2, step 1 checks first.
Node.js / npmNoThe single most important row on this page. The installer does not install Node for you. Without npm it cannot install an AI coding tool (a Runtime), and you end up with "KernelHub is installed, but it cannot run a single task". Distro nodejs packages are often old, so this page uses the official nodejs.org build.
~/.local/bin on PATHNot alwaysKernelHub and the Node from this page both live there. Section 2, step 3 makes sure it's on PATH with one line.
A logged-in RuntimeNoKernelHub ships no model. It drives a CLI coding agent that you installed and logged into yourself (Claude Code by default). Installed is not usable: until you log in, tasks still cannot run.
A phoneWith the KernelHub app installed, for scanning the pairing code, sending tasks and approving.
Three rules (follow them in this order and you skip most of the redo)
  1. Install Node first, then KernelHub. The other way round, you end up unable to work (it is recoverable: once Node is in, run the install command again).
  2. Log the Runtime in once after installing (type claude and follow the prompts). Otherwise every task comes back with "no runtime here can run it".
  3. After pairing, keep icloser run open (or install it as a background service). Pairing only introduces your phone to this machine; the thing that actually picks up work is icloser run.
Where the "you will see" samples come from
The grey boxes are verbatim from a real run: a freshly started container from the official Ubuntu 24.04 image plus systemd (Ubuntu 24.04.4 LTS, aarch64), with only sudo and a normal user added — no curl, no Node. x86-64 (amd64) was not run.

Apart from installing curl and enabling lingering, no step on this page needs sudo. If you are already root (your prompt ends in #), just drop the sudo in front of those commands.

2 · With internet access: the shortest path (15 steps)

One line per step, copy and run.

Step 1 · Make sure curl is there

$ curl --version

Success: curl prints its version — go on to step 2. On a fresh minimal system you may see (no curl):

bash: curl: command not found

Install curl on Ubuntu / Debian:

$ sudo apt-get update && sudo apt-get install -y curl ca-certificates

Install curl on Fedora / RHEL / CentOS Stream:

$ sudo dnf install -y curl

Step 2 · Check whether Node is there

$ node -v

Node already installed: it prints a version — skip to step 3; steps 4 and 5 can be skipped. On a fresh system you will see (Node is missing; carry on):

bash: node: command not found

Step 3 · Put ~/.local/bin on your PATH

This line does three things: appends one line to ~/.bashrc if it isn't there yet, applies it to the current window straight away, and prints the first PATH entry so you can see it. Running it twice doesn't add it twice. (If you use zsh, change both ~/.bashrc to ~/.zshrc.)

$ grep -qsF ".local/bin" ~/.bashrc || echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc; export PATH="$HOME/.local/bin:$PATH"; echo "$PATH" | cut -d: -f1

Success: it prints /home/your-name/.local/bin (for root, /root/.local/bin):

/home/kh/.local/bin

Step 4 · Download Node.js and check it against the official checksum

The current LTS is Node v24.21.0. This line downloads the official nodejs.org .tar.gz for your CPU (x86-64 / arm64), then checks it against the official SHASUMS256.txt:

$ V=v24.21.0; A=$(uname -m | sed -e s/x86_64/x64/ -e s/aarch64/arm64/); F=node-$V-linux-$A.tar.gz; curl -fL -o /tmp/$F https://nodejs.org/dist/$V/$F && curl -fsSL https://nodejs.org/dist/$V/SHASUMS256.txt | grep " $F\$" | (cd /tmp && sha256sum -c -)

Success: the last line is the file name followed by OK:

node-v24.21.0-linux-arm64.tar.gz: OK

Ends in FAILED: the download is broken — run the line again. Errors like curl: (6) / (7) / (28) / (35): a network problem; retry, and if it keeps failing set a proxy (section 5).

sha256sum: WARNING: 1 computed checksum did NOT match
node-v24.21.0-linux-arm64.tar.gz: FAILED

Step 5 · Unpack Node into ~/.local (no sudo)

$ V=v24.21.0; A=$(uname -m | sed -e s/x86_64/x64/ -e s/aarch64/arm64/); mkdir -p ~/.local && tar -xzf /tmp/node-$V-linux-$A.tar.gz -C ~/.local --strip-components=1 && node -v && npm -v

Success: two version lines, one for node and one for npm:

v24.21.0
11.19.0

Why this way: node, npm, and later icloser and claude all end up in ~/.local/bin, and npm's global installs land in ~/.local too — no sudo, no EACCES errors. The official build is for glibc systems (Ubuntu, Debian, Fedora and the like); on musl systems such as Alpine, use the distro's own nodejs package and see section 4.

Step 6 · Install KernelHub with one line

$ curl -fsSL https://icloser.xyz/install | sh

It does these things in order — you don't have to do anything, just watch:

What it doesDetails
Identifies the machineDetects Linux and the CPU (amd64 / arm64).
Reads the release manifestFetches stable.json from kernelhub.icloser.xyz; refuses to install unless it says READY, with no override.
Downloads and verifiesDownloads icloser, checks SHA-256 and size, discards it and stops on mismatch; if the machine has neither shasum nor sha256sum, it refuses to install.
Where it goes~/.local/bin/icloser, plus three legacy names icli / kernelhub-cli / devnexus-cli.
Local consoleAlso installs ~/.local/bin/kernelhub-desktop; if that download fails it prints one warning and the CLI install still stands.
Self-checkRuns icloser version on the spot and compares it with the manifest's version and build.
One RuntimeIf npm is there: npm install -g @anthropic-ai/claude-code (only claude, by default). If it's already installed, it's left alone.

On success you'll see (the installer's messages are in Chinese):

  清单:stable 频道 · v1.0.2 · build 202609161352 · commit a4099c062543
下载 icloser_linux_arm64 …
✓ SHA-256 核对通过
✓ 装到 /home/kh/.local/bin/icloser
✓ 装到 /home/kh/.local/bin/kernelhub-desktop(本机管理台)
✓ icloser 1.0.2 (build 202609161352, commit a4099c062543)
装 Runtime:claude(npm install -g @anthropic-ai/claude-code)…
✓ 装好 Runtime:claude(@anthropic-ai/claude-code)
下一步:
  ① 登录 Runtime(只用一次)。装上但没登录,任务照样跑不了:
        claude
     按提示登录完,退出它即可。
  ② cd 到你的项目目录,跑:
        icloser pair

If it contains "这台机器上没有 npm,Runtime 没装" (no npm on this machine, no Runtime installed), steps 4–5 were skipped or didn't take. KernelHub itself is installed; go back, install Node, then run this line again:

  清单:stable 频道 · v1.0.2 · build 202609161352 · commit a4099c062543
下载 icloser_linux_arm64 …
✓ SHA-256 核对通过
✓ 装到 /home/kh/.local/bin/icloser
  ⚠️ 管理台没下下来(kernelhub-desktop_linux_arm64)—— 只装了 CLI
✓ icloser 1.0.2 (build 202609161352, commit a4099c062543)
  ⚠️ 这台机器上没有 npm,Runtime 没装(KernelHub 本体已经装好)
     装好 Node.js(自带 npm)之后重跑一次这个安装器即可。

If it says "管理台没下下来 —— 只装了 CLI" (console didn't download, CLI only): usually a network hiccup — just run the line again.

Step 7 · Check which version you got

$ icloser version

Success: one line containing both 1.0.2 and 202609161352:

icloser 1.0.2 (build 202609161352, commit a4099c062543)

icloser: command not found: step 3 wasn't done; do it and retry.

Step 8 · Log the Runtime in once

First, see what it looks like before logging in. The first output is long (a matrix plus dozens of lines of notes) — only look at the Claude Code row at the top: before login it says 状态不明 (STARTABLE_AUTH_UNKNOWN), after login ✅ 可用 (AUTHENTICATED).

$ icloser runtimes
这台机器上的 Runtime(14 家已注册 · 1 家装着 · 0 家可直接用 · 0 家卡在登录/凭据)
     Runtime                版本          状态           登录态 / 位置
  ⚠️ Claude Code            2.1.272     状态不明         声明了探测手段但一条都没命中,状态问不出来(不猜)
  Claude Code            STARTABLE_AUTH_UNKNOWN       起得来,但登录态问不出来:声明了探测手段但一条都没命中,状态问不出来(不猜)

Then log in. The first run walks you through it; follow the prompts on screen (on a server with no browser, finish the login on your own computer the way it tells you). When done, type /exit or press Ctrl + C twice to leave:

$ claude

The screens the first claude run shows (Linux run, the gist of each screen):

  1. Welcome to Claude Code v2.1.272 / Choose the text style — press Enter (Dark mode is the default).
  2. Select login method — press Enter (1. Claude account with subscription).
  3. Browser didn't open? Use the url below to sign in — open that link in a browser, sign in, paste the code the page gives you back here, press Enter.
  4. Login successful. Press Enter to continue… — press Enter.
  5. Security notes … Press Enter to continue… — press Enter.
  6. Accessing workspace … ❯ No, exit / Yes, I trust this folder — press Enter (No, exit: you only came to log in).

Run icloser runtimes again. Success: the Claude Code row is ready to use:

这台机器上的 Runtime(14 家已注册 · 1 家装着 · 1 家可直接用 · 0 家卡在登录/凭据)
     Runtime                版本          状态           登录态 / 位置
  ✅  Claude Code            2.1.272     可用           /home/kh/.local/bin/claude
  Claude Code            AUTHENTICATED                起得来、凭据也在(配置文件 /home/kh/.claude.json 里有顶层键 oauthAccount(只看键是否存在,不读值))。 …

Still stuck on login (the state reads STARTABLE_NOT_AUTHED or "unknown" (STARTABLE_AUTH_UNKNOWN)): type claude and log in again.

Step 9 · Create a project folder and go into it

If you already have a project, use its path instead:

$ mkdir -p ~/projects/my-app
$ cd ~/projects/my-app

If the project uses git, make sure git is installed (sudo apt-get install -y git on Ubuntu / Debian, sudo dnf install -y git on Fedora).

Step 10 · Pair your phone

$ icloser pair

It does three things in turn:

  1. Creates a workspace in the current folder.
  2. Asks how far the agent may go on this machine — just press Enter for tier 3, read + write + execute: it can change files and run builds and tests. Most people pick this.
  3. Draws a QR code in the terminal, with a "paste this if you can't scan" link below it.

The Linux run did not keep this screen separately; in the Mac run the permission question is a 6-tier menu where Enter = 3 — see step 10 of Mac from zero.

Scan the code with the KernelHub app on your phone. If your SSH window is too narrow to draw the whole code, widen it and use the line below to redraw it.

QR code scrolled away? Do not run pair again. Pairing again creates a new, empty workspace, and none of your earlier tasks are in it. Bring the code back with:
$ icloser qr

Step 11 · Enter an invitation code in the phone app

The first time a brand-new account runs pair, the window shows this (verbatim from the Mac run; Linux uses the same CLI):

  账户 7a9e5014 还没有使用权 —— 在 iPhone 或 Android 上输入一次邀请码即可,
  这台机器会自动继承(CLI 上没有激活入口)

It says, in Chinese: this account has no access yet — enter an invitation code once on iPhone or Android and this machine inherits it; the CLI has no activation entry. Open the KernelHub app on your phone and enter your invitation code there. Once it is redeemed, this computer inherits it automatically; nothing to type here.

How to get a code: Access & activation. An account that is already active says access was inherited during pair, and you skip this step.

Step 12 · Start taking work

$ icloser run

Leave this window open. It keeps running and checks for new tasks every few seconds — it is what picks up the tasks you send from your phone. Its first lines say which workspace it joined and which Runtime it chose:

icloser · 设备 cli-fresh-ubuntu · 工作区 cc274549-d96e-44d8-a0a1-f89727078c5e
Selected Runtime: claude · 适配器 claude-code(支持会话接续、只读可强制)
  选择原因: eligible pool 里最佳可用:claude
工作目录 /home/kh/projects/my-app · 轮询 3s
等待命令中(Ctrl-C 退出)…

Step 13 · Send a real task and watch it deliver

With the step 12 window still open, pick up your phone:

  1. In the KernelHub app, choose the workspace you just paired and type one sentence into the box. Name the file if you know it, for example "Add a line with today's date at the end of README.md. Don't commit."
  2. The icloser run window on your computer scrolls a few new lines — the task has been picked up. On the phone the task card turns into a live view: which files it read, which commands it ran, which files it changed.
  3. If the task lacks a permission tier, it stops before acting and shows a card; approving applies to this task only.
  4. When it finishes, tap the card's changes to read the diff line by line, then read the result: what it did, what it changed, what it couldn't do. Not happy? Ask for changes, and the same task carries on.

Success: the task shows as done, the changes show the new line in README.md, and the file on your computer really has that extra line.

No Go / Python on a fresh machine? The first task does not stop for it
KernelHub checks the machine-level base tools (Go, Python) on the way. If one is missing and this machine has not been allowed to access the internet, the task carries on and no card is raised; the icloser run window just logs one line (verbatim from the CLI source, in Chinese: the base tool is not installed, no card, not blocking, the task carries on; to fill it in run icloser env bootstrap go, or allow exec + web for this device):
  ⓘ go:KernelHub Base(机器级,与这条任务无关)没装上 —— 不挂卡、不阻塞,这条任务照常往下跑。要补齐:在这台机器上跑 icloser env bootstrap go,或在设备权限里批 exec + web 后它会自动补上。
A card appears only when the task really needs it — the request names it (say, it mentions go build) or the work actually runs it — and the install is refused; approving applies to this one task only. On a machine that already allows exec and internet access it installs the tool and carries on without any card (seen on a real Windows machine).

This is what a delivered task looks like in the step 12 window (Linux transcript):

▶ 102f0802  新建 hello.txt,内容只写一行:hello from kernelhub。不要提交。
  ✓ 落盘核对通过(看了 2 个路径,没有越权改动)
  ⓘ 改动 1 个文件(+0 −0)
  ⓘ 已上传 1 份产物,手机上可以直接打开
✓ 完成(16s)
    我在项目根目录新建了 `hello.txt`,写入前确认过这个文件不存在。里面只有一行:
    ```
    hello from kernelhub
    ```

The result of that task (seen from the CLI; the phone shows the same record):

新建 hello.txt,内容只写一行:hello from kernelhub。不要提交。
  状态    done
  机器    fresh-ubuntu
  完成于  09-15 16:58
  小结    我在项目根目录新建了 `hello.txt`,写入前确认过这个文件不存在。里面只有一行:

```
hello from kernelhub
```

我没有提交,这个目录也不是 git 仓库。
  改动    改了你的文件。看改了什么:icloser task diff 102f0802

Nobody picks it up: check that the step 12 window is still open; if it's still stuck, do the health check in the next step.

Step 14 · Health check (in a second terminal / SSH window)

$ icloser doctor
  ✓ 工作目录  /home/kh/projects/my-app
  ? 常驻服务  Unknown(查不到服务状态)
  ✓ 网关连得上、令牌有效
  ✓ 读得到任务队列
      任务 2 条,其中 0 条还没人接
      执行体 fresh-ubuntu · 在轮询(0 秒前)
  ✓ 读得到本机权限
      已生效:exec read write
  ✓ 读得到 AI 团队
  本机 Runtime
      ✓ claude     /home/kh/.local/bin/claude
都正常。

This transcript is from the version before the fix: the background-service line used to be Unknown on Linux every time; from v1.0.2 it says whether it is installed and running.

It checks the service, gateway, queue, permissions and Runtimes in one go. Whatever is wrong, it tells you what to type next.

Step 15 · Keep working after you close the window or disconnect SSH: run it in the background

Once the step 12 window closes (or SSH drops), nobody picks up tasks from your phone. On a system with systemd, run this (it uses the project folder recorded at pairing, so it doesn't matter which folder you type it in):

$ icloser daemon install
✓ 已常驻 kernelhub-cli.service
  执行体   /home/kh/.local/bin/icloser run
  项目目录 /home/kh/projects/my-app
  unit     /home/kh/.config/systemd/user/kernelhub-cli.service
  日志     /home/kh/.kernelhub/daemon.log
     除非开启常驻会话:sudo loginctl enable-linger kh
     想要开机即起、与登录无关:用 root 再跑一次 icloser daemon install
  看状态:icloser daemon status

(Excerpt: between the log line and the lingering line, the real output has one more ⚠️ line saying this is a user-level service because you are not root, and it will be stopped when you log out.)

$ sudo loginctl enable-linger "$USER"
$ nohup icloser run >> ~/icloser-run.log 2>&1 &

Check on the background service any time:

$ icloser daemon status
✓ 正在跑 · pid 12860
  日志 /home/kh/.kernelhub/daemon.log

See what it has been doing lately:

$ icloser logs -n 50

With the background service installed, you can close the manual icloser run from step 12 — running both is redundant.

3 · Local console (including servers with no desktop)

The console is a separate program; icloser run doesn't start it. Start it (this window stays busy too):

$ kernelhub-desktop --port 18092

Open http://127.0.0.1:18092 in a browser on this machine. It listens only on the loopback address, so other machines can't reach it directly — on purpose.

No browser on the server? On your own computer, open an SSH tunnel (replace the user name and server address; leave that window open), then open http://127.0.0.1:18092 in your own computer's browser:

$ ssh -N -L 18092:127.0.0.1:18092 you@your-server

To see whether the console is running and at what address:

$ icloser console

4 · Another way to get Node: your distro's package

You can also install nodejs and npm with your distro's package manager. Two things to know: the version is often old, and it installs into system folders, so afterwards npm install -g often fails with EACCES (permission denied) — including the step where the KernelHub installer installs claude for you.

Don't force it with sudo. Point npm's global folder at ~/.local instead (do section 2, step 3 first):

$ npm config set prefix ~/.local && npm config get prefix

Success: it prints /home/your-name/.local. Then run step 6's install command again.

5 · Adding Runtimes, blocked networks

Add another Runtime (no need to rerun the whole install)

Only claude is installed by default. These can be installed with npm: claude, codex, qwen, codebuddy, opencode, kimi, mimo, pi. This line installs codex; swap in any of them:

$ icloser env bootstrap codex

Or pick it through the installer (again, swap codex for the one you want):

$ KERNELHUB_RUNTIME=codex curl -fsSL https://icloser.xyz/install | sh

Each one needs its own login (type its name, for example codex). Google's entry is agy (Antigravity); it does not come from npm — install it from Google's official channel.

Set a proxy (for this window only)

Replace PORT with your own proxy's address and port; the last part checks it:

$ export https_proxy=http://127.0.0.1:PORT http_proxy=http://127.0.0.1:PORT; curl -sS -o /dev/null -w "install -> HTTP %{http_code}\n" https://icloser.xyz/install

Success: it prints install -> HTTP 200. If not, the proxy is wrong — don't go further yet. Once it works, run section 2's commands in this same window.

Use an npm mirror

$ npm config set registry https://registry.npmmirror.com && npm config get registry

It echoes the mirror address. registry.npmmirror.com is a common public mirror; to use another one, swap the address.

Point KernelHub's downloads at a mirror / skip the Runtime

For company networks with an internal mirror (it must mirror the official folder exactly; SHA-256 is still checked; the installer script itself still comes from icloser.xyz):

$ KERNELHUB_INSTALL_BASE=https://your-mirror.example.com/kernelhub curl -fsSL https://icloser.xyz/install | sh

Install no Runtime at all (CLI only):

$ KERNELHUB_SKIP_RUNTIME=1 curl -fsSL https://icloser.xyz/install | sh

6 · Verify the files yourself

$ sha256sum ~/.local/bin/icloser ~/.local/bin/kernelhub-desktop

Compare the output with the table below, using the rows for your CPU:

FileBytesSHA-256
icloser_linux_amd64 (x86-64)12,853,432d3475109eb22f704a2690a2295c289f6b5cd76ed164e19c90d6f4113cc754a68
kernelhub-desktop_linux_amd648,220,8566a6ebce22da317af3efd70d08d417ac41588ff0523ca1a935aae1b08ab4332ee
icloser_linux_arm64 (arm64)12,058,808f182497a8f536a9bda060d0cb7f46f227eace2ccf2506caf58bd963dd03c4bac
kernelhub-desktop_linux_arm647,798,96878aacc8bdd4e0be4b8815dc5c93715a0cde0edf986829de21ae8dab2199bd258
node-v24.21.0-linux-x64.tar.gz6e1db87ef58b8819e5d5402eff1536491b18edd8eb7bee5ef7897876e88dc5ff
node-v24.21.0-linux-arm64.tar.gz724282c3b43aec998aa9527380465b45d229e021b58035f5f4f63095eabfe5d5

The KernelHub rows match the release manifest stable.json exactly; the two Node rows come from nodejs.org's SHASUMS256.txt. The installer has already checked these for you — this is for anyone who wants to see it with their own eyes.

7 · Common errors: symptom → cause → the one line to run

What you seeCauseWhat to do
curl: command not foundMinimal system without curlSection 2, step 1: install curl
node: command not foundNode not installed, or step 3 skippedSection 2, steps 3–5
icloser: command not found~/.local/bin isn't on PATHSection 2, step 3
Installer says "这台机器上没有 npm,Runtime 没装"KernelHub was installed before NodeInstall Node, then run curl -fsSL https://icloser.xyz/install | sh again
Installer says "这台机器上没有 shasum 也没有 sha256sum"coreutils missingInstall coreutils, then rerun the installer
npm install -g fails with EACCESNode is installed in system foldersSection 4: npm config set prefix ~/.local
Node check says FAILEDBroken downloadRerun the step 4 line
Tasks come back with "no runtime here can run it"Runtime not installed or not logged inType claude to log in, then check with icloser runtimes
Phone paired, but tasks just sit thereicloser run isn't open and no background service is installedRun icloser run in the project folder, or icloser daemon install from step 15
Once SSH drops or you log out, tasks stop being picked upsystemd stopped the user-level serviceEnable lingering in step 15 (loginctl enable-linger)
daemon install says there is no systemctlContainer or minimal system without systemdUse the nohup line from step 15
The QR code scrolled awayThe window scrolledType icloser qrdon't pair again
curl: (18) / (35) / (56), or "清单和服务器上的产物对不上了" (manifest and server disagree), or just a single curl: (35) … line and nothing elseThe network dropped mid-download (seen several times in our runs; the artifacts were fine)Run the same command again
daemon install says Failed to connect to busThis is not a normal login session (switched in with su, exec'd into a container), so user-level systemd is unreachableLog in normally (desktop or SSH) and run it again; as a normal user, run sudo loginctl enable-linger "$USER" first

8 · Uninstall / clean up completely

① If you installed the background service, remove it first:

$ icloser daemon uninstall

② See exactly what would be deleted (list only):

$ ls -d ~/.kernelhub ~/.kernelhub-gate ~/.kernelhub-protected 2>/dev/null

③ Delete the data folders. The three hold configuration and the task ledger, identity credentials, and the protected root; it's only clean once all three are gone. You'll have to pair again afterwards:

$ rm -rf ~/.kernelhub ~/.kernelhub-gate ~/.kernelhub-protected

④ Delete the programs:

$ rm -f ~/.local/bin/icloser ~/.local/bin/icli ~/.local/bin/kernelhub-cli ~/.local/bin/devnexus-cli ~/.local/bin/kernelhub-desktop

The PATH line in ~/.bashrc can stay (Node lives in that folder too). Node and the Runtimes are yours: the Node from this page lives under ~/.local — if you don't want it, delete ~/.local/bin/node, ~/.local/bin/npm, ~/.local/bin/npx and ~/.local/lib/node_modules.