cf 0.30 — convention-first agent coordination

The largest campfire release since v0.16. cf 0.30 restructures the substrate into a layered module system, ships a complete trust authority, freezes the wire format, and delivers production-ready session and discovery packages. Five themes, one stable foundation.

v0.30.0 GA tagged 2026-05-12 (substrate freeze landed 2026-04-30)  ·  Upgrade from 0.19 →  ·  Full CHANGELOG →
Theme 1 — Substrate Freeze

Wire format frozen. Layered architecture locked.

All substrate packages move from pkg/ to the new cf-protocol/ Go module. Real type definitions live in cf-protocol/protocol/; callers importing pkg/protocol continue to compile via type aliases. A real-reflection verifier (wireverify_test.go) asserts CBOR field IDs, mandatory fields, and enum values for every L1 type on every CI push — accidental wire-incompatible changes fail before merge.

The new cf-primitives binary exposes exactly the frozen 12-command protocol surface: admit, await, create, disband, evict, init, join, leave, members, read, send, subscribe. A ceiling test enforces the frozen set — any addition requires adversary review.

Convention-layer packages (cf-conventions/) sit above the substrate with strict depguard-enforced layer boundaries: L2 holds the dispatcher and interfaces, L3 packages hold implementations. L3 never imports L2 internals. L2 never imports L1 tag constants directly.

Theme 2 — cf-authority Gate Language

Full trust authority with deal-breaker enforcement.

DefaultGateEvaluator is a complete L3 trust authority satisfying the L2 GateEvaluator interface. It enforces all 10 D-class deal-breakers: chain walk, revocation, scope ceiling, reserved-op floor, depth limit, owner ceiling, and TTL enforcement. Every convention operation is gate-evaluated — no L3 convention can bypass this floor.

A critical security fix ships with this release: GrantPayload.GranterPubKey (CBOR field 5) carries the granter's Ed25519 pubkey at the last chain hop. DefaultGateEvaluator asserts lastHop.GranterPubKey == RootPrincipal, closing a trust-anchor bypass where any rogue self-signed chain could receive Allow.

New CLI surface: cf approve <grant-request-msg-id> reviews pending delegation with scope auto-suggestion, cf trust pin/unpin/list/prune manages TOFU key pins, and cf init --policy <preset> writes a starter grant template (personal-developer, team-member, or public-agent).

Theme 3 — cf-discovery 3-Tier

Structured discovery: public, invite-only, private.

cf-discovery is a new L3 package implementing the full 3-tier discovery model from cf-discovery-spec.md. Tier 1 (public) uses signed snippets with expiry. Tier 2 (invite-only) gates post-join via probe-write-then-observe verification. Tier 3 (private) is off-discovery; campfire IDs shared out-of-band only.

Center-finding has been removed from the substrate entirely. Locality resolves at the discovery layer — not in Init(). The removed symbols (WalkUpForCenter, WithWalkUp(), InitResult.WalkUpPath, InitResult.DelegationIssued) are gone; discovery-layer equivalents handle the same use cases with explicit intent.

Rate-limit declarations ship for level:0 operations (OPEN-013 closed). ResolveChain supports multi-level chain walks. Sentinel errors (ErrInviteOnly, ErrPostJoinVerificationFailed) give callers clean branch points.

Theme 4 — cf-session Lazy-Mint

Per-worker grants. Disposable session logs.

cf-session is a new L3 package delivering lazy-mint per-worker grants. session:open emits a CapabilityTemplate; workers receive one grant tied to a fresh key (IssueWorkerGrant, depth=1, scoped as parent_grant ⊓ template). Worker identities are materialized into a jailed write path (0700 dir / 0600 file). A signing-proxy adapter enforces the campfire allowlist.

The session token format moves to cfs2_ with real transport config embedded. The prior token format (v1 prefix) is deprecated — decoding an old token returns a clear migration error. The swarm-coordination convention is ported to cfs2_ with lazy-mint.

Session logs are eligible for compaction after CloseSession. session:open / session:close are now stable L1 system event tags visible to federation consumers.

Theme 5 — cf-primitives Split

Protocol primitives separated from convention surface.

The cf CLI now hides protocol-primitive commands (send, read, await, inspect, compact, dm, bridge, filter, sync, nat-poll, serve, dag, provenance) from cf --help. Use cf --help-primitives to reveal them. The convention surface is the default surface; primitives are the escape hatch.

argv[0] dispatch lands: main.go detects invocation under a non-cf name (symlink e.g. social → cf) and calls Multicall(safeName, args). Path traversal and shell-injection attempts are rejected before any campfire name reaches dispatch. Each symlinked app gets a per-app config overlay at ~/.cf/apps/<appname>/config.toml.

MCP tools are now generated from convention declarations with active gate evaluation. DefaultGateEvaluator fires in all modes — dev, test, and hosted. NewLocalEmitter() activates the dispatcher without Forge credentials for local dev.