发布

  • feat(storage): asset registry — allocated ids over the content-addressed blob layer (#1007 part 1) (#1024)

    frostbyte_neo 发布于 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 from put tells 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: put allocates, 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.

    BrowserAssetStore is now the package's asset backend. It maps an allocated
    AssetId to 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.

    put always 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 concurrent put
    has just adopted.

    Images, audio and video share one id space; the medium is a mime column, not
    a partition. There is no principal column: 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. toAssetId brands without checking; an id this store
    never issued is a miss, never an error.

    BrowserAssetProvider is 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. runAssetStoreContract pins 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 — while runBlobStoreContract
    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

    storage takes a minor bump rather than a patch: BrowserAssetProvider is 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

    下载附件