# Omnara
## Full Omnara setup with `happy` for machine management
2026-01-13
### System (root) setup
(Local) Copy `lexe-agent` SSH keys into root account
```bash
$ scp ~/.ssh/lexe-agent-2026-ed25519 root@<IP>:~/.ssh/
$ scp ~/.ssh/lexe-agent-2026-ed25519.pub root@<IP>:~/.ssh/
```
(Local) SSH into the VM as root
```bash
ssh root@<IP>
```
(Root) Set up 16 GB of swap
```bash
fallocate -l 16G /mnt/swap
chmod 600 /mnt/swap
mkswap /mnt/swap
swapon /mnt/swap
echo '/mnt/swap none swap sw 0 0' >> /etc/fstab
```
(Root) Install `gh`
```bash
# https://github.com/cli/cli/blob/trunk/docs/install_linux.md#debian
$ (type -p wget >/dev/null || (sudo apt update && sudo apt install wget -y)) \
&& sudo mkdir -p -m 755 /etc/apt/keyrings \
&& out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg \
&& cat $out | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \
&& sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
&& sudo mkdir -p -m 755 /etc/apt/sources.list.d \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& sudo apt update \
&& sudo apt install gh -y
```
(Root) Install Rust dependencies
```bash
sudo apt-get update && sudo apt-get install -y build-essential
sudo apt-get install -y protobuf-compiler
```
(Root) Install happy
```bash
# Install nvm, npm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
\. "$HOME/.nvm/nvm.sh"
nvm install 24
node -v # Should print "v24.12.0".
npm -v # Should print "11.6.2".
# Install happy
$ npm install -g happy-coder
```
(Root) Daemonize happy as `happy-root`
- TODO(dev): Can remove this step; it seems that giving the user passwordless `sudo` access still allows Omnara to work
```bash
cat > /etc/systemd/system/happy-root.service << 'EOF'
[Unit]
Description=Happy Claude Code Instance (root)
After=network.target
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/root
Environment=TERM=xterm-256color
ExecStart=/usr/bin/tmux new-session -d -s happy-root "bash -lc 'source ~/.nvm/nvm.sh && happy --yolo'"
ExecStop=-/usr/bin/tmux kill-session -t happy-root
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable happy-root.service
systemctl start happy-root.service
systemctl status happy-root.service
```
(Root) Create non-root user
```bash
sudo useradd -m -s /bin/bash dev
# Allow passwordless sudo
echo 'dev ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/dev
chmod 440 /etc/sudoers.d/dev
# Create .ssh directory and copy authorized_keys
sudo mkdir -p /home/dev/.ssh
sudo cp /root/.ssh/authorized_keys /home/dev/.ssh/
# Copy the lexe-agent SSH key
sudo cp /root/.ssh/lexe-agent-2026-ed25519 /home/dev/.ssh/
sudo cp /root/.ssh/lexe-agent-2026-ed25519.pub /home/dev/.ssh/
# Set ownership
sudo chown -R dev:dev /home/dev/.ssh
# Set permissions
sudo chmod 700 /home/dev/.ssh
sudo chmod 600 /home/dev/.ssh/authorized_keys
sudo chmod 600 /home/dev/.ssh/lexe-agent-2026-ed25519
# Enable lingering so systemd services can run without login:
# TODO(dev): Confirm if this is actually required
sudo loginctl enable-linger dev
```
### User setup
(Local) SSH into the VM as non-root user
```bash
ssh dev@<IP>
```
(User) Set up SSH config
```bash
# SSH setup
cat > ~/.ssh/config << 'EOF'
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/lexe-agent-2026-ed25519
IdentitiesOnly yes
EOF
chmod 600 ~/.ssh/config
```
(User) Install Claude Code
```bash
$ curl -fsSL https://claude.ai/install.sh | bash
$ echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc
# Log in to Claude
$ claude
```
(User) Complete `gh` auth
```
gh auth login
```
(User) Install happy
```bash
# Install nvm, npm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
\. "$HOME/.nvm/nvm.sh"
nvm install 24
node -v # Should print "v24.12.0".
npm -v # Should print "11.6.2".
# Install happy
npm install -g happy-coder
# Authenticate happy
happy
```
(User) Daemonize happy as `happy-dev` user service
```bash
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/happy-dev.service << 'EOF'
[Unit]
Description=Happy Claude Code Instance (dev)
After=default.target
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=%h
Environment=TERM=xterm-256color
ExecStart=/usr/bin/tmux new-session -d -s happy-dev "bash -lc 'source ~/.nvm/nvm.sh && happy --yolo'"
ExecStop=-/usr/bin/tmux kill-session -t happy-dev
[Install]
WantedBy=default.target
EOF
systemctl --user daemon-reload
systemctl --user enable happy-dev.service
systemctl --user start happy-dev.service
systemctl --user status happy-dev.service
```
(User) Setup repo
```bash
mkdir -p ~/lexe-agent
git clone
[email protected]:lexe-agent/lexe.git ~/lexe-agent/lexe
cd ~/lexe-agent/lexe
git remote add upstream
[email protected]:lexe-app/lexe.git
```
(User) Install omnara, and daemonize as `com.omnara.daemon.service` user service
```bash
curl -fsSL https://omnara.com/install/install.sh | bash
source ~/.bashrc
# Ensure systemctl service is OK
systemctl --user status com.omnara.daemon
```
(User) Init Omnara session and set up auth
```bash
cd ~/lexe-agent/lexe
omnara
```
(Optional, User) Uninstall Omnara
```bash
systemctl --user stop com.omnara.daemon
systemctl --user disable com.omnara.daemon
rm ~/.config/systemd/user/com.omnara.daemon.service
systemctl --user daemon-reload
# Optionally also delete all local data
rm -rf ~/.omnara
# Optionally remove from .bashrc
sed -i '/^# omnara$/d; /^export OMNARA_INSTALL=/d' ~/.bashrc
```