Repository navigation
Ci Asset Versioning
Decision record for how Volley versions binary assets. Closes the spike (#819); blocks #900 (Sam Level-of-Detail). Implementation is follow-up work, not part of this spike.
Store binary assets on Cloudflare R2, fronted by a Git LFS proxy (git-lfs-s3-proxy on a
Cloudflare Worker). One backend, two directory-keyed classes. The repo holds LFS pointers, never bytes.
| Class | Directory | Needed to run the game? | Fetched by default? |
|---|---|---|---|
| Assets |
assets/ (sprites, audio, imported game content) |
Yes | Yes, on build/run/CI |
| Concepts |
concepts/ (concept art, reference, PSDs) |
No | No, opt-in only |
-
Assets pull transparently via
git lfs pullon the run/build path. CI fetches them as a build step. -
Concepts are excluded from the default fetch; pulled only on demand with
git lfs pull --include="concepts/**"(wrapped asmake concepts).
A public clone gets pointers and no bytes. A contributor who wants to run the game fetches assets/; a
contributor who wants the art additionally fetches concepts/. Nobody drags down concept art to build.
One piece of self-hosted infra (a Cloudflare Worker running the LFS proxy) plus a contributor git-lfs
install, bought in exchange for zero clone-bandwidth cost and transparent updates as churny art
iterates. The rejected alternatives:
| Option | Why not |
|---|---|
| Commit binaries directly | Concepts land in history forever; every clone pulls them; fails the "clone stays lean, assets not pulled by default" requirement. |
| GitHub LFS | Bandwidth billed to the repo owner for every public clone (bots included); free tier exhausts fast; documented account-wide LFS disables. Wrong for a public repo. |
| R2 + pointer manifest (no LFS) | Zero infra, but a per-fetch make assets step the contributor must remember; stale bytes between fetches. Worse under high churn. |
Sprites and audio are expected within the prototype phase and will iterate often, so assets are soon and churny. The proxy beats the plain manifest on that horizon: per-fetch friction compounds with churn, while one-time infra setup does not.
The size gate blocks large binaries from plain git history, regardless of backend.
-
Local:
pre-commitframeworkcheck-added-large-files(auto-skips LFS-tracked files), threshold ~500KB, keyed soassets/andconcepts/route through LFS rather than tripping the gate. - Hard gate: a CI PR check comparing added files against the merge base, failing the PR on any plain binary over threshold. Catches contributors who skipped local hook install.
- Both exclude
.importsidecars from the threshold (they are generated config, can be large).
Commit the per-asset .import sidecars, ignore .godot/imported/ (the binary cache). The size gate
excludes .import files from its threshold. Import-settings detail lives in
art/tech-pipeline.md, the home for import behaviour.
The Cloudflare Worker running the LFS proxy is the chosen infra, on the condition that it stays within the R2 and Worker free tiers (both permanent, not trials):
| Resource | Free tier | Our load |
|---|---|---|
| R2 storage | 10 GB-month | ~50MB now, low single-digit GB with sprites/audio. Storage is the first ceiling to bump; overage is $0.015/GB-month. |
| R2 egress | Free, always | The meter that kills GitHub LFS does not exist here. |
| R2 Class B (reads) | 10M/month | One read per asset object fetched. Vast headroom. |
| R2 Class A (writes) | 1M/month | Only studio uploads of new art. Trivial. |
| Worker requests | 100,000/day | One request per LFS object transfer. A full asset fetch is N requests; hundreds of full fetches/day stay under cap. |
The realistic first ceiling is R2 storage at 10 GB, not requests or bandwidth, and overage there is cents. The one load that could eat Worker requests is CI re-fetching every asset on every job, so CI must cache LFS objects rather than re-pull (implementation note). If projected usage would exceed the free tier, revisit before paying: the R2 + manifest fallback (zero always-on infra, per-fetch friction) is the escape hatch.
The flow the implementation must deliver and document in CONTRIBUTING:
-
git lfs install(once per machine). - Clone. Working tree has pointers;
assets/LFS objects fetch on checkout via the proxy. -
make conceptsonly if you want the concept art.
Wire .lfsconfig at the proxy, .gitattributes LFS-tracking assets/ and concepts/, stand up the
Cloudflare Worker, add the size-gate hook + CI check, un-gitignore concepts/, create assets/,
document setup in CONTRIBUTING. This unblocks #900.
Contributing
The game
The Cast
Process
Reference