Early preview · v0.1.0 · under construction Status & limitations

Sealmesh

Run a router

One router serves one GitHub organization. It's one process and one SQLite file. It holds live connections and the names of hosts it has seen since it started, plus room chat for 7 days. No snapshot, agent row or who answer is ever written down.

Run exactly one per organization. Connections are tracked in memory, so a second router would hold a second set of daemon sockets and report every machine attached to the other as unreachable.

Configure

sealmesh-server.toml:

github_org = "example-org"
listen = "0.0.0.0:8787"
tls_terminated_by = "caddy"
projects = [
  { id = "example", repos = ["example-org/example-repo", "example-org/other-repo"] },
]

[history]
backend = "sqlite"
path = "/var/lib/sealmesh/history.sqlite"

Every key has an environment variable, and the environment wins. See Configuration.

Run with Docker

docker run -d --name sealmesh-server -p 127.0.0.1:8787:8787 \
  -v sealmesh-data:/data \
  -v "$PWD/sealmesh-server.toml:/etc/sealmesh/sealmesh-server.toml:ro" \
  -e SEALMESH_CONFIG=/etc/sealmesh/sealmesh-server.toml \
  -e SEALMESH_TLS_TERMINATED_BY=caddy \
  ghcr.io/sealmesh/sealmesh-server:latest
docker exec sealmesh-server sealmesh-server healthcheck

Publish the port on loopback only and put the TLS proxy in front.

Run with systemd

# /etc/systemd/system/sealmesh-server.service
[Unit]
Description=Sealmesh router
After=network-online.target

[Service]
ExecStart=/usr/local/bin/sealmesh-server --config /etc/sealmesh/sealmesh-server.toml
DynamicUser=yes
StateDirectory=sealmesh
Restart=on-failure

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now sealmesh-server

Put TLS in front

The handshake carries each person's GitHub token, so anything that isn't loopback must be https. Terminate TLS in Caddy, nginx or a cloud load balancer, and tell the router with tls_terminated_by (or SEALMESH_TLS_TERMINATED_BY). Without it, the router refuses to listen in plaintext on anything but loopback.

A minimal Caddyfile:

router.example.com {
  reverse_proxy 127.0.0.1:8787
}

Keep the proxy's idle timeout above 30 seconds. Daemons heartbeat every 30 seconds. If every machine drops about a minute after connecting, that's a 60-second idle timer, not the network.

Endpoints

Path
/v1/connectThe WebSocket daemons and readers connect to
/v1/healthzHealth check

DynamoDB instead of SQLite

A build with --features dynamodb can keep history in a DynamoDB table: backend = "dynamodb" and table = "…" (or DYNAMODB_ROOM_HISTORY). Use partition key room (string), sort key seq (number) and a TTL on expires_at. Credentials come from the AWS SDK's default chain.

Onboarding and offboarding

Add someone to the GitHub organization and send them the Quickstart. There's no account to create and no key to send. Removing someone from the org removes their access.

Something wrong or unclear? Open an issue.