๐Ÿ  Home โ† Docs Index
Open Beta ๐Ÿ“– Interactive Reader Raw .md llms.txt

Distribution + Auto-Update

Architecture

flowchart TD
    subgraph DIST["GoClaw Distribution"]
        BIN["<b>Single Binary</b><br/>cgo: SQLite + tree-sitter"]
        DOC["<b>Docker</b><br/>Multi-stage Alpine"]
        SVC["<b>OS Service</b><br/>systemd + launchd"]
        BUILD["<b>CGO_ENABLED=1 go build -o goclaw ./cmd/gateway</b><br/>C compiler per target ยท Linux builds: glibc 2.34+"]

        BIN --> BUILD
        DOC --> BUILD
        SVC --> BUILD
    end

GoClaw needs cgo: mattn/go-sqlite3 (the database) and smacker/go-tree-sitter (the code analysis tools) are C code compiled into the binary, so a build with CGO_ENABLED=0 does not compile. The Linux release binaries link glibc dynamically and need glibc 2.34 or newer (Ubuntu 22.04, Debian 12, RHEL 9 or later); they do not run on musl (Alpine). On an older or musl system, build from source there or use the Docker image, which builds its own binary.

Distribution / Installation (Section 17)

# Method OpenClaw GoClaw Status
17.1 Single binary โ€” One binary per platform with SQLite and tree-sitter compiled in (cgo); Linux builds need glibc 2.34+ Done
17.2 From source pnpm install && pnpm build go build -o goclaw ./cmd/gateway with a C compiler; add CGO_CFLAGS="-DSQLITE_ENABLE_FTS5" as the release does, or the knowledge graph falls back from full-text search to LIKE Done
17.3 Docker Dockerfile + docker-compose โœ… Multi-stage Alpine build (cgo on musl, non-root user) + docker-compose.yaml Done
17.4 OS service install launchd/systemd/schtasks goclaw service install for systemd (Linux) and launchd (macOS); --system [--run-as <user>] for a system service run by an ordinary user Done
17.5 OS service uninstall Platform-specific goclaw service uninstall (stop + disable + remove) Done
17.6 Cross-compilation โ€” Release matrix: darwin arm64/amd64 built on macOS; linux amd64 (gcc), arm64 (aarch64-linux-gnu-gcc), armv7 (arm-linux-gnueabihf-gcc) and windows amd64 (x86_64-w64-mingw32-gcc) cross-compiled on Ubuntu 24.04 Done
17.7 npm install npm i -g openclaw (primary) โ€” TODO
17.8 Shell script (macOS/Linux) curl openclaw.ai/install.sh | bash curl -fsSL https://goclaw.ulab.mn/install.sh | sh Done
17.9 PowerShell (Windows) irm openclaw.ai/install.ps1 | iex โ€” (the Windows binary is a release asset) TODO
17.10 macOS .app / .dmg Signed app bundle with Sparkle โ€” TODO
17.11 Homebrew โ€” โ€” TODO
17.12 Go install โ€” โ€” The module is ulab.mn/agents/goclaw and its main package ./cmd/gateway, so go install would name the binary gateway; build from source instead TODO
17.13 GitHub Releases Release assets (binaries) Every tag publishes its assets to a GitHub Release, to https://goclaw.ulab.mn/releases/<version>/ and to the release bucket (https://storage.googleapis.com/goclaw-releases/releases/<version>/) Done

install.sh

curl -fsSL https://goclaw.ulab.mn/install.sh | sh installs the latest release on macOS (arm64, amd64) and Linux (amd64, arm64, armv7):

  1. Reads the version from https://goclaw.ulab.mn/releases/latest.json, falling back to https://goclaw-hosting.web.app and then to the release bucket (https://storage.googleapis.com/goclaw-releases) when the one before does not answer, and stops when none does. It downloads from the mirror that answered.
  2. Downloads goclaw-<os>-<arch> and that release's checksums.json, and stops unless the binary's SHA-256 matches (it also stops when no SHA-256 tool is found).
  3. Installs it as /usr/local/bin/goclaw through a staged file and a rename, so it replaces a running binary; it asks for sudo when the directory is not writable.
  4. Prints the next steps: goclaw setup, goclaw, goclaw service install, goclaw doctor.

Release assets

A tag vX.Y.Z runs .github/workflows/release.yml, which publishes to https://goclaw.ulab.mn/releases/<version>/ (Firebase Hosting), to a GitHub Release of the same name and, from that Release, to the release bucket gs://goclaw-releases, public at https://storage.googleapis.com/goclaw-releases/releases/<version>/:

Asset What it is
goclaw-darwin-arm64, goclaw-darwin-amd64 macOS binaries, codesigned when the signing certificate is configured
goclaw-linux-amd64, goclaw-linux-arm64, goclaw-linux-armv7 Linux binaries (glibc 2.34+)
goclaw-windows-amd64.exe Windows binary
goclaw-fleet-<version>.tar.gz The fleet toolkit (ansible/)
goclaw-<version>-linux-amd64-sbom.cdx.json, goclaw-<version>-linux-amd64-sbom.spdx.json CycloneDX 1.5 and SPDX 2.3 SBOMs read from the linux-amd64 binary (SBOM)
goclaw-<version>-vex.json OpenVEX statements from security/vex.json, stamped with the release
goclaw-<version>-govulncheck.json, goclaw-<version>-govulncheck.txt govulncheck report
checksums.json SHA-256 of every asset above

releases/latest.json ({"version": โ€ฆ, "published_at": โ€ฆ}) and releases/latest/checksums.json name the newest release; install.sh and goclaw update read them. Firebase Hosting replaces the whole site on every deploy, so a docs-only deploy (.github/workflows/deploy-site.yml) first rebuilds dist/releases/ with scripts/restore_releases.sh, from the GitHub Release (else the bucket, else the live site), checking every asset against checksums.json.

The site keeps only the newest release; the bucket keeps every version from v0.2.2 on, under the same paths (releases/<version>/โ€ฆ, releases/latest.json, releases/latest/checksums.json). A versioned file is never replaced once uploaded and is served with Cache-Control: public, max-age=31536000, immutable; the two latest files are no-cache and move only forward, so republishing an older version leaves them on the newer one.

Docker (Multi-Stage Alpine Build)

The Dockerfile, without its comments:

# Stage 1: Build
FROM golang:1.27-alpine AS builder
WORKDIR /app
RUN apk add --no-cache git build-base sqlite-dev
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=1 GOOS=linux \
    CGO_CFLAGS="-DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_JSON1" \
    go build -tags="sqlite_fts5 sqlite_json" -o goclaw ./cmd/gateway

# Stage 2: Runtime
FROM alpine:3.21
RUN apk add --no-cache ca-certificates tzdata bash sqlite-libs
RUN adduser -D -u 10001 goclaw
WORKDIR /app
COPY --from=builder /app/goclaw .
COPY --from=builder /app/web ./web
RUN touch config.yaml && chown -R goclaw:goclaw /app \
    && mkdir -p /home/goclaw/.goclaw && chown goclaw:goclaw /home/goclaw/.goclaw
USER goclaw
EXPOSE 8080
CMD ["./goclaw"]

The gateway runs as the unprivileged user goclaw (uid 10001); its state directory is /home/goclaw/.goclaw.

Docker Compose: docker-compose.yaml builds the image and runs it as goclaw-gateway on port 8080 with restart: unless-stopped. It mounts:

Set GOCLAW_SECRET_KEY under environment (the file has the line commented out). Without it the vault key is derived from the user name and home directory, which are the same in every container built from this image, so anyone with the image can derive it.

OS Service Templates

Platform Service Features
Linux systemd (goclaw.service) Restart=always, RestartSec=5, journal logging; WantedBy=default.target for a per-user unit (~/.config/systemd/user/, with lingering so it starts at boot), multi-user.target for --system (/etc/systemd/system/)
macOS launchd (goclaw.plist in ~/Library/LaunchAgents/, or /Library/LaunchDaemons/ for --system) RunAtLoad=true, KeepAlive=true, stdout/stderr to ~/Library/Logs/goclaw.stdout.log and goclaw.stderr.log

Service install auto-detects binary path via os.Executable() fallback to $PATH lookup. The systemd unit comes from goclaw's own template (systemdUnitTemplate in internal/cli/service.go): kardianos/service's writes RestartSec=120 and WantedBy=multi-user.target for every unit, and a per-user unit enabled for that target never starts at boot. goclaw doctor and the gateway's start-up log report an installed unit that will not start at boot, with the command that fixes it.


Auto-Update (Section 21)

# Feature OpenClaw GoClaw Status
21.1 CLI update command openclaw update goclaw update [--version vX.Y.Z] [--force] [--backup] Done
21.2 macOS Sparkle appcast.xml, signed updates โ€” TODO
21.3 Auto-update config update.auto.* (stableDelayHours, jitter) โ€” TODO
21.4 Control UI update "Update & Restart" button โ€” TODO
21.5 Version check npm registry / GitHub โ€” (goclaw update reads releases/latest.json when it is run; nothing checks on its own) TODO

How goclaw update works (internal/cli/cmd_update.go, internal/cli/update_backup.go):

  1. Takes the version from --version, or from https://goclaw.ulab.mn/releases/latest.json (the release bucket's when the site does not answer), and stops when that version is already running, unless --force.
  2. Downloads releases/<version>/checksums.json and the asset for this OS and architecture to a temporary file, and stops unless its SHA-256 matches. Both come from the site first and, when it does not have that version (it keeps only the newest) or its copy does not match, from the release bucket; so --version can go back to any release from v0.2.2 on.
  3. On macOS, verifies the code signature (codesign --verify --strict) when the binary is signed; an unsigned binary is installed with a warning.
  4. When --backup is passed, snapshots gc_state.sqlite and kg.sqlite (using VACUUM INTO) together with config.yaml, credentials/, workspace/, agents/, memory/, skills/, extensions/ and plugins/ into <state dir>/backups/goclaw-backup_<version>_<new-version>_<timestamp>.tar.gz, keeping the 3 most recent update archives.
  5. Renames the running binary to <binary>.prev (one step of rollback) and the new one into its place, asking for sudo when the directory (such as /usr/local/bin) is not writable.
  6. Stops and starts the installed service: the system service when /etc/systemd/system/goclaw.service or /Library/LaunchDaemons/goclaw.plist exists, else the per-user one (running as $SUDO_USER when goclaw update was invoked through sudo). When that fails it says to run goclaw service restart (or sudo goclaw service restart for a system service).

The fleet toolkit's make upgrade runs it on each host (Upgrade and restart).


OpenClaw vs GoClaw Distribution Comparison

Aspect OpenClaw GoClaw
Primary install npm (requires Node.js) Single binary (install.sh or a release asset)
Runtime deps Node.js 22+ glibc 2.34+ on Linux; nothing else (SQLite and tree-sitter compiled in with cgo)
Docker Dockerfile โœ… Multi-stage Alpine (non-root; state in a named volume)
OS service launchd/systemd/schtasks systemd + launchd (auto-detect binary)
Cross-compile Platform packages darwin arm64/amd64, linux amd64/arm64/armv7, windows amd64, each with its C compiler
Service features โ€” auto-restart, journal/file logging, KeepAlive
macOS app Signed .app with Sparkle โ€” (TODO)
Auto-update Sparkle + npm goclaw update (run by hand or by the fleet toolkit)
Install methods 6+ install.sh, release binaries, Docker, source

Not implemented: npm, a PowerShell installer, a macOS app, Homebrew, go install, and automatic update checks.