-
feat(storage): asset registry — allocated ids over the content-addressed blob layer (#1007 part 1) (#1024)
发布于
2026-08-02 02:21:23 +00:00 - refactor(dsl): restate the asset seam as allocated, non-disclosing refs
The asset seam's docstrings described a ref as a handle whose scheme was the
backend's business, and recommended content-addressed hashing so identical
bytes de-duplicate. Both are now wrong in the same place: a content-addressed
ref is a statement about the bytes, so returning one fromputtells the
caller whether those bytes were already stored — an existence oracle over data
the caller never wrote.State the contract the global asset pool needs instead:
putallocates, every
call returns a new ref, and nothing the caller receives reveals whether the
bytes pre-existed. A provider may still de-duplicate internally; that is a
property of its storage layer, not of the ref. Also spell out that the ref
domain is unconstrained, so an unrecognized ref is a miss rather than an error.Comments only — no interface signature changes.
- feat(storage): add the asset registry — allocated ids over the blob layer
Part 1 of the global asset pool (#1007): the browser registry core.
BrowserAssetStoreis now the package's asset backend. It maps an allocated
AssetIdto a registry entry (contentHash,mime,meta), and the registry
maps that hash to bytes, giving three properties at once:- an id survives the bytes behind it being regenerated or replaced, and one set
of bytes can back several ids with different metadata; - identical bytes occupy one row however many ids name them;
- the content hash never leaves the package, so "whoever knows the hash can
reach the bytes" — the threat pure content-addressing has to defend against —
structurally does not arise, and the blob layer beneath can stay global and
metadata-free without being an isolation hole.
putalways allocates a new id and always writes the blob unconditionally, so
the first-store and de-duplication-hit paths run the same statements, issue the
same requests, and return the same shape. The caller cannot infer that someone
else's bytes are already present.Both stores live in one IndexedDB database, so reference counting on
remove
happens inside the transaction that deletes the entry, over an index on
contentHash. IndexedDB serializes readwrite transactions over the same
stores, so a last-reference removal can never reclaim bytes a concurrentput
has just adopted.Images, audio and video share one id space; the medium is a
mimecolumn, not
a partition. There is noprincipalcolumn: a principal is derived server-side
from an authenticated session, and a value the client supplies about itself
authorizes nothing — the column arrives with the server backend.The id domain is opaque and unvalidated, taking the KV key domain's conclusion
as its starting point.toAssetIdbrands without checking; an id this store
never issued is a miss, never an error.BrowserAssetProvideris demoted to an internal blob backend and dropped from
the public exports (0.x, and it had no consumers): its content-addressed ref is
precisely the de-duplication signal an outward API must not emit.Tests split along the same seam.
runAssetStoreContractpins the outward
semantics — allocation, an existence-disclosure matrix compared facet by facet
rather than asserted in prose, reference-counted release, and a table of
foreign ids that must all miss without throwing — whilerunBlobStoreContract
keeps the content-addressed semantics for the layer underneath. Facts the
outward API must not disclose (one blob row per byte string, reclamation at the
last reference, the put/remove race) are asserted against the rows.- chore(packages): bump @openmaic/dsl to 0.5.1 and @openmaic/storage to 0.2.0
storagetakes a minor bump rather than a patch:BrowserAssetProvideris no
longer exported from the package entry point, which is breaking under 0.x
convention even though nothing consumed it.-
fix(storage): address asset registry review findings
-
fix(storage): complete asset registry lifecycle
-
fix(storage): harden asset cache coherence and lifecycle
-
fix(storage): close pending asset mint edges
-
refactor(storage): adopt immutable asset URL snapshots
-
fix(storage): close asset snapshot lifecycle gaps
-
test(storage): pin asset lifecycle contracts
-
docs(storage): document 0.1.x asset upgrade
-
fix(storage): reject incompatible asset database schemas
-
fix(storage): enforce asset allocation independence
-
fix(storage): instrument production asset allocation
-
test(storage): cover allocation ordinals across histories
-
refactor(storage): remove standalone blob backend seam
下载附件