Proposal: Git-backed Forest components via srcball #3
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
014 - Git-Backed Forest Components via srcball
Status: Phase 1 — Proposal / design review.
Depends on: Forest component v2 (
forest.cue+forest.component.cue), component cache,forest update,forest.lock, srcball source proxy.Driver: Forest's component registry currently owns source upload, file bundles, binary distribution, search metadata, auth, and CUE module publishing. The stronger long-term model is to make Git repositories the package authority and use srcball as a pull-through Git/source cache, so teams publish components by tagging source instead of uploading copies into a Forest-owned registry.
Problem
Forest components are already authored as source packages:
forest.cuedeclares project/component metadata.forest.component.cuedeclares#Spec,#Commands,#Hooks, and optional tool/runtime facets.org/nameand configure usage blocks under the same identity.But distribution still flows through a Forest registry upload path:
forest components publishuploads files and binaries.forest updateand runtime paths pull from Forest registry APIs.That duplicates what Git already does well: authority, audit, tags, access control, and source history. It also creates a second source of truth for component source bundles.
srcball is a better substrate for the source half of this system: it turns a Git repository/subpath/ref into an immutable cached source artifact, exposes direct package and OCI-style pull endpoints, materializes source trees, and can be backed by a local volume today and object storage later.
Decision
Use srcball to replace Forest's source distribution path, not Forest's component runtime.
Keep Forest's component framework:
forest.cueforest.component.cueforest runforest.lockReplace the publish/upload source flow with Git/source resolution:
Do not make srcball a Forest-aware component registry in the first version. Keep it generic: Git locator + optional subpath + ref → cached source artifact.
Goals
org/namebecause usage blocks, commands, and docs depend on that identity.~/.cache/forest/components/<org>/<name>/<version>/.forest updatecan resolve0.1,1,latest, and exact versions from remote tags.forest.lockrecords the resolved Git commit and source artifact digest.Non-goals
forest run, component SDK protocols,#Commands,#Hooks, or runtime descriptors.Current Forest shape
Component contract
Component authors define metadata in
forest.cue:Component interfaces live in
forest.component.cue:Consumer projects declare dependencies and usage separately:
This means
org/nameis a stable logical identity. srcball's raw Git locator cannot replace it directly; Forest needs a mapping layer fromorg/nameto Git source.Registry/API responsibilities today
Forest registry currently provides:
GetComponent,GetComponentVersion,ListComponentVersionsBeginUpload,UploadFile,CommitUpload,AbortUploadGetComponentFilesUploadBinary,DownloadBinaryPublishManifest,GetComponentManifestListOrgToolsThose responsibilities should be split. Source distribution can move to srcball/Git; runtime metadata, cataloging, binary strategy, and auth need separate treatment.
Proposed dependency syntax
Extend
#ForestDependencywith a source object while keeping existingversionandpathforms.For semver ranges, omit
refand provide a tag prefix:Rules:
pathremains local development.versionremains the semantic version requirement.source.git.locatoridentifies the Git repository and optional component subdirectory using srcball'srepo//sub/pathconvention.refpins an exact Git ref/tag.tag_prefixsupports monorepo component tags.forest.component.versionmust match the selected semantic version.forest addbehaviorNew accepted form:
Flow:
forest.cuefrom the materialized source.project.organisation,forest.component.name, andforest.component.version.org/name.cue.mod/module.cueforforest.sh/{org}/{name}@v0when CUE imports are needed.The user provides a source. Forest records the component identity declared by the source.
Cache hydration
Forest should keep its current local cache layout:
For a Git-backed dependency, Forest should ask srcball for the source artifact, then materialize the selected subpath into the cache directory:
Existing code paths can then continue to work:
ComponentParserProjectParserforest runforest generateForest should not depend on srcball's internal storage paths. Treat srcball as a fetch/materialize API, not as a cache directory to walk directly.
Version resolution
For Git-backed components,
forest updateshould resolve versions from Git tags instead of registryListComponentVersions.Suggested monorepo tag convention:
Single-component repositories may use normal tags:
Resolution flow:
tag_prefixwhen present.VersionSpecbehavior (exact,minor,major,latest,*).forest.component.versionmatches the selected version.Lockfile extension
Current lockfile entries should grow a source form.
Example:
For binary components, keep platform-specific binary hashes as separate lock entries or attach them to the same logical dependency:
Important invariants:
CUE module compatibility
Forest currently makes component CUE imports work by publishing CUE files as OCI modules under:
srcball's current source OCI artifact is not the same as a CUE module registry artifact. CUE expects module metadata/zip semantics; srcball currently returns source OCI layout artifacts.
Use a staged approach.
Phase A: Forest-side vendoring
After srcball materializes a component source tree, Forest vendors
.cuefiles into the project'scue.mod/pkg/forest.sh/{org}/{name}@v<major>/directory, matching existing behavior.This proves source-backed components without changing srcball's media formats.
Phase B: CUE adapter
Add either a Forest-side or srcball-side CUE module adapter:
or OCI-compatible CUE module endpoints backed by srcball source artifacts.
Do this after Phase A works.
Binary component strategy
srcball currently distributes source, not per-platform executables. Forest binary/global-tool behavior needs a deliberate strategy.
Option 1: Build on install
Pros:
Cons:
Good first path for internal components.
Option 2: GitHub Releases / external assets
Declare release assets in component metadata or manifest:
Pros:
Cons:
Preferred long-term path for global tools.
Avoid initially: srcball as binary registry
If srcball starts storing build outputs, it becomes another Forest registry. Defer until a concrete need proves this is worth the complexity.
Auth and private repositories
Local mode is safe:
Central shared mode is unsafe unless authorized:
Before running central srcball for private repositories, require:
Until then, shared srcball should serve only public sources or single-tenant/private deployments.
Catalog and discovery
A searchable registry is still useful, but it should be derived, not authoritative.
Possible catalog indexer:
forest.cue+forest.component.cue.forest search.The catalog can replace registry search/detail while Git/srcball remain the source path.
Migration plan
Milestone 1 — Source-backed local cache hydration
Implement Git-source dependency parsing and materialization through srcball.
Acceptance:
path:can be consumed via Git source.~/.cache/forest/components/<org>/<name>/<version>/is populated from srcball.ComponentParserreads the materialized component.forest run/forest generatepaths operate on the cache.Milestone 2 — Git tag semver resolution
Implement tag listing and version selection for Git-backed deps.
Acceptance:
version: "0.1"resolves to highest matching Git tag.latestresolves to the highest semver tag, not mutableHEAD.Milestone 3 — CUE import support
Vendor CUE files from materialized source into
cue.mod/pkgor add a CUE module adapter.Acceptance:
import "forest.sh/{org}/{name}@v0"works for Git-backed components.cue exportworks in a clean checkout afterforest update.Milestone 4 — Publish becomes validation/tagging
For Git-backed components,
forest publishvalidates component metadata and release readiness instead of uploading files.Acceptance:
forest.component.versionmatches the tag/version.UploadFilefor source-only components.Milestone 5 — Binary strategy
Choose local build or external release assets for binary/global-tool components.
Acceptance:
Milestone 6 — Registry reduction
Delete or deprecate source-file upload paths once Git-backed source components are stable.
Candidate removals/deprecations:
BeginUpload/UploadFile/CommitUploadfor Git-backed components,Keep registry/catalog APIs only where they provide metadata/search/auth not covered by Git/srcball.
srcball additions needed
Materialize API
Forest needs a stable way to hydrate a destination directory without depending on srcball internals.
or an HTTP endpoint that streams normalized files.
Reference listing
Needed for semver tag resolution.
Response should include tags, heads, and commit IDs.
Artifact digest in metadata
ArtifactMetadataalready includes package, version, source ref, commit, and time. Forest also needs the source artifact digest for lockfile integrity.CUE adapter later
Add CUE module/proxy endpoints only after Forest-side vendoring proves the model.
Auth model before shared private use
Central srcball must authorize cache hits, not just Git fetches.
Forest changes needed
#ForestDependencywithsource.ComponentSourceResolverabstraction for local path, current registry, and srcball Git sources.forest addto inspect Git-backed component sources and write canonicalorg/namedependencies.forest updateto resolve Git tags and hydrate the component cache from srcball.forest.lockwith source locator/ref/commit/digest.forest publishinto source validation/tagging vs legacy registry upload.Risks
High — private cache authorization
A central srcball can leak private repository content if cache hits are not authorized per requester.
Mitigation: local-only first; central private mode requires identity, GitHub/Git forge authorization, cache ACLs, and revocation behavior.
High — binary distribution
Source fetch does not replace per-platform executable distribution.
Mitigation: build locally first for internal components; use GitHub Releases/external manifests for global tools; defer srcball binary storage.
High — CUE module semantics
CUE module OCI artifacts are not the same as srcball source OCI artifacts.
Mitigation: Forest-side CUE vendoring first, CUE adapter later.
Medium — monorepo version tags
Monorepos need tag naming conventions and prefix filtering.
Mitigation: require explicit
tag_prefix; validate component version against selected tag.Medium — search/catalog regression
A Git-only model may lose registry discovery UX.
Mitigation: build a derived catalog index over Git repositories.
Medium — reproducibility
Git tags can move.
Mitigation: lock commit and artifact digest; fail when a locked tag resolves differently.
Recommended first spike
Pick one existing local path component, for example
forest-contrib/build-rust, and consume it from Git through srcball.Target dependency:
Spike acceptance:
forest updatefetches the source through srcball.~/.cache/forest/components/forest-contrib/build-rust/0.1.0.forest.component.cueandforest.cueparse from the cache.forest.lockrecords source locator, ref, commit, and digest.Bottom line
The strongest version of this idea is not "rebuild the Forest registry on top of srcball".
It is:
That cuts away duplicated source storage while preserving the valuable parts of Forest: typed CUE contracts, composable components, release hooks, and
forest run.