Recipe: run the local dev loop
harbor dev boots a local Runtime + Protocol server on the loopback, mints an ephemeral dev token, and serves until you stop it. Pair it with harbor validate as a pre-boot check.
Steps
Build the CLI:
shmake buildWrite a config. Start from the annotated reference at
examples/dev.yaml— copy it toharbor.yamlat your project root. The two fields you almost always edit:identity.issuer/identity.audience/identity.jwks_url— point at a real OIDC provider for non-local deployments. The dev loop mints its own ephemeral ES256 token for local convenience.llm.api_key— uses anenv.NAMEreference (e.g.env.OPENROUTER_API_KEY). The bifrost driver resolves it viaos.Getenvat boot; a missing env var fails closed withErrMissingAPIKey(CLAUDE.md §13 — fail loudly).
Validate before booting:
shharbor validate ./harbor.yamlharbor validateruns the in-process config loader without booting any subsystem and emits stable, file:line-precise errors — suitable as a CI pre-flight gate. Exit0= valid,1= validation errors,2= unexpected error.Boot the dev loop:
shexport OPENROUTER_API_KEY=sk-or-... harbor devFlags:
--config <path>— config file; defaults toharbor.yaml.--port <int>— loopback port; defaults to18080(also overridable via theHARBOR_BINDenv var).--no-hot-reload— disable the fsnotify-driven hot-reload watcher (overridescli.dev_hot_reload.enabled).
harbor devserves untilSIGINT/SIGTERM, then shuts down gracefully.
The dev-only mock LLM escape hatch
For first-clone convenience and CI smoke with no real provider:
HARBOR_DEV_ALLOW_MOCK=1 harbor devThis prints a [DEV-ONLY MOCK LLM — DO NOT USE IN PRODUCTION] stderr banner on every boot. The default path with no flag set demands a real provider (CLAUDE.md §13).
Notes
harbor devboots the Runtime only — it does NOT serve the Console. The Console runs via the separateharbor consolesubcommand (D-091).- Governance tiers enforce (Phase 111a, D-198): a populated
governance.identity_tiersblock inharbor.yamlgates LLM calls at boot — cost ceilings, rate limits, and per-call MaxTokens each reject with a typed error and agovernance.*event on the stream. Empty tiers stay fully latent (D-044). Headless embedders running multiple stacks composegovernance.Wrapinstead — seedocs/recipes/embed-harbor-headless.md. - Hot reload watches the config file's directory and
.harbor/agentsby default; an edit toharbor.yamltriggers a drained reload. - To inspect a running dev server, use
harbor inspect-eventsandharbor inspect-runsagainst the same loopback.