The Shell¶
Bash is the default shell for all workspace terminals. Two system files
set up the environment before your personal ~/.bashrc runs:
/etc/profile.d/klangk-*.sh— environment exports (PATH=/opt/klangk/bin,EDITOR) sourced by every login shell, interactive or not. This is why one-shot commands likeklangk exec(bash -lc) still findpiand theklangk-*helpers. (The workspace health check is the exception — it runs as a non-loginbash -cand sources nothing; see Health Check.)/etc/bash.bashrc— interactive-shell setup: waits for container readiness, runson-shell-initfeature hooks, and (via the default command) launches the workspace's configured service.
Your ~/.bashrc persists across container restarts (it lives on the
bind-mounted home directory), so any customizations you make are permanent.
Users and the single-UNIX-user design¶
Workspace containers run as a single UNIX user called klangk. There
are no separate UNIX accounts for each workspace member — everyone
runs as the same uid.
This is intentional. Collaboration between actual UNIX users is invariably painful: file ownership mismatches, group permission headaches, umask defaults that lock teammates out, setgid directory hacks that half-work. Every project that tries to make multi-user file sharing work on UNIX ends up with a pile of permission workarounds.
By running everything as a single UNIX user, Klangk sidesteps all of this. Every workspace member can read and write every file without fighting permissions. Collaboration just works.
Home directory layouts¶
A workspace chooses between a shared home (/home/klangk for
everyone) and a per-handle home (each member gets a private
/home/<handle>/). See
Workspaces — Home directory layout
for the full explanation, ceiling behavior, and CLI flags.
The service command session always runs with
HOME=/home/klangk — under both layouts — so environment setup that
must reach the service goes in /home/klangk/.profile (see
Sandbox setup for the recipe).
See Handles for how handles are assigned and how they relate to your home directory path.
The tradeoff¶
Because all workspace members share the same UNIX user, every member
can read and modify every file under /home — including other members'
home directories and dotfiles. This is the cost of frictionless
collaboration. Use separate workspaces for collaboration with different
groups of differently trusted users, or for solo work where you need
full privacy.
Warning
Do not store secrets or sensitive data in your home directory if you share the workspace with untrusted users.
Customizing your environment¶
You can customize your shell the same way you would on any UNIX system. The key question is which file to put a change in, because Klangk runs commands in several contexts and not all of them source the same startup files (see Startup files below):
- Edit
~/.profilefor environment exports (PATH additions,OPENCLAW_HOME, tool-manager setup like nvm/asdf) that login-shell commands — interactive terminals, the service command, andklangk exec— must see. (The health check is deliberately not a profile consumer; see Health Check.) - Edit
~/.bashrcfor interactive niceties (aliases, prompt customization) that only matter in a terminal you're typing into. - Add scripts to
~/bin - Configure
~/.gitconfig,~/.vimrc, etc.
All changes persist across container restarts.
Startup files¶
Klangk runs in-container commands in a few different ways, and each
sources a different set of startup files. Getting this right matters:
an environment export buried below ~/.bashrc's interactivity guard is
invisible to klangk exec, so a one-shot command run there can fail
to find a tool even though an interactive terminal finds it fine.
The one deliberate exception is the health check: it runs as a
non-login bash -c and sources nothing, so it stays deterministic
and immune to your interactive setup. It uses absolute paths instead.
See Health Check.
Convention¶
| File | Purpose | Sourced by |
|---|---|---|
/etc/profile.d/klangk-*.sh |
system-wide defaults (klangk PATH, EDITOR) |
every login shell |
~/.profile |
environment exports (PATH additions, tool homes, nvm/asdf) — anything login-shell commands must see | login shells: interactive terminals, the service command, klangk exec (bash -lc) |
~/.bashrc (below the interactivity guard) |
interactive niceties (aliases, prompt) | interactive non-login bash shells; also chained from ~/.profile for login shells |
Rule of thumb: if a login-shell command (klangk exec, a setup
script, the service command) needs it, it goes in ~/.profile. If it
only matters when you're at a prompt, it goes in ~/.bashrc. The
health check is not a ~/.profile consumer — see
Health Check.
Which code path sources what¶
| In-container command | How Klangk runs it | Sources ~/.profile? |
|---|---|---|
| Interactive terminal | tmux new-session (login shell) or bash -l |
yes — your own home's, or /home/klangk/.profile under the shared layout |
service_command (the service-cmd window) |
login shell (tmux window 0) | yes — always /home/klangk/.profile (the shared home), under both layouts |
| Workspace health check | bash -c (a non-login shell) |
no — the probe is deterministic; uses absolute paths (see Health Check) |
klangk exec (default) |
bash -lc (a login shell) |
yes |
klangk exec --raw / klangk sync |
raw command (no shell) | no — programmatic transports (rsync) must not source startup files |
This is why workspace setup scripts (sandboxes/*/setup.sh) persist
their env exports to ~/.profile rather than ~/.bashrc: the exports
must be visible to the service command and klangk exec, both of
which are login shells that source ~/.profile. ~/.bashrc's
interactivity guard (case $- in *i*) ;; *) return) hides its body
from those non-interactive login shells. The health check is the
exception — it deliberately sources nothing (see
Health Check).
Using zsh instead¶
Zsh is installed but not the default shell. To switch, add the following
to your ~/.bashrc inside the workspace:
This lets the bash startup complete normally (feature hooks, default
command handling), then replaces bash with zsh. The ZSH_STARTED guard
prevents infinite loops. Your ~/.zshrc will be sourced as usual.