Calamoose Labs Presents
Robomoose 🫎 is a self-hosted, personal AI orchestration assistant. It runs as
a NixOS systemd service alongside antlers'
vibe-server, giving you a persistent chat companion (desktop + mobile web UI)
that you brainstorm and delegate with. Robomoose answers via one-shot claude -p
calls streamed token-by-token, spawns and monitors vibe sessions (Claude Code
instances) through vibe-server's API, and keeps a durable memory of your todos,
theories, and outstanding tasks.
It is a coordinator and memory layer, not a remote keyboard: it spawns the right session, tells you exactly what to say to it from claude.ai / the Claude mobile app, tracks the task as outstanding, and watches session state live.
Built on Deno with zero external imports — only Deno.* and Web-platform
globals — so the deno compile step needs no network and builds reproducibly in
the sandbox (a cold build fetches only the pinned denort runtime).
- Streaming web chat — a persistent SSE connection streams robomoose's reply token-by-token; user messages right, robomoose left with a 🫎 avatar.
- Session orchestration — robomoose emits
<dispatch>blocks to spawn / kill vibe sessions; each spawn drops an inline "open in claude.ai →" card and records an outstanding task. - Persistent memory — todos (checkable inline), theories, and outstanding
tasks, stored as flat JSON with serialized, corruption-safe writes. Robomoose
updates them itself via
<memory>blocks. - Live monitoring — a background poller reflects session status/state/token changes in the sidebar within 5s, reconciles finished tasks, and shows an "unreachable" indicator (recovering automatically) when vibe-server is down.
- Plan-usage panel — a colour-coded usage bar; robomoose proactively warns you when usage crosses 80%.
- Log tail — click a session to stream its live log in a panel.
- Installable PWA — dark theme, mobile bottom-sheet layout, manifest + icons
(installable from
localhostor behind TLS). - Shared-password auth — an HMAC-signed cookie (or fully passwordless on a trusted host).
Browser ──SSE / POST──▶ robomoose (Deno.serve :8421)
├─ brain : `claude -p` per turn, history in the prompt
├─ memory : todos / theories / outstanding JSON + ROBOMOOSE.md
├─ monitor : polls sessions (5s) + usage (60s)
└─ VibeClient ──HTTP──▶ vibe-server (:8420)
GET /api/presets · GET /api/sessions
POST /api/sessions {preset} · DELETE /api/sessions/:id
GET /api/sessions/:id/logs (SSE) · GET /api/usage
The two services share one host, the vibe user, and one CLAUDE_CONFIG_DIR
(the subscription OAuth login), so robomoose's claude -p calls and vibe-server's
sessions bill the same plan.
One persistent EventSource per browser tab carries every async event:
| Event | Payload | Meaning |
|---|---|---|
token |
{text} |
a streamed chunk of robomoose's reply |
done |
{dispatches} |
the turn finished; render dispatch chips |
vibe_spawned |
{preset, sessionId, name, directories} |
a session was spawned |
vibe_update |
{sessionId, name, state, tokens, status} |
a session changed |
state |
{todos, theories, outstanding, presets, sessions, usage, vibeReachable} |
full sidebar refresh |
error |
{message} |
a turn or dispatch failed |
Add both flakes as inputs and import their modules. Run robomoose next to
vibe-server, sharing the vibe user and the Claude config dir:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
antlers.url = "github:CalamooseLabs/antlers";
robomoose.url = "github:CalamooseLabs/robomoose";
};
outputs = { nixpkgs, antlers, robomoose, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
antlers.nixosModules.vibe # programs.vibe — the launcher + presets
antlers.nixosModules.vibe-server # services.vibe-server
robomoose.nixosModules.robomoose # services.robomoose
./configuration.nix
];
};
};
}# configuration.nix
{
# The vibe launcher: define presets robomoose can route work to.
programs.vibe.presets.api = {
directories = [ "/srv/api" ];
branch = "main";
};
services.vibe-server = {
enable = true;
passwordFile = "/run/secrets/vibe-password";
claudeConfigDir = "/var/lib/vibe/claude"; # the shared subscription login
};
services.robomoose = {
enable = true;
passwordFile = "/run/secrets/robomoose-password";
vibeServerPasswordFile = "/run/secrets/vibe-password"; # same file vibe-server uses
claudeConfigDir = "/var/lib/vibe/claude"; # share one login
# openFirewall = true; localNetworkOnly = true; # LAN access
};
}Authenticate the vibe user to Claude once so both services share the login.
Easiest is vibe-server's "Log in to Claude" banner (it targets the shared config
dir). To do it from a shell, point CLAUDE_CONFIG_DIR at that same dir:
sudo -u vibe env CLAUDE_CONFIG_DIR=/var/lib/vibe/claude claude # then /loginThen browse to http://myhost:8421.
| Option | Default | Purpose |
|---|---|---|
port / hostname |
8421 / 0.0.0.0 |
listen address |
passwordFile |
null |
shared login password (null ⇒ passwordless) |
vibeServerUrl |
http://127.0.0.1:8420 |
vibe-server API base |
vibeServerPasswordFile |
null |
vibe-server login password |
model |
claude-opus-4 |
model for robomoose's own replies |
claudeConfigDir |
<stateDir>/.claude |
Claude OAuth login dir (share with vibe-server) |
stateDir / memoryDir |
/var/lib/robomoose / …/memory |
runtime state |
user / group |
vibe / vibe |
service identity (shared, auto-created) |
openFirewall / localNetworkOnly |
false |
firewall exposure |
requireVibeServer |
false |
hard-couple to vibe-server (off — robomoose survives it being down) |
enableNixLd |
true |
needed for the compiled Deno ELF |
The vibe user, CLAUDE_CONFIG_DIR passthrough, and a hardened systemd sandbox
(ProtectSystem=strict, NoNewPrivileges, PrivateTmp, scoped ReadWritePaths)
are configured for you. Edit ROBOMOOSE.md in the memory dir to give robomoose
your infrastructure knowledge and routing rules.
This repo doubles as a vibe-shell
dev environment. direnv allow (or nix develop) puts deno, vibe, git,
gh, and the commit/wiki helpers on PATH.
deno task dev # run with --watch (defaults when $ROBOMOOSE_CONFIG is unset)
deno task test # the full unit + integration suite
deno task compile # deno compile → ./robomoose
nix build .#robomoose # the packaged binary
nix flake check # builds the package + runs deno test + evaluates the module
Commits here are cryptographically signed by a human key — see CLAUDE.md for the
gcommit workflow.
Robomoose is open-source software licensed under the MIT License.

