Summary
Every application that uses Foundry Local downloads and stores its own private copy of
every model it uses. For a 7B class model that is several gigabytes per application, and
nothing is shared even when the bytes are identical.
Windows already solves this problem for application payload. An MSIX package is described
by a signed AppxBlockMap.xml that hashes every file in 64 KB blocks. Single instance
storage keys on those hashes, so a block that is already on the machine is stored once no
matter how many packages contain it, and deployment does not fetch a block the machine
already has. The same mechanism covers both the download and the disk.
Model weights are close to the perfect input for it: large, immutable, and byte identical
wherever they appear.
What we are asking for: that Foundry Local support and document loading a model from a
read only directory inside the application's own MSIX package. Two applications shipping
the same model would then cost one copy on disk and one download, each application would
pin the exact model version it shipped and was tested against, and uninstalling an
application would reclaim its model as ordinary package payload.
The duplication is real, and it is byte identical
Measured across every Foundry Local cache on one machine:
| Caches |
Files |
On disk |
Redundant |
Share |
| 32 |
365 |
40.05 GB |
17.55 GB |
43.8% |
This is not a name heuristic. Candidates were grouped by exact byte length and then every
candidate was hashed with SHA-256, which found 55 groups of identical content and 101
redundant copies. Examples:
655 MB encoder.onnx.data x3
439 MB context_0_ctx_qnn.bin x3 (and _1, _2, _3, also x3)
787 MB context_0_ctx_qnn.bin x2 (and _1, _2, _3, also x2)
Every one of those is the same model, downloaded and stored once per application.
Why MSIX is the right mechanism
Shipping the model as package payload changes four things at once, and no application code
is needed for any of them:
|
Today, per-app cache |
Model in the MSIX |
| Second app, same model, disk |
Second full copy |
Stored once, SIS |
| Second app, same model, download |
Full download again |
Blocks already present are not fetched |
| Integrity |
Unverified, see the note below |
Signed block hashes, verified by the platform |
| Uninstall |
Nothing reclaimed |
Reclaimed as package payload |
| Version the app depends on |
Whatever the shared state currently holds |
Pinned to the package, unaffected by other apps |
Uninstall is listed for completeness and is covered properly in a companion issue, since it
applies to the default location regardless of how models are distributed. The rows that are
unique to packaging are the first two and the last: no other mechanism available to Foundry
Local deduplicates the download as well as the disk, and none of them lets an application
share storage without also sharing fate with whoever else is using it.
The integrity row is not hypothetical. We filed a separate issue after a model was
silently corrupted by a download that was interrupted when the machine slept: the file was
left at its full final length with the remainder reading as zeros, and nothing in the SDK
could tell. A content addressed transport cannot produce that state, because a partial
transfer does not match the block hashes and is not mistaken for a complete one.
Sharing without coupling, which a shared cache cannot offer
This is the property that makes packaging different in kind from any cache location, and
it is worth stating plainly because it is easy to miss.
Single instance storage is transparent and reference counted. Each package logically
contains its own complete copy of the model. The platform stores identical blocks once and
tracks how many packages reference them. The application does not opt in, does not
coordinate with anyone, and cannot observe the difference.
The consequences are all the ones an application actually wants:
- The application pins the model version it shipped and was tested against. The model
is package payload, versioned with the application, updated only when the application
updates. Nothing can move it underneath a release that was validated against it.
- Other applications cannot affect it. Another application installing, updating, or
uninstalling the same model cannot remove, evict, downgrade, or lock this one. The
platform reference counts the blocks, so the last reference is what frees them.
- Uninstall is exact. Removing an application releases its reference and nothing more.
There is no shared directory left behind, no ownership question about who is allowed to
delete it, and no orphan.
Compare that with a shared cache keyed on model identity, which is the right answer during
development and the wrong one here. A shared mutable directory used by multiple shipped
applications introduces exactly the problems that packaging avoids: who is allowed to evict
an entry, what happens to application A when application B's cleanup runs, what happens
when two applications want different revisions of the same model, and what a user should do
when it is corrupt. Every one of those is a coupling between applications that were never
written with each other in mind.
MSIX gives the storage and bandwidth savings of sharing with the semantics of a private
copy. That combination is the whole argument, and it is not something Foundry Local could
reasonably build itself.
What the SDK needs to make this work
The good news is that the payload looks ready for it. We inspected a cached model
directory: it contains only immutable content, and every file was written during the
download with nothing modified since.
qwen2.5-7b-instruct-qnn-npu-2\v2\
context_0..3_ctx_qnn.bin 787 MB each
embeddings.onnx 325 MB
lm_head.onnx 325 MB
context_ctx.onnx, iterator_ctx.onnx, tokenizer.json, vocab.json,
merges.txt, genai_config.json, tokenizer_config.json, special_tokens_map.json
The mutable pieces already live above it. foundry.modelinfo.json sits at the cache\models
root rather than inside the model folder, and logs live in a separate logs directory. So
the writable index and the immutable payload are already cleanly separated, which is the
hard part.
What we think is needed:
- Let a model be sourced from a read only directory.
ModelCacheDir can already be
pointed anywhere, and AppDataDir relocates everything, so the configuration surface
largely exists. What is not established is whether a model actually loads when its
directory cannot be written to. We have not been able to confirm this either way, and
it is the single thing this whole request depends on. If it already works, this issue
is mostly a documentation request.
- Keep the writable index separate from the payload. If anything is written into the
model directory during load, it needs somewhere else to go, since package install
directories are read only to the application.
- Document the layout. What an application must place in its package, and how to point
the SDK at it, so that applications do not have to reverse engineer the cache layout.
- Let the resolver see a packaged model so
IsCachedAsync and friends report it as
present rather than trying to download it again.
Alternatives we considered and rejected
Framework packages and optional packages. Both are the textbook Windows answers for
sharing payload between applications, and both are the wrong fit here. Optional packages
must share a publisher with their main package to go through the Store, which rules out
independent vendors sharing a model. Framework packages are cross publisher, but they are
independently versioned and independently serviced, which reintroduces the problem the
application was trying to avoid: the model can move underneath an application that was
tested against a specific one. Neither is removed cleanly when the last consuming
application is uninstalled.
Shipping the model in the application's own package avoids all of that. It uninstalls with
the application, it is reference counted by the platform, and SIS still dedupes it across
unrelated publishers because dedupe operates on block hashes, not on package identity.
Applications cleaning up after themselves. Covered in the companion issue on uninstall.
Briefly: MSIX has no custom uninstall actions by design, and Windows Installer deliberately
leaves runtime created data alone, so this does not work in practice for either packaging
model.
On the objection that the app now owns model updates
Shipping the model in the package does mean the application is responsible for updating it,
and we would argue that is a benefit rather than a cost.
An application whose behaviour depends on a specific model should be able to pin that
model and update it deliberately, as part of its own dependency graph, with its own
testing and its own release. That is how every other dependency an application ships is
handled. It matters most in exactly the settings where on-device AI is most interesting,
such as clinical or regulated software, where "the model changed underneath us" is not an
acceptable answer.
Summary
Every application that uses Foundry Local downloads and stores its own private copy of
every model it uses. For a 7B class model that is several gigabytes per application, and
nothing is shared even when the bytes are identical.
Windows already solves this problem for application payload. An MSIX package is described
by a signed
AppxBlockMap.xmlthat hashes every file in 64 KB blocks. Single instancestorage keys on those hashes, so a block that is already on the machine is stored once no
matter how many packages contain it, and deployment does not fetch a block the machine
already has. The same mechanism covers both the download and the disk.
Model weights are close to the perfect input for it: large, immutable, and byte identical
wherever they appear.
What we are asking for: that Foundry Local support and document loading a model from a
read only directory inside the application's own MSIX package. Two applications shipping
the same model would then cost one copy on disk and one download, each application would
pin the exact model version it shipped and was tested against, and uninstalling an
application would reclaim its model as ordinary package payload.
The duplication is real, and it is byte identical
Measured across every Foundry Local cache on one machine:
This is not a name heuristic. Candidates were grouped by exact byte length and then every
candidate was hashed with SHA-256, which found 55 groups of identical content and 101
redundant copies. Examples:
Every one of those is the same model, downloaded and stored once per application.
Why MSIX is the right mechanism
Shipping the model as package payload changes four things at once, and no application code
is needed for any of them:
Uninstall is listed for completeness and is covered properly in a companion issue, since it
applies to the default location regardless of how models are distributed. The rows that are
unique to packaging are the first two and the last: no other mechanism available to Foundry
Local deduplicates the download as well as the disk, and none of them lets an application
share storage without also sharing fate with whoever else is using it.
The integrity row is not hypothetical. We filed a separate issue after a model was
silently corrupted by a download that was interrupted when the machine slept: the file was
left at its full final length with the remainder reading as zeros, and nothing in the SDK
could tell. A content addressed transport cannot produce that state, because a partial
transfer does not match the block hashes and is not mistaken for a complete one.
Sharing without coupling, which a shared cache cannot offer
This is the property that makes packaging different in kind from any cache location, and
it is worth stating plainly because it is easy to miss.
Single instance storage is transparent and reference counted. Each package logically
contains its own complete copy of the model. The platform stores identical blocks once and
tracks how many packages reference them. The application does not opt in, does not
coordinate with anyone, and cannot observe the difference.
The consequences are all the ones an application actually wants:
is package payload, versioned with the application, updated only when the application
updates. Nothing can move it underneath a release that was validated against it.
uninstalling the same model cannot remove, evict, downgrade, or lock this one. The
platform reference counts the blocks, so the last reference is what frees them.
There is no shared directory left behind, no ownership question about who is allowed to
delete it, and no orphan.
Compare that with a shared cache keyed on model identity, which is the right answer during
development and the wrong one here. A shared mutable directory used by multiple shipped
applications introduces exactly the problems that packaging avoids: who is allowed to evict
an entry, what happens to application A when application B's cleanup runs, what happens
when two applications want different revisions of the same model, and what a user should do
when it is corrupt. Every one of those is a coupling between applications that were never
written with each other in mind.
MSIX gives the storage and bandwidth savings of sharing with the semantics of a private
copy. That combination is the whole argument, and it is not something Foundry Local could
reasonably build itself.
What the SDK needs to make this work
The good news is that the payload looks ready for it. We inspected a cached model
directory: it contains only immutable content, and every file was written during the
download with nothing modified since.
The mutable pieces already live above it.
foundry.modelinfo.jsonsits at thecache\modelsroot rather than inside the model folder, and logs live in a separate
logsdirectory. Sothe writable index and the immutable payload are already cleanly separated, which is the
hard part.
What we think is needed:
ModelCacheDircan already bepointed anywhere, and
AppDataDirrelocates everything, so the configuration surfacelargely exists. What is not established is whether a model actually loads when its
directory cannot be written to. We have not been able to confirm this either way, and
it is the single thing this whole request depends on. If it already works, this issue
is mostly a documentation request.
model directory during load, it needs somewhere else to go, since package install
directories are read only to the application.
the SDK at it, so that applications do not have to reverse engineer the cache layout.
IsCachedAsyncand friends report it aspresent rather than trying to download it again.
Alternatives we considered and rejected
Framework packages and optional packages. Both are the textbook Windows answers for
sharing payload between applications, and both are the wrong fit here. Optional packages
must share a publisher with their main package to go through the Store, which rules out
independent vendors sharing a model. Framework packages are cross publisher, but they are
independently versioned and independently serviced, which reintroduces the problem the
application was trying to avoid: the model can move underneath an application that was
tested against a specific one. Neither is removed cleanly when the last consuming
application is uninstalled.
Shipping the model in the application's own package avoids all of that. It uninstalls with
the application, it is reference counted by the platform, and SIS still dedupes it across
unrelated publishers because dedupe operates on block hashes, not on package identity.
Applications cleaning up after themselves. Covered in the companion issue on uninstall.
Briefly: MSIX has no custom uninstall actions by design, and Windows Installer deliberately
leaves runtime created data alone, so this does not work in practice for either packaging
model.
On the objection that the app now owns model updates
Shipping the model in the package does mean the application is responsible for updating it,
and we would argue that is a benefit rather than a cost.
An application whose behaviour depends on a specific model should be able to pin that
model and update it deliberately, as part of its own dependency graph, with its own
testing and its own release. That is how every other dependency an application ships is
handled. It matters most in exactly the settings where on-device AI is most interesting,
such as clinical or regulated software, where "the model changed underneath us" is not an
acceptable answer.