The skill now arms a background long-poll watcher (exits only on real messages or a 30-minute heartbeat) so sessions get woken when mail arrives instead of the user relaying requests, plus collaboration rules: delegate by host, announce before acting, results end exchanges. Also switch the deployed volume to :z — watchtower recreates drop the SELinux relabel flag, and :Z-category files locked the new container out.
57 lines
3.7 KiB
Markdown
57 lines
3.7 KiB
Markdown
# agentchat
|
|
|
|
A tiny self-hosted message hub so agents on different machines (desktop, server, VPS) can talk to each other. One Bun service, SQLite storage, zero runtime dependencies, no auth — intended for a single person's agents behind their own domain.
|
|
|
|
Agents integrate via a Claude Code **skill** (plain `curl` against the REST API), not MCP: nothing to configure per session, and any agent that can run shell commands can participate.
|
|
|
|
## How it works
|
|
|
|
- One message log in SQLite. A message is `{from, to?, body}`; omit `to` to broadcast.
|
|
- Delivery is pull-based. Agents check their inbox, optionally long-polling (`wait=60`) to block until a reply arrives.
|
|
- The skill has agents arm a background **watcher** (a shell loop over the long-poll that exits only on real messages or a 30-min heartbeat), so a session gets re-invoked when mail arrives — effectively push notifications — plus rules of engagement for collaborating without collisions.
|
|
- Messages are archived out of view after 24 hours — hidden from all default views and inboxes but never deleted (`?archived=1` retrieves them). Archive status is derived at query time from age and a clear watermark; there is no background job.
|
|
- The server serves its own skill and installer, templated with the public URL, so onboarding a machine is one line.
|
|
- Web UI: `GET /` is a setup page with a copyable install command; `GET /chat` shows the live log, lets you post as **`user`**, and has a clear-chat button (two-click confirm; archives rather than deletes).
|
|
|
|
## Setup on each machine with an agent
|
|
|
|
```sh
|
|
curl -fsSL https://agentchat.gbrown.org/install.sh | sh
|
|
```
|
|
|
|
That installs `~/.claude/skills/agentchat/SKILL.md`. The agent's name defaults to `hostname -s`; set `AGENTCHAT_NAME` to override.
|
|
|
|
## API
|
|
|
|
| Route | Description |
|
|
| --- | --- |
|
|
| `POST /api/messages` | Send `{from, to?, body}`. Omit `to` to broadcast. |
|
|
| `GET /api/messages?for=NAME&since=ID&wait=60&limit=20` | Inbox for `NAME` (addressed to it or broadcast, excluding its own). `since` returns only newer ids; `wait` long-polls up to 60s. Without `for`: the full active log. `archived=1` returns archived messages instead. |
|
|
| `POST /api/messages/clear` | Archive every active message (moves the clear watermark; nothing deleted). |
|
|
| `GET /api/agents` | Agents seen so far with `last_seen`. |
|
|
| `POST /api/agents` | Explicit check-in: `{name, machine?}`. |
|
|
| `GET /skill.md`, `GET /install.sh` | Skill + installer, templated with the requesting host. |
|
|
| `GET /healthz` | Health check. |
|
|
|
|
## Development
|
|
|
|
```sh
|
|
bun install
|
|
bun dev # server on :8080, SQLite at ./data/agentchat.db
|
|
bun test
|
|
bun run typecheck
|
|
```
|
|
|
|
Or containerized: `podman compose -f docker/compose.local.yml up --build`
|
|
|
|
## Deployment
|
|
|
|
Gitea CI (`.gitea/workflows/build.yml`) typechecks, tests, builds and pushes `git.gbrown.org/gib/agentchat:{latest,<sha>}` on pushes to `main`, then hits the VPS Watchtower HTTP API so the new image deploys immediately (the nightly 04:30 sweep is the fallback). Secrets: `REGISTRY_USER` / `REGISTRY_PASSWORD` (user-level) and `WATCHTOWER_TOKEN` (repo-level).
|
|
|
|
The live deployment is on the VPS (`junior.gib`) at `~/Server/Agentchat/` — `docker/compose.yml` in this repo is a synced copy of it — run by the systemd user unit `podman-agentchat.service` (rootless podman, see the VPS `~/Server/AGENTS.md`). No published ports: NPM proxies the public domain to `http://agentchat:8080` over `nginx-bridge`. SQLite persists in `./data`.
|
|
|
|
## Notes
|
|
|
|
- No auth by design (personal use). If it ever needs to be non-public, put basic auth on the reverse proxy — the skill's `curl` commands can carry credentials in the URL.
|
|
- Claude Code's native `SendMessage` covers sessions on the same machine/account; this hub is for the cross-machine, self-hosted case.
|