Initial commit — fireclaw multi-agent system

Firecracker microVM-based multi-agent system with IRC orchestration and local LLMs.

Features:
- Ephemeral command runner with VM snapshots (~1.1s)
- Multi-agent orchestration via overseer IRC bot
- 5 agent templates (worker, coder, researcher, quick, creative)
- Tool access (shell + podman containers inside VMs)
- Persistent workspace + memory system (MEMORY.md pattern)
- Agent hot-reload (model/persona swap via SSH + SIGHUP)
- Non-root agents, graceful shutdown, crash recovery
- Agent-to-agent communication via IRC
- DM support, /invite support
- Systemd service, 20 regression tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-07 13:28:29 +00:00
commit ff694d12f6
28 changed files with 5917 additions and 0 deletions

165
src/network.ts Normal file
View File

@@ -0,0 +1,165 @@
import { execFileSync } from "node:child_process";
import { openSync, closeSync, readFileSync, writeFileSync } from "node:fs";
import { CONFIG } from "./config.js";
function run(cmd: string, args: string[]) {
execFileSync(cmd, args, { stdio: "pipe" });
}
function sudo(args: string[]) {
run("sudo", args);
}
export function ensureBridge() {
try {
execFileSync("ip", ["link", "show", CONFIG.bridge.name], {
stdio: "pipe",
});
} catch {
sudo(["ip", "link", "add", CONFIG.bridge.name, "type", "bridge"]);
sudo([
"ip",
"addr",
"add",
`${CONFIG.bridge.ip}/24`,
"dev",
CONFIG.bridge.name,
]);
sudo(["ip", "link", "set", CONFIG.bridge.name, "up"]);
sudo(["sysctl", "-w", "net.ipv4.ip_forward=1"]);
}
}
export function ensureNat() {
// Check if rule already exists
try {
execFileSync(
"sudo",
[
"iptables",
"-t",
"nat",
"-C",
"POSTROUTING",
"-s",
CONFIG.bridge.subnet,
"-j",
"MASQUERADE",
],
{ stdio: "pipe" }
);
} catch {
// Find the default route interface
const routeOut = execFileSync("ip", ["route", "show", "default"], {
encoding: "utf-8",
});
const extIface = routeOut.match(/dev\s+(\S+)/)?.[1] ?? "eno2";
sudo([
"iptables",
"-t",
"nat",
"-A",
"POSTROUTING",
"-s",
CONFIG.bridge.subnet,
"-o",
extIface,
"-j",
"MASQUERADE",
]);
sudo([
"iptables",
"-A",
"FORWARD",
"-i",
CONFIG.bridge.name,
"-o",
extIface,
"-j",
"ACCEPT",
]);
sudo([
"iptables",
"-A",
"FORWARD",
"-i",
extIface,
"-o",
CONFIG.bridge.name,
"-m",
"state",
"--state",
"RELATED,ESTABLISHED",
"-j",
"ACCEPT",
]);
}
}
export function createTap(tapName: string) {
sudo(["ip", "tuntap", "add", tapName, "mode", "tap"]);
sudo(["ip", "link", "set", tapName, "master", CONFIG.bridge.name]);
sudo(["ip", "link", "set", tapName, "up"]);
}
export function deleteTap(tapName: string) {
try {
sudo(["ip", "tuntap", "del", tapName, "mode", "tap"]);
} catch {
// Already gone
}
}
export function macFromOctet(octet: number): string {
return `AA:FC:00:00:00:${octet.toString(16).padStart(2, "0").toUpperCase()}`;
}
interface IpPool {
allocated: number[];
}
function readPool(): IpPool {
try {
return JSON.parse(readFileSync(CONFIG.ipPoolFile, "utf-8"));
} catch {
return { allocated: [] };
}
}
function writePool(pool: IpPool) {
writeFileSync(CONFIG.ipPoolFile, JSON.stringify(pool));
}
export function allocateIp(): { ip: string; octet: number } {
const fd = openSync(CONFIG.ipPoolLock, "w");
try {
// Simple flock via child process
const pool = readPool();
for (
let octet = CONFIG.bridge.minHost;
octet <= CONFIG.bridge.maxHost;
octet++
) {
if (!pool.allocated.includes(octet)) {
pool.allocated.push(octet);
writePool(pool);
return { ip: `${CONFIG.bridge.prefix}.${octet}`, octet };
}
}
throw new Error("No free IPs in pool");
} finally {
closeSync(fd);
}
}
export function releaseIp(octet: number) {
const fd = openSync(CONFIG.ipPoolLock, "w");
try {
const pool = readPool();
pool.allocated = pool.allocated.filter((o) => o !== octet);
writePool(pool);
} finally {
closeSync(fd);
}
}