Scaffold a new Harbor agent
Harbor ships one static binary — harbor — and a four-step flow that lands a working agent in under five minutes:
harbor init --target <dir> # drop the tiered yaml + companion docs
$EDITOR <dir>/harbor.yaml # pick a provider, set api_key
harbor validate <dir>/harbor.yaml # fail-loud config check
harbor scaffold --name <name> # materialise the Go projectThe flow is deliberately bounded — harbor init does not assume what LLM you have keys for or what tools you need. It drops a tiered harbor.yaml (REQUIRED → COMMON → ADVANCED sections) plus three companion files (AGENTS.md / CLAUDE.md / README.md) that document the project for human contributors and AI coding agents alike. You edit the yaml, validate it, then scaffold a Go project around it.
1. Drop the tiered yaml
harbor init --target ~/my-first-agentYou get:
~/my-first-agent/
├── harbor.yaml # the tiered config (REQUIRED / COMMON / ADVANCED)
├── AGENTS.md # contributor + AI-agent rules (verbatim mirror of CLAUDE.md)
├── CLAUDE.md # same
└── README.md # quickstart pointer for human contributorsThe yaml has a REQUIRED block at the top with four commented LLM-provider examples (OpenRouter / Anthropic direct / OpenAI direct / NVIDIA NIM). Uncomment exactly one block and set api_key: env.YOUR_KEY_NAME. Bifrost (Harbor's LLM driver) speaks many providers under one wire surface; the four examples cover the common cases. The full provider list is in docs/CONFIG.md.
AGENTS.md and CLAUDE.md are verbatim mirrors — Claude Code picks up CLAUDE.md automatically; other agents pick up AGENTS.md. Edit one, then cp AGENTS.md CLAUDE.md (or vice versa). If they drift, your project's drift-audit catches it.
2. Pick a provider, set an API key
$EDITOR ~/my-first-agent/harbor.yamlUncomment one provider block:
llm:
driver: bifrost
provider: openrouter
model: anthropic/claude-haiku-4.5
api_key: env.OPENROUTER_API_KEY
timeout: 60sThen export the key:
export OPENROUTER_API_KEY=sk-or-... # or set via .envHarbor's LLM driver reads the env var at boot. A missing key fails LOUDLY at startup with ErrMissingAPIKey — there is no silent fallback to a stub (CLAUDE.md §13: test stubs are never production defaults). If you want a dev-only mock for first-clone convenience, set HARBOR_DEV_ALLOW_MOCK=1 and see wire-the-llm-provider.
3. Validate before scaffolding
harbor validate ~/my-first-agent/harbor.yamlharbor validate runs Harbor's config loader against your yaml with file:line precision error messages — a missing llm.driver, a malformed tools.mcp_servers[0].command, or a memory.budget_tokens set to a negative number all surface here. Run this every time you edit the yaml; the failure modes are usually obvious from the message.
If validate is clean, you're cleared to scaffold the Go project.
4. Materialise the Go project
cd ~/my-first-agent
harbor scaffold --name my-first-agentharbor scaffold reads the yaml and drops a Go project around it:
my-first-agent/
├── go.mod
├── README.md
├── agent.go # your agent code (worked EchoAgent + RegisterTools)
├── agent_test.go # harbortest-driven smoke test
├── tools/ # one typed stub per tools.custom[] yaml entry
│ └── <name>.go / <name>_test.go
└── harbor.yaml # the same yaml init dropped (copied verbatim)When the yaml declares tools (tools.built_in / tools.custom), agent.go gains a generated RegisterTools function and each custom tool gets a typed stub (input struct, output struct, handler) under tools/. The generated imports are the public sdk/ facade paths, so the project builds as a standalone external module. Use the stubs as the template for your real tools (see add-an-in-process-tool).
5. Boot the runtime
go build -o bin/harbor ./cmd/harbor # only the first time; harbor dev re-builds incrementally
harbor devharbor dev boots the Runtime on 127.0.0.1:18080 and mints an ephemeral HARBOR_DEV_TOKEN (printed on stderr). The Console is a separate process — see run-the-dev-loop for the attach flow.
Common failure modes
harbor initoverwrote my edits. It won't —harbor init --target <dir>refuses to overwrite any of its four target files: ifharbor.yaml,AGENTS.md,CLAUDE.md, orREADME.mdalready exists it errors out (per-file, fail-loud). Delete the conflicting file or pick a fresh--targetdirectory and re-run.harbor validatesaysunknown field "tools". You're either on an oldharborbinary or your yaml uses a renamed key. Runharbor versionand check the upgrade notes; field renames are flagged in the CHANGELOG.harbor devexits withErrMissingAPIKeyimmediately. Your provider block declaresapi_key: env.OPENROUTER_API_KEY(or similar) but the env var isn't set in the shell that ranharbor dev. Check withecho $OPENROUTER_API_KEYbefore re-running. Source your.envif you keep keys there.harbor scaffoldsaysname must match ^[a-z0-9][a-z0-9_-]{0,63}$. The--nameflag is the Go module name, not a free string — it must start with a lowercase letter or digit and contain only lowercase letters, digits, hyphens, or underscores (up to 64 chars). It does not have to match the directory name;kebab-casekeeps imports clean.
See also
define-the-agent-yaml— every field inharbor.yamlexplained.wire-the-llm-provider— provider selection, models, the mock-vs-real posture.run-the-dev-loop—harbor dev+ Console attach.drive-the-playground— chat against your agent once it's running.- Sibling project: Dockyard's
scaffold-a-server— the same flow for MCP-server projects (different product, identical convention).