Shipwright Harness

The Agent

The Shipwright agent (artifact C) is a thin autonomous runner: it picks the next ready task from the queue, builds it, ships a PR, and forwards metrics — all without manual intervention. It has a Prisma-backed PostgreSQL store and four HTTP surfaces: a machine-polled runtime API, a human-facing admin CRUD API, a server-rendered admin UI, and a public read-only task board.

Run modes

There are three ways to run the agent depending on your context:

ModeEntry pointTransportWhen to use
Pi / bare-metalagent/src/index.tsSlack Socket ModeRunning directly on a host with a local .env file
K8s containeragent/src/entrypoint-main.tsSlack Socket ModeDeployed via the Dockerfile (validates vars, fetches config, installs plugins, spawns runner)
Local devtask stack (Docker agent pane)Chat poll loop → admin Chat UITesting Claude locally without a Slack workspace — chat via the admin console’s Chat tab

The admin service Dockerfile (admin/Dockerfile) ENTRYPOINT is bun run admin/src/main.ts, which runs migrations, constructs all services, and mounts all admin and runtime routes. The agent container Dockerfile (agent/Dockerfile) ENTRYPOINT is bun run agent/src/entrypoint-main.ts, which starts the health server in-process on SHIPWRIGHT_HEALTH_PORT (default 3459) before the startup sequence so Kubernetes liveness probes are reachable during init.

Running the agent locally

Run the full local dev stack:

task stack

This launches every service (metrics, admin, task-store, chat service, and the agent in Docker) in a single tmux session, seeds dev tokens, and opens the admin console in your browser. Chat with the agent from the console’s Chat tab (/admin/chat) — the agent’s chat poll loop claims each message, runs it through Claude, and posts the reply back into the thread.

Admin CRUD API

The admin CRUD API is the human-facing control plane for agents. It is mounted at /agents/*.

Auth is checked in this order: env API keys (SHIPWRIGHT_ADMIN_API_KEYS), then per-agent DB bearer tokens, then session cookies. Admin keys bypass all per-agent checks; scoped bearer tokens are restricted to their own agent ID.

ResourceEndpoints
AgentsPOST /agents, GET /agents, GET /agents/:id, PATCH /agents/:id, POST /agents/:id/provision
EnvsPOST / GET / PATCH /agents/:id/envs, DELETE /agents/:id/envs/:key
CronsPOST /agents/:id/crons, PATCH / DELETE /agents/:id/crons/:cronId, POST /agents/:id/crons/reconcile
ToolsPOST / GET /agents/:id/tools, PATCH / DELETE /agents/:id/tools/:toolId
TokensPOST / GET /agents/:id/tokens, DELETE /agents/:id/tokens/:tokenId
PluginsPOST / GET / PATCH /agents/:id/plugins, DELETE /agents/:id/plugins

Token creation returns the raw token once at creation. Only its SHA-256 hash is persisted — token validation is an O(1) hash-index lookup with no plaintext at rest.

Data model

The agent store uses nine Prisma models on a dedicated database (DATABASE_URL_SHIPWRIGHT_ADMIN):

ModelWhat it owns
AgentRunner identity — name, slackId, selfHosted, repos, encrypted slackBotToken / anthropicApiKey
AgentEnvKey/value env store — key, value (AES-256-GCM encrypted); unique per [agentId, key]
AgentCronJobScheduled prompts — schedule, prompt, channel or user, silent, enabled, preCheck
AgentCronRunCron execution history — startedAt, completedAt, outcome, inputTokens, outputTokens, costUsd, model
AgentToolAllowed tool patterns — pattern (e.g. Read, Bash), enabled
AgentTokenScoped API tokens — token (SHA-256 hash), label, revokedAt
AgentPluginInstalled Claude Code plugins — name, version, enabled
AgentMemberAuthorized human members — email; unique per [agentId, email]
AgentChatTokenUsageDailyDaily chat token usage aggregates — date, inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens, costUsd; unique per [agentId, date]

All child models cascade-delete with their parent Agent.

Secrets at rest (env values, Slack/Anthropic keys) are AES-256-GCM encrypted at the service layer. Set SHIPWRIGHT_ENCRYPTION_KEY to a 64-char hex string (32 bytes) in any real deployment — if unset, secrets are stored in plain text with a logged warning.

Default system crons

Every new agent is seeded with eleven system crons. Three are enabled by default; the rest are opt-in and can be toggled in the admin UI or via PATCH /agents/:id/crons/:cronId.

CronScheduleDefaultWhat it does
shipwright-dev-taskevery 30 minonPick next ready task, build with tests, open PR
shipwright-reviewevery 30 minonReview-only pass over open PRs
shipwright-patchevery 30 minonFix failing CI and unresolved review findings
shipwright-deployevery 30 minoffMerge approved PRs and deploy
shipwright-test-readinessdaily 06:00offFull test-readiness audit
shipwright-docs-freshnessdaily 07:00offRefresh docs that drifted from the code
learn-dreamdaily 03:00offMine merged PRs for durable learnings
dependabot-triagedaily 08:00offReview and triage open Dependabot PRs
entropy-patrol-maintenanceweekly Mon 04:00offScan for code entropy and fix what’s PR-worthy

Crons run silent by default — they post to Slack only when there is a result worth surfacing or on error. Most carry a preCheck script whose stdout becomes the actual prompt, so a cron only spends a Claude turn when there is real work ready.

Required environment variables

VariableRequiredPurpose
DATABASE_URL_SHIPWRIGHT_ADMINalwaysPostgres connection string for the admin service
SHIPWRIGHT_AGENT_IDentrypointThe agent’s ID in the platform
SHIPWRIGHT_API_URLentrypointBase URL of the admin API for config fetch at startup
SHIPWRIGHT_AGENT_API_KEYentrypointBearer token for the config fetch
SHIPWRIGHT_ENCRYPTION_KEYproduction64-char hex for AES-256-GCM secrets at rest
SLACK_BOT_TOKENSlack modeSlack bot user OAuth token (xoxb-...)
SLACK_APP_TOKENSlack modeSlack app-level token for Socket Mode (xapp-...)
SLACK_SIGNING_SECRETSlack modeVerifies incoming Slack request signatures

See configuration.md for the full environment variable reference.

Kubernetes provisioning

When SHIPWRIGHT_K8S_PROVISIONING=enabled, the admin service acts as a Kubernetes provisioner. POST /agents/:id/provision creates:

  1. A PersistentVolumeClaim for the agent’s home directory (AGENT_HOME)
  2. A Secret carrying the scoped per-agent API token (and optional task-store token)
  3. A Deployment running the agent container, referencing both

The PVC is retained on deletion (data safety policy) — cluster admins clean it up manually. Self-hosted agents (selfHosted: true) skip provisioning; they manage their own workload.

Next steps

Ready to configure how much the agent does on its own? See Configuring Autonomy next, then Deploying to Cloud when you’re ready to ship.