github.com/codefly-dev/core (Go 1.27) is the shared library every codefly
agent, the CLI, and user services depend on. It owns the resource model, the
agent gRPC contracts, the network and configuration models, the proto schema
source of truth, and the runner/companion surfaces.
It does not own: the codefly CLI (codefly-dev/cli, a private module — do
not import it, and do not make CI depend on it), any individual agent
implementation, or user workspaces. Core defines contracts that those fill in.
When a change you need belongs to one of them, it gets made there.
Fleet standard — handbook#68. These land hard here: core is a library, so a defect is observed downstream and the temptation is always to absorb it locally rather than fix what owns it.
- Prove reuse before you build, and build the simplest thing that works; size is the signal: fill a gap on what the platform already has, keep a coding task to about 500 hand-written lines, and past 2,000 stop and ask the user. Both in full, with their tells, in the handbook.
- Look for the
codeflycommand before doing anything by hand. The CLI owns the fleet verbs —codefly agent deps --pin,codefly agent release --pin,codefly update deps,codefly update workspace,codefly generate proto— and each gates what a hand-edit skips:agent releaseruns the agent's CI before it tags, so a fleet bump cannot ship an agent that does not build. Read--helpon the command group first. A bulk edit across repos that no command performed is the signal this step was skipped. See.claude/skills/release-corefor the cascade after a core tag. - A gap in the tooling is a bug in the tooling — never a reason to reach
around it. When something needs a step
codefly,bufor a companion does not perform, the answer is a capability fixed in whichever tool owns it, named in the PR — never a hand-assembled substitute, not "just this once". - Never hack. Provide the best fix, even when it spans repos. The fix living
in
codefly-dev/clior an agent repo is no reason to work around it here: open the PR there. When it cannot be fixed now, the deliverable is a precise issue against that owner plus a stopgap labelled as one. - Classify every change that makes something work, in the PR body: a fix at the place that owns the behaviour, or a hack.
- Never hardcode what the system resolves — injected environment, derived
ports, service addresses, credentials copied out of another component. Core is
where that resolution is defined: ports come from
network.ToNamedPort(), endpoints and credentials from the configuration flow. Typing one encodes something true only on one machine for ten minutes, and it fails quietly — a runtime missing a credential can skip registration silently, so the service boots, serves, and is simply absent. - Diagnose, do not pattern-match. "It started working when I set X" is not a diagnosis — set X back and confirm it breaks. Do not trust an error message before checking its claim: a guard here reporting a version contract failure has meant a stale embedded file, not a bad package.
- Say what you did not verify. Unverified is not working: a unit run is not the tagged suite, nor is a boot. If you could not exercise it, the PR says so.
Derived from .github/workflows/go.yml — CI pins Go 1.27.3 and every step is
strict (nothing skips when a prerequisite is missing).
go test ./... # the suite
go test ./resources/ -v # one package
go test -race ./... -timeout 10m # CI runs this separately
make check-coverage # thresholds in .testcoverage.yamlCI additionally runs, and all of these are runnable locally:
| Command | Guards |
|---|---|
go test ./... -tags=sandbox_e2e,nix_required |
needs bubblewrap, ripgrep, uv, nix |
./scripts/check_cgo_free.sh (make check-cgo-free) |
cgo creep outside code/semantic |
./scripts/check_version_tag.sh (make check-version-tag) |
version/info.codefly.yaml vs. published tags |
./scripts/govulncheck.sh |
unsuppressed vulns that have an upstream fix |
make buf-breaking |
schema breaks, PACKAGE rules |
make buf-lint |
BASIC + COMMENTS: every field, enum value and message documented |
Run buf checks through the Makefile, never a bare buf: its targets pin the
version CI's gate and the companion's generator both use. Generation runs buf
inside the published companion, not from the host's PATH.
GOFLAGS ?= -timeout=300s is exported by the Makefile so no test run hangs.
Tests with the proto_companion_required tag are not in CI — they need an
image core alone cannot build. Run them locally before touching that surface.
Never mock. Tests use real infrastructure and real YAML fixtures under
testdata/. If a boundary is hard to reach, reach it anyway.
resources/ is the source of truth for every type; read it there first.
| Path | Owns |
|---|---|
resources/ |
Workspace/Module/Service/Endpoint/Agent, YAML loading, validation |
proto/ |
schema source of truth (codefly/*, mind/*); generated/ is output |
callertoken/ |
shared gRPC caller-token authentication for agent and service listeners |
agents/ |
agent registration, process manager, the embeddable server types |
network/ |
deterministic port allocation, DNS, native/container/public modes |
configurations/ |
what services provide and consume, injected as env vars |
runners/, companions/ |
process execution; sidecar images for language tooling |
imagepolicy/ |
target-declared Dockerfile requirements (docs) |
solutionhost/ |
ModuleManifest, the one signed record of a module instance on a host: its workloads, endpoints and requested authority (docs) |
contracts/module/ |
the ONE implementation of codefly/module-contract/v1 (Go package module, imported as modulecontract) — the request a module publishes, strict decoding, every refusal as a named rule (rules.go), slot resolution, a serialisable model, shipped fixtures every reader is driven through and a self-check that deletes each rule and proves a fixture notices (docs). The cell (codefly/cell/v1) has no implementation in core |
code/semantic |
the only package allowed to use cgo (tree-sitter) |
internal/ciguard |
invariants about CI config that no other test would notice |
The deeper picture is in docs/architecture.md —
resource hierarchy, agent lifecycle, network mapping, configuration flow,
readiness, dependency graph. Read it rather than re-deriving from the tree.
- Core owns runtime configuration and secrets; CLI owns deployment. Infra-base
and other producers emit the CLI's
codefly/coordinate/v1declarations. Cluster, registry, ingress, managed services, identity attachments and GitOps policy stay in CLI. Never translate private producer inventories in Core/CLI. Seedocs/configuration-contract.md. - Core and the CLI have no concrete-agent compatibility knowledge. Check
the running agent's advertised wire/startup protocol and operation capabilities
at runtime, before work. Never gate on agent names, release pins, linked Core
versions, or a compiled compatibility roster. Unchanged protocols require no
fleet rebuild or repinning. Missing declarations are errors, not permission to
fall back. Agent-specific selection, settings and cleanup belong to the agent;
Core transports declarations and implements generic contracts. See
docs/agent-contract.md. Shared language/framework tooling may remain when explicitly invoked by its consumers; it must not choose agents or impose agent runtime policy. - Product replacements never rewrite owner releases. Resolve exact instances
from signed metadata; no implicit dependency-source acquisition. Local inputs
are development-only. Deployment admits owner output or authorized unchanged-
source builds with input-bound qualification; see
docs/composition-selections.md. - Ports come from
network.ToNamedPort()orRuntimeManager. Never hardcode one, always track allocations — a missing dedup once assigned the same port twice. - Identity comes from a verified Work Context, never assembled beside one.
workcontextmints and verifies the signed capability;Principalis derived from a verified one, and its delegation chain with it. An approval is a grant hop — the one audited exception to attenuation, bound to one tool, subject and call, single-use, and one hop however large the quorum. A capability's seal and every hop's epoch are REQUIRED by the schema. They were optional once, with archived receipts as the reason; two reviews called that a compatibility hedge the rules forbid, and archived receipts are historical data that get a snapshot type if they need one. Core owns theapproval_requiredsignal; the approvals engine is product-level. It has exactly one implementation and one strength — this one. Nothing else signs, verifies or re-encodes a capability; the entrypoints share one check path: calleesVerify/Authenticateand refuse what they cannot answer, a hopInspects without consuming and forwards what only live state refuses, the issuerReadBacks its own capability. A weaker check is the owner's question, never a local accommodation. A consumer DEMONSTRATES BEHAVIOUR withworkcontext/conformance.Run,RunAuthenticatororRunInspector— the kit proves behaviour and not identity, and the identity proof is the consumer's own import gate; a foreign encoding is refused before the signature withErrNotACoreToken, never as a bad signature. A second implementation already cost a day of key-rotation debugging. Seedocs/work-context.mdandworkcontext/README.md. - An endpoint declares three axes, each read on its own.
visibilityis reach,locationis where it lives,exposureis whether an outward address exists;allow_modulesis derived (Workspace.DeriveAllowModules), refused when authored. Seedocs/network-model.md. - Readiness is a gRPC health check, never a TCP connect. An open port does
not mean a ready service. Endpoints declare
health.readiness; consumers go throughresources.PlanReadinessand thereadinesspackage. Seedocs/readiness.md. - Regenerate protos with
codefly generate proto --proto ./proto --output ./generated --local.--localselectsgenerated/buf.gen.local.yaml; buf, plugins and goimports run inside the pinned proto companion. Do not hand-runbuf/protoc. Commit the.protochange and the regenerated bindings together; seeproto/README.md. - Keep the CGO-free surface CGO-free. cgo lives only in
code/semantic; consumers take the build-tag-aware constructor fromcode/codeserver.New(-tags codefly_nosemantic). Seedocs/cgo.md. - Companions are ours. A broken companion gets fixed, not routed around.
- A CI credential is a job of its own, gated to a merged ref that checks out
nothing under review. See
docs/ci-credentials.md.
Step-by-step procedures live in .claude/skills/, loaded on demand rather than
carried here:
ci-guard-triage— one of the bespoke CI guards is red, and you need to tell a real finding from an escape hatch that would only make it green.release-core— cutting a version, and why the tag is never made by hand.
- Branch and PR; never commit to
main. Conventional Commits for the title. - Agent protocol changes update
agents/contract/contract.json; releases report its compatibility separately from Core's version. Seedocs/agent-contract.md. internal/ciguardholds the meta-invariants, including this file's length budget and each skill's frontmatter contract. It runs undergo test ./..., so CI enforces it with no workflow change.- Keep this file under ~150 lines (hard cap 200). Push depth into a nested
AGENTS.mdbeside what it describes, into.claude/skills/, or intodocs/. CLAUDE.mdpoints here, and the PR that changes a process updates this file.