Status: early development. The packages work, but nothing is released yet and the API will change until there is. Consumers are being migrated to
bundlenow. See Packages for what works today.
The problem it solves
Applications tend to grow a separate loader for each type of content, each with its own file layout, parser, naming rule and error format. cclab-platform, for example, reaches 55 files through seven unrelated mechanisms.
Foundation Kit replaces that with one pipeline. You declare a kind of content and how to read it, and the kit handles path safety, identity, digests, linking and error reporting:
// Declare a kind next to the type it produces.
var Profile = kind.New[ProfileSpec]("example.profile", 1)
// Load bundles from disk and built-in definitions from Go code into one catalog.
catalog, err := bundle.Load(ctx, bundle.Options{
Sources: []bundle.Source{bundle.Dir("res/profiles")},
Compilers: []bundle.AnyCompiler{bundle.Compile(Profile, compile.JSON[ProfileSpec]())},
Providers: []kind.Provider{{
ID: "example.builtins",
Version: "1.0.0",
Builtins: []kind.AnyBuiltin{kind.Builtin(Profile, "default", ProfileSpec{Retries: 3})},
}},
})
if err != nil {
return err // lists every problem at once, not just the first
}
// Read definitions back with their Go type, and no casting.
for _, def := range bundle.All(catalog, Profile) {
fmt.Println(def.Ref, def.Value.Retries)
}A bundle is a directory with a bundle.yaml at its root. The manifest is always required, and is never generated from a folder layout:
profiles/
├── bundle.yaml
└── fast.json
apiVersion: craggy.io/bundle/v1
metadata:
id: example.profiles
version: 0.1.0
contributions:
- id: fast
kind: example.profile@1
path: fast.jsonLayers
The kit is organised in layers, and imports only ever point down. This is what lets it keep growing: a library can use a low layer without inheriting everything above it.
| Layer | What belongs there | Dependencies |
|---|---|---|
| Contracts | Types and interfaces that libraries can depend on cheaply. No I/O. | Standard library only |
| Engines | Generic mechanisms that do the real work, such as the bundle pipeline. | Contracts, plus small dependencies hidden in internal/ |
| Extensions | Optional additions to an engine or contract, such as ready-made compilers. | Contracts and engines. May add their own dependencies. |
| Test support | Helpers for testing against the kit, such as bundletest. Sits alongside the layers. |
Anything, but only tests may import it |
Nothing in the kit knows about any product. If a package needs a word like "lab" or "APK", it belongs in the library or application that owns that concept.
Packages
| Package | Layer | Purpose | Status |
|---|---|---|---|
kind |
Contracts | Versioned types of definition, with optional schemas and built-ins | Working |
bundle |
Engines | Loads bundles and built-ins into one catalog, with bundle-level overrides, global kinds, and Live reloading and installs |
Working, consumers being migrated |
bundle/compile |
Extensions | Ready-made compilers for text, JSON and YAML | Working |
kind/jsonschema |
Extensions | JSON Schema support for kinds | Working |
settings |
Engines | Live, user-editable values, declared by the code that reads them | Working |
fsx |
Engines | Crash-safe atomic file writes, and exclusive file locks | Working, consumers not migrated yet |
canonjson |
Engines | One canonical byte form of JSON, for hashing | Working, consumers not migrated yet |
retryafter |
Engines | Reads the HTTP Retry-After header |
Working, consumers not migrated yet |
feed |
Engines | Ordered change feed with per-reader cursors and backlog replay | Working; first consumer craggy-labs |
adapters/sqlite |
Extensions, own Go module | Opens SQLite with safe settings, and migrates schemas by version | Working, consumers not migrated yet |
bundle/bundletest |
Test support | Helpers for testing kinds, compilers and bundles | Working |
archtest |
Test support | Turns architecture rules into test failures | Working |
We also expect to add packages for lifecycle management, logging and an HTTP server. None of them exist yet, and each will be added once real applications need it.
Using it
The kit requires Go 1.25 or later, and its module path is github.com/craggy-io/foundation-kit. There's no tagged release yet, and the API will change until there is.
To develop the kit alongside an application, use an uncommitted go.work file rather than a replace directive in go.mod.
Contributing
go build ./... && go vet ./... && go test ./...
(cd adapters/sqlite && go vet ./... && go test ./...)Before adding a package, check the rules for what belongs in the kit. They apply to people as well as coding agents.
Documentation
| Document | What it's for |
|---|---|
| AGENTS.md | Rules for working in this repository, and for building on it |
| docs/decisions | Decisions we've made: the standard vocabulary, and live catalogs and layering |
| docs/assets | Brand wordmarks and diagrams. The wordmarks are unchanged copies of the Craggy brand kit's light and dark variants; the diagrams use the same palette and type as the other Craggy services and are generated by python3 docs/assets/diagrams.py, which fails if any text overflows its box. |
| docs/research | Background research. It informed the decisions but isn't binding, and its proposals conflict with each other. |