# Gantry **Onboard AI employees like real ones.** Give them a seat in Teams or Slack, only the access they need, a full audit trail, and offboarding in one command. Self-hosted, any model. Gantry is the open-source runtime that lets IT onboard AI agents the way it onboards people — provision a seat in the channels where the team already works (or inside your own product via SDK and API), grant scoped access, log every action, revoke in one command. It gives agents a controlled host process, durable state, approved tools, channel adapters, memory, and audit records without tying application code to one model provider or chat surface. Gantry is not a chatbot wrapper and not a personal assistant that staff install on their own laptops. It is the runtime boundary between: - human channels such as Slack, Microsoft Teams, Telegram, Discord, and web/SDK clients; - application events, SDK calls, and scheduled jobs; - approved tools, local CLIs, browser automation, skills, and MCP servers; - Postgres-backed runtime state, artifacts, settings, credentials, and audit. What that buys a team in practice: - **Governed autonomy.** Capability grants, declarative per-agent `tool_rules`, an optional LLM auto-permission mode that relieves prompt fatigue without ever writing policy by itself, and one-tap durable approvals — every decision audited. - **Fleet operations.** Multiple agents across conversations and channels, scheduled jobs with delivery guarantees, sandboxed worker and lightweight inline runtimes, and versioned desired-state settings. - **Developer surface.** A typed Node SDK generated from the OpenAPI doc, lifecycle webhooks, a `/v1/usage` API, a provider-shaped direct LLM API, and runnable NestJS/Next.js examples. - **Institutional memory.** Per-agent and shared memory with review flows, so what agents learn stays inspectable and survives model or provider swaps. ## Detail Documentation - [Product brief](docs/product/BRIEF.md) - [Architecture docs](docs/architecture/README.md) - [Decision records](docs/decisions/README.md) - [SDK docs](docs/sdk/overview.md) - [Deployment docs](docs/deployment/aws-terraform.md) ## Status This repository is being prepared as an open-source project. The intended npm package shape is: - `@gantry/runtime` for the runtime and `gantry` CLI binary - `@gantry/sdk` for the Node.js SDK - `@gantry/contracts` for shared TypeScript contracts Until the first public package publish, build and run from source. ## Requirements - Node.js `>=24 <26` - npm - Postgres for runtime state - `ripgrep` - For `sandbox_runtime` on Linux: `bubblewrap` and `socat` Ubuntu prerequisites: ```bash sudo apt update sudo apt install -y ca-certificates curl git libatomic1 bubblewrap socat ripgrep ``` ## Quick Start From Source ```bash git clone https://github.com/knacklabs/Agent.Gantry.git cd Agent.Gantry npm ci npm run dev ``` Use Node 24 and npm (not pnpm). `dev` starts/reuses local Postgres, applies all migrations, runs core from TypeScript and serves the Web UI through Vite with hot reload. The SDK is a client library, not another process. The ready UI link is printed in the terminal. No built `dist/ui` assets are used. Source-local commands: ```bash npm run dev # start the complete source development stack npm run dev:doctor # verify Node, Docker and Docker Compose without changing state npm run reset:db # reset database schemas, preserve files, restart npm run reset # reset database and known runtime state, restart onboarding npm run reset:only # reset database and known runtime state, then exit npm run dev:stop # stop core, Vite and this home's managed Postgres npm run dev:core # core only (requires an already configured database) ``` The equivalent CLI commands are `gantry local start`, `gantry local doctor`, `gantry local reset-db`, `gantry local reset`, `gantry local reset --no-start`, and `gantry local stop`, when the CLI is built/linked from this checkout. Without a build, use `npm run cli:dev -- local ` after `npm run build:contracts`. Run these from the source checkout. Local development defaults to `/.gantry`, **not** `/gantry` or `~/gantry`. Its `.env` is created securely there with local defaults and a generated encryption key. Existing values are preserved. An exported `GANTRY_HOME` or `--runtime-home` overrides the default directly: ```bash export GANTRY_HOME="/absolute/path/to/my-gantry-dev" npm run dev ``` Environment variables override `/.env`. Set `GANTRY_CONTROL_HOST` (loopback only) and `GANTRY_CONTROL_PORT` (default `3939`) to choose the public UI origin. Existing `authentication.canonical_origin` must match it; startup prints the exact correction if it does not. After each healthy local start or reset, startup prints a fresh short-lived one-time authorization link; existing browser sessions remain valid. If link issuance fails, run `gantry ui authorize` to retry. Reset commands run **without confirmation** and print the target first. Only a loopback database named `gantry` may be reset. `reset:db` recreates the `gantry` and `pgboss` schemas and reapplies every migration. Full reset also removes settings, onboarding state, agents, data, store, logs, artifacts and runtime projections. Both preserve `.env`, `postgres/` and unknown files. Stop other database clients before resetting. Ctrl-C stops core and Vite but keeps Postgres warm. `dev:stop` also stops the verified home-owned `gantry-postgres` container, without deleting its data. Custom databases and unrelated containers are never stopped. Docker must be running for Gantry to manage its default Postgres container; an unreachable custom URL is reported rather than replaced. Production/bundled startup remains unchanged: ```bash npm run build npm start ``` ## Runtime Configuration Runtime home defaults to `~/gantry` unless `GANTRY_HOME` or `--runtime-home` is set. Human-readable settings live in `~/gantry/settings.yaml`; the durable desired-state authority is the latest Postgres `settings_revisions` row. Runtime secrets belong in `/.env` or a runtime secret provider. Model/provider credentials must be configured through Gantry credentials and the Gantry Model Gateway. Do not pass raw model provider keys directly to agent processes. Common local commands: ```bash gantry setup gantry doctor gantry status gantry start gantry stop gantry restart gantry logs gantry model list gantry provider list gantry settings validate ``` Bundled runtime surfaces: - `/commands` is host-managed command help, not an SDK skill folder. - `gantry-admin` is the maintainer skill for local runtime administration. ## Execution Surfaces Gantry exposes three ways to run model work, all under the same permission, credential, and audit authority: - **Worker agents** (`runtime: worker`, the default) run in a sandboxed subprocess with the full reviewed capability projection: filesystem, shell, skills, browser automation, and stdio MCP servers. - **Inline agents** (`runtime: inline`) run the provider loop in the Gantry host process for lightweight chat-style workloads: core tools, remote `http`/`sse` MCP servers, subagent delegation, scheduled jobs, per-agent turn caps, and per-message structured output (`response_schema`). - **Direct LLM API** (`POST /llm/v1/messages`, `POST /llm/v1/chat/completions`) is a provider-shaped passthrough for raw model calls with Gantry-held credentials — point an official Anthropic or OpenAI SDK at Gantry via its `baseURL` option and authenticate with a Control API key holding `llm:invoke`. See [docs/architecture/capability-management.md](docs/architecture/capability-management.md) for runtime tiers and the LLM API contract. ## SDK The Node SDK talks to the Control API over a Unix socket or HTTP endpoint. ```bash npm i @gantry/sdk ``` ```ts import { createClient } from '@gantry/sdk'; const client = createClient({ apiKey: process.env.GANTRY_CONTROL_API_KEY!, baseUrl: 'http://127.0.0.1:3939', }); const health = await client.health(); console.log(health.status, health.processRole); ``` See [packages/sdk](packages/sdk/README.md) and [examples/control-api-local](examples/control-api-local/README.md). ## Security Model Gantry treats model output, provider SDKs, workers, browser backends, MCP servers, and local tools as untrusted execution surfaces. Risky tool execution passes through Gantry-owned permission policy, sandbox policy, credential brokering, and audit. Public deployments should expose only the routes they need: - `/v1/*` is the authenticated Control API and should be restricted to trusted admin or application networks. - `/webhooks/*` is for provider webhook ingress when enabled. - `/healthz`, `/readyz`, and `/metrics` are operational endpoints and must stay internal. For details, read [docs/SECURITY.md](docs/SECURITY.md) and [docs/architecture](docs/architecture/README.md). ## Development Tests and harnesses live outside production source trees: - `apps/core/test/unit/**` - `apps/core/test/integration/**` - `apps/core/test/e2e/**` - `apps/core/test/harness/**` - `packages/contracts/test/unit/**` Common checks: ```bash npm run format:check npm run lint npm run typecheck npm test npm run build npm run security:package ``` Postgres-backed integration tests require a disposable Postgres database via `GANTRY_TEST_DATABASE_URL`; do not run them against a developer's persistent runtime database. ### Postgres migrations Change the Drizzle schema, then generate a timestamped migration and review its SQL before committing: ```bash npm run db:migrations:generate -- --name add_feature ``` For data migrations or SQL Drizzle cannot generate, use a custom migration: ```bash npm run db:migrations:custom -- --name backfill_feature ``` Run `npm run db:migrations:check` locally. After rebasing overlapping schema work, regenerate and resolve any Drizzle conflicts. CI also verifies that a clean schema produces no uncommitted migration files. Runtime deployments continue to apply migrations with `npm run db:migrate`. ## Contributing Read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request. Keep changes small, current-behavior focused, and backed by the smallest relevant checks. ## License MIT. See [LICENSE](LICENSE). ## Working in this repo — Forge This repo runs on [Forge](https://github.com/knacklabs/symphony-forge) v1, which takes each change from an approved plan to a merged pull request. Open an agent session (Claude Code or Codex) in the repo root, then: - **Run `forge next` whenever you are unsure.** It says where things stand and gives the exact next command. - **Changes arrive as stories or fixes.** A story is one short doc that gets one cold read and one human approval, then runs task by task; a small change is a fix. `AGENTS.md` has the rules, `forge.toml` the settings. - **Humans approve and merge.** Agents plan, build and review; approving a story and merging its pull requests stay with people. The old copied-in Forge is history: see [0002 Symphony Forge adoption](docs/decisions/0002-symphony-forge-adoption.md) and [0093 client sign-off gate](docs/decisions/0093-client-signoff-is-a-pinned-project-gate.md), both superseded.