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.
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.
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).
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.
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.
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.