Teams adopting AI-assisted development often face fragmented workflows across competing tools (Claude Code, Codex, Cursor, etc.). Coordination requires manual handoffs, context switching, and duplicate effort when organizing agents for sustained workstreams. There is no standardized framework for building hierarchical agent organizations that maintain session continuity and task ownership across a project lifecycle.
What This Does
OpenGoat provides a platform for constructing hierarchical organizations of OpenClaw agents that coordinate work across multiple LLM tools. The repository contains 1263 files with a TypeScript/React frontend (738 TS, 221 TSX) built with Tauri for the desktop UI, and Rust backend components. The source organizes into packages/ (437 files), apps/desktop/ (423 files), and test/ (201 files). Skill definitions live in .agents/skills/ai-elements/ with 68 reference markdown files and 97 script components handling UI patterns like chain-of-thought, confirmation flows, and task management. The CLI entry point is ./bin/opengoat exposed through package.json scripts. Configuration persists in ~/.opengoat/config.json with a defaultAgent field.
How It Is Wired
Execution flows through the Tauri desktop app at apps/desktop/src-tauri/src/lib.rs and main.rs, which launches the React UI served from apps/desktop/src/app/App.tsx. Tauri commands are defined in apps/desktop/src-tauri/src/commands/ (bootstrap.rs, chat.rs, mod.rs) and bridge to the Rust runtime. The CLI binary opengoat is built from the root package.json and bin/ entry point. Agent management commands (opengoat agent create, opengoat agent list) route through the Tauri command layer to the Rust backend. Session continuity uses --session flags passed to agent commands, persisting state in the workspace shim. The changeset workflow at .changeset/ handles version bumps and publishes. The CI pipeline at .github/workflows/ci.yml gates on pnpm build and test execution.
Configuration: Required environment variable OPENGOAT_DEFAULT_AGENT or config entry defaultAgent in ~/.opengoat/config.json. The Tauri config lives at apps/desktop/src-tauri/tauri.conf.json.
Running it: Start the UI server with opengoat start (binds to 127.0.0.1:19123). For initial project provisioning: opengoat project create <url> provisions a CMO agent workspace. Agent creation: opengoat agent create "CTO" --manager --reports-to goat. Runtime override: export OPENGOAT_DEFAULT_AGENT=stone.
Real-World Use
A project lead runs opengoat project create https://myproject.com to bootstrap a CMO agent with browser-skills. They then create opengoat agent create "Engineer" --individual --reports-to cto --skill coding and opengoat agent create "Designer" --individual --reports-to cto. Work is dispatched via opengoat agent cto --message "Plan the Q2 roadmap" and opengoat agent engineer --message "Implement auth middleware". Session continuity allows follow-up: opengoat agent goat --session saaslib-planning --message "Now draft the changelog".
Code Health & Issues
Static analysis found 388 test files and 48 documentation files across the repo. The dependency graph includes package.json (pnpm), Cargo.toml (Rust), and Dockerfile. No structural red flags were detected: tests, CI, license, and lockfile are present where expected. The .husky/pre-commit hook and .npmrc are configured. No missing license or committed secrets were observed in the file structure.
The Bottom Line
OpenGoat delivers a functional framework for orchestrating multi-tool AI agent organizations with a working desktop UI and CLI. The Tauri/React architecture is sound, the skill taxonomy is extensive, and the installation path is straightforward. It is best suited for teams that need persistent agent hierarchies across Claude Code, Codex, or Cursor workflows and are comfortable configuring the Rust-backed runtime. Teams looking for a zero-config, purely JavaScript-based agent framework may find the Tauri dependency unnecessary.
What the analyser found
Deployment readiness
7/7
✓Container image
✓CI pipeline
✓Lockfile committed
✓Test suite
✓README
✓License
✓No committed secrets
Composition
1,263 files
TypeScript738
TSX221
Markdown187
JSON27
Rust8
YAML7
ReactDocker
Architecture
Top-level areas of the codebase, sized by module count. Arrows show how many imports cross from one area into another.
Ranked by severity × confidence × production reach. Reach is the honest discriminator across a collection that is mostly other people's code: the same finding matters more in something that ships.
high5
medium6
low2
Pin third-party GitHub Actions to a commit SHAhigh4 occurrences
A tag can be moved, so the action running with your token and secrets is whatever its owner last pushed; this is how tj-actions/changed-files leaked secrets from thousands of repos.
Fix: Replace each @vN with the 40-character commit SHA, keep # vN as a comment, and let Dependabot bump the SHAs.
Upgrade the pinned dependency carrying a published advisoryhigh
tauri@2.10.3 CVE-2026-42184 (medium)
The pinned version is the one that installs, so a published advisory against it describes this deployment rather than a hypothetical one, and the advisory is also a public description of how to exploit it.
Fix: Upgrade to the fixed version named in the advisory, then commit the lockfile.
Drop privileged mode and host networkinghigh
docker-compose.yml
privileged: true
privileged hands the container every capability including direct device access, which makes escaping to the host a documented one-liner, and host networking removes the network boundary so every port the container opens is open on the host.
Fix: Grant the specific capability the workload needs with cap_add, and publish ports explicitly.
Open a pull request instead of pushing to the default branchhigh
.github/workflows/desktop-release.yml
git push origin "HEAD:$RELEASE_BRANCH"
Automated commits land on the branch that deploys, with no test having run against the result.
Fix: Push to a bot branch and open a pull request, or restrict the push to a tag ref.
Stop passing the secret as a build argument or image envhigh
Dockerfile
ENV OPENCLAW_GATEWAY_TOKEN with a baked value
ARG and ENV values are recorded in the image layers, so the credential is readable with docker history by anyone who can pull the image, and deleting the file in a later layer does not remove it.
Fix: Mount it at build time with --mount=type=secret, or inject it at run time.
Declare least-privilege permissions for GITHUB_TOKENmedium
.github/workflows/ci.yml
1 workflow(s) declare no permissions
With no declaration the token inherits the repository default, so any injected step can push commits or mint releases from inside your own CI.
Fix: Add permissions: contents: read at the top of the workflow and widen per job only where needed.
Enable Dependabot or Renovatemedium
8 manifest(s), no update bot configured
Without a bot a published advisory sits unpatched until someone audits by hand, which across 1,322 repositories means never.
Fix: Commit .github/dependabot.yml covering the repo ecosystems plus github-actions.
Pin the container base image by digestmedium2 occurrences
Dockerfile
node:22, node:22
An untagged or mutable base means today's build and last month's contain different libc and a different CVE set, with no record of which shipped.
Fix: Use image:tag@sha256:<digest> and enable Dependabot's docker ecosystem.
Gate pull requests on a dependency vulnerability scanmedium
.github/workflows
no dependency scan in CI
This is the one gate that would catch a known-vulnerable package before it reaches a build, and no repository in the sample had it.
Fix: Add dependency-review-action on pull_request, or osv-scanner on push and a schedule.
Set persist-credentials: false on checkoutmedium
.github/workflows/ci.yml
checkout keeps the token, then dependencies are installed
The token stays in .git/config for every later step, so a malicious postinstall script reads a pushable credential without one ever being passed to it.
Fix: Add with: persist-credentials: false, and pass an explicit token only to the step that pushes.
Add a non-root USER to the imagemedium
Dockerfile
CMD or ENTRYPOINT with no USER directive
A process running as root in the container is root against every mounted volume, and it turns any container escape or writable-mount mistake from a contained problem into a host one.
Fix: Create an unprivileged user, chown what it needs, and end the Dockerfile with USER.
Set timeout-minutes on the workflow jobslow3 occurrences
.github/workflows/ci.yml
3 workflow(s) declare no job timeout
A wedged step runs to the six-hour platform default, which on a two-hourly schedule means three runs overlap behind it.
Fix: Add timeout-minutes with a realistic bound to each job.
1 further finding(s) are listed in the full report.
Checked deterministically against the repository tree and a bounded set of its files: committed credentials, unpinned actions and base images, missing lockfiles and update bots, workflows that discard failures, published advisories against the declared dependencies, runtime configuration, licensing and notebook reproducibility. No language model is involved in this section.