Summary
A .lnk today describes where an asset comes from, never where it should end up. There is no notion of a storage target (LittleFS, SPIFFS, SD card, embedded-in-firmware, WASM VFS), so a build cannot tell a correctly-configured project from a broken one — and says nothing either way.
This issue is to investigate the design, not to lock one in. The shape below is a starting point; the corner cases are the real content.
Verified current behavior
fbuild lnk add writes:
{ "sha256": "164804d9…", "size": 6051840, "url": "https://…", "v": 1 }
No destination field. And fbuild lnk pull resolves into the global cache only — nothing is materialized beside the .lnk:
lnk pull: 1 ok, 0 failed (of 1)
→ ~/.fbuild/prod/cache/archives/lnk-blobs/raw-video1.rgb/…/<sha256>/video1.rgb
$ ls examples/Fx/FxLedmapper32x32/data/
.gitignore readme.txt screenmap.json video1.rgb.lnk # <- no video1.rgb
So the bytes are fetched and verified, then stop. Nothing consumes them.
Related context: FastLED has no LittleFS backend at all — FsImpl is implemented only for Arduino SD (SPI), the WASM browser VFS, and a host stub. And fbuild has no buildfs/uploadfs. See #1354.
The gap
data/*.lnk → [lnk pull ✅] → global cache → ??? → device filesystem
▲
no destination concept
Direction to investigate
1. A destination on the descriptor
Something like a dest block naming the storage target:
{
"sha256": "164804d9…",
"size": 6051840,
"url": "https://…",
"dest": { "target": "littlefs", "path": "/video1.rgb" },
"v": 2
}
Candidate targets: littlefs, spiffs, sdcard, firmware (baked as a byte array), vfs (WASM preview), none (host/test only).
assets.json would carry the same vocabulary, plus a file-level default so many assets can share one target without repeating it:
{
"defaults": { "target": "littlefs" },
"assets": { "video1.rgb": { "url": "…", "sha256": "…" } }
}
Version bump required. Adding dest to a v1 .lnk changes its meaning, so this is v: 2. A v1 .lnk must keep working — which forces the "no destination declared" question below.
2. Diagnostics — the actual ask
Every declaration should produce a clear verdict. A proposed severity split:
Error (fails the build)
- sha256 mismatch — already the rule; never silently accept
- asset exceeds the filesystem partition
- target unsupported on the selected board (e.g.
littlefs on AVR)
littlefs target with no filesystem partition in the partition table
- two declarations naming the same file with conflicting targets
Warning (yellow)
.lnk with no destination descriptor — the case that prompted this issue. Fetches and caches, but goes nowhere. Should say so and name the default it fell back to.
sdcard target — fbuild cannot write to a card it does not have; the copy is manual and the warning must say that plainly
- a large file in
data/ with no .lnk (committed binary that arguably should be remote)
.lnk missing sha256 — fetch works, integrity does not
Info
- cache hit vs fetched, and the resolved target per asset
Warnings must name the offending file and the concrete fix. A yellow line reading "no destination" is only useful if it also says what to add.
Corner cases to work through
These are the reason this needs investigation rather than a patch:
- No destination declared — hard error, or warn-and-default? A default of
littlefs breaks SD-based sketches like FxLedmapper32x32; a default of sdcard silently does nothing at build time. Neither is obviously right, and v1 .lnk compatibility forces an answer.
.lnk sidecars must be excluded from any generated image — shipping video1.rgb.lnk to the device is always wrong.
- Plain files in
data/ with no .lnk — do they join the image implicitly? Mixed remote and committed assets in one directory is the common case.
- Partition sizing — total
data/ versus partition capacity must fail at build time, never at runtime mount.
- Platform capability matrix — LittleFS support differs across ESP32 / ESP8266 / RP2040 / Teensy / AVR. Needs a real table, not assumptions.
- SD card is not deployable by a build tool. Is it a target at all, or just documentation?
- Conflicting declarations —
.lnk currently overrides assets.json for the same filename (FastLED's ci/asset_resolver.py rule). Should a target conflict silently follow that precedence, or error?
- WASM preview ignores targets entirely — everything is browser VFS. Should a
littlefs target warn under a WASM build, or be silently ignored?
- Filename constraints — LittleFS filename length and path-depth limits can reject names that were fine on disk.
- Offline builds — cache miss plus no network needs a distinct, actionable message, not a generic fetch failure.
- Shared assets across sketches with different targets — is the target a property of the asset or of the sketch that uses it?
- Firmware-embedded target — bakes into the binary and interacts with bloat/size budgets rather than a filesystem.
Open questions
- Does
target belong on the asset, on the sketch, or on the board environment? An asset reused by two sketches may need different answers.
- Should
lnk pull gain a --materialize mode that writes beside the .lnk, or does only buildfs consume the cache?
- Is
assets.json the right home for defaults, or should that live in platformio.ini / the environment?
Related
Summary
A
.lnktoday describes where an asset comes from, never where it should end up. There is no notion of a storage target (LittleFS, SPIFFS, SD card, embedded-in-firmware, WASM VFS), so a build cannot tell a correctly-configured project from a broken one — and says nothing either way.This issue is to investigate the design, not to lock one in. The shape below is a starting point; the corner cases are the real content.
Verified current behavior
fbuild lnk addwrites:{ "sha256": "164804d9…", "size": 6051840, "url": "https://…", "v": 1 }No destination field. And
fbuild lnk pullresolves into the global cache only — nothing is materialized beside the.lnk:So the bytes are fetched and verified, then stop. Nothing consumes them.
Related context: FastLED has no LittleFS backend at all —
FsImplis implemented only for Arduino SD (SPI), the WASM browser VFS, and a host stub. And fbuild has nobuildfs/uploadfs. See #1354.The gap
Direction to investigate
1. A destination on the descriptor
Something like a
destblock naming the storage target:{ "sha256": "164804d9…", "size": 6051840, "url": "https://…", "dest": { "target": "littlefs", "path": "/video1.rgb" }, "v": 2 }Candidate targets:
littlefs,spiffs,sdcard,firmware(baked as a byte array),vfs(WASM preview),none(host/test only).assets.jsonwould carry the same vocabulary, plus a file-level default so many assets can share one target without repeating it:{ "defaults": { "target": "littlefs" }, "assets": { "video1.rgb": { "url": "…", "sha256": "…" } } }Version bump required. Adding
destto a v1.lnkchanges its meaning, so this isv: 2. A v1.lnkmust keep working — which forces the "no destination declared" question below.2. Diagnostics — the actual ask
Every declaration should produce a clear verdict. A proposed severity split:
Error (fails the build)
littlefson AVR)littlefstarget with no filesystem partition in the partition tableWarning (yellow)
.lnkwith no destination descriptor — the case that prompted this issue. Fetches and caches, but goes nowhere. Should say so and name the default it fell back to.sdcardtarget — fbuild cannot write to a card it does not have; the copy is manual and the warning must say that plainlydata/with no.lnk(committed binary that arguably should be remote).lnkmissingsha256— fetch works, integrity does notInfo
Warnings must name the offending file and the concrete fix. A yellow line reading "no destination" is only useful if it also says what to add.
Corner cases to work through
These are the reason this needs investigation rather than a patch:
littlefsbreaks SD-based sketches likeFxLedmapper32x32; a default ofsdcardsilently does nothing at build time. Neither is obviously right, and v1.lnkcompatibility forces an answer..lnksidecars must be excluded from any generated image — shippingvideo1.rgb.lnkto the device is always wrong.data/with no.lnk— do they join the image implicitly? Mixed remote and committed assets in one directory is the common case.data/versus partition capacity must fail at build time, never at runtime mount..lnkcurrently overridesassets.jsonfor the same filename (FastLED'sci/asset_resolver.pyrule). Should a target conflict silently follow that precedence, or error?littlefstarget warn under a WASM build, or be silently ignored?Open questions
targetbelong on the asset, on the sketch, or on the board environment? An asset reused by two sketches may need different answers.lnk pullgain a--materializemode that writes beside the.lnk, or does onlybuildfsconsume the cache?assets.jsonthe right home for defaults, or should that live inplatformio.ini/ the environment?Related
buildfs/uploadfsgapci/asset_resolver.pyin FastLED/FastLED — reference parser for both formats, with 19 tests