Out-of-Band Content Transfer

A GitSync content sync mirrors a Space's git-committed content/** binaries into the Space's content collection. Until now the bytes travelled inline, on the SyncContentFilesRequest itself.

#2885 stopped the producer building one delivery per Space, so a delivery is now ≀ budget + largest single file. That closed the aggregate axis and left one residual, which that page records against itself:

A single file larger than the budget travels whole. […] A file that large belongs behind a content-store handle rather than inline.

This page is that handle. A file whose packaged cost alone exceeds ContentDeliveryBudget.BudgetBytes no longer rides the message: its bytes go into the destination collection once, and the delivery carries a content-addressed reference to them.

Why the residual could not be closed by moving a number

ContentDeliveryBudget.BudgetBytes is DeliveryPayloadBounds.MemoryStreamBlockBytes = 1,048,576 β€” Orleans' memory-stream block size, hard-coded in MemoryAdapterFactory with no configuration surface. It is not a knob, and even if it were, raising it is the move Oversized Delivery Refusal exists to forbid.

The scale is not an edge case. Measured on Systemorph/MeshWeaver.Education@f7ae723 (2026-09-04, unchanged from the 2026-09-03 measurement on 61cbbac):

Space files over budget total largest packaged
AgenticEngineering 25 12 101.2 MB 13,188,871
AgenticBusiness 9 4 27.2 MB 10,910,243
AgenticPrimerDe 7 3 10.9 MB 4,291,888
AgenticPrimer 7 3 9.8 MB 3,873,652
DataModeling 3 1 8.6 MB 10,929,144
AdvancedBusinessRules 2 1 9.5 MB 12,540,448
AgenticOffice 3 1 8.2 MB 10,224,527

Every Space in the repo has at least one file over budget β€” 25 in total. 🚨 The axis is "has a video", not "is large": AdvancedBusinessRules totals 9.5 MB β€” one of the smallest Spaces there β€” and carries the second-largest single file. Sorting Spaces by total size does not identify the affected set.

Where the bytes land

In the destination collection itself, under a reserved staging folder β€” ContentStaging.Folder (_staging/), at the collection root.

That choice is not arbitrary; it is the only location that needs no new configuration and no new assumption about the deployment:

The producer reaches the destination collection the way MeshOperations.Upload and the content route already do: it asks the owning node's hub for the collection config with a GetDataRequest(ContentCollectionReference) β€” a few hundred bytes β€” registers it locally under the qualified name {nodePath}/{collection}, and resolves a provider over it. Only the config crosses the mesh; the bytes never do.

producer (import hub)                       receiver (Space-root node hub)
──────────────────────                      ──────────────────────────────
GetDataRequest(collection) ───────────────▢ config   (a few hundred bytes)
       ◀─────────────────────────────────── ContentCollectionConfig
write _staging/{sha256}  ══════▢ content store ◀══════ read _staging/{sha256}
SyncContentFilesRequest{ StagedFiles:[…] } β–Ά SaveFile(videos/intro.mp4)
       ◀─────────────────────────────────── ImportContentResponse
delete _staging/{sha256} ══════▢ content store

What the handle is

public record StagedContentFile(string Path, string Handle, long Length);

SyncContentFilesRequest carries them in a new StagedFiles list beside Files. A sync with no over-budget file produces a request byte-for-byte identical to what it produced before β€” StagedFiles is null and nothing else changes.

Content-addressing is what makes the transfer idempotent. Two files with identical bytes stage once. A sync that runs twice writes the same blob at the same key and the same file at the same destination path β€” no duplication anywhere. A staged blob that is already present with the right length is not rewritten, so a retry after a partial run does not re-copy 100 MB over SMB.

Who owns the lifetime

The producer owns every blob it stages, from Post() to the last delivery's answer.

How the receiver resolves a handle

ContentImportExtensions.SyncFiles writes the inline files exactly as before, then writes the staged ones:

target.GetContent($"{ContentStaging.Folder}/{staged.Handle}")
    .SelectMany(stream => stream is null
        ? Observable.Throw<int>(new InvalidOperationException(
            $"Staged content '{staged.Handle}' for '{staged.Path}' is not in the collection's "
            + "staging area β€” the out-of-band transfer did not complete."))
        : target.SaveFile(dir, name, () => stream).Select(_ => 1))

Every leaf runs on the collection's own IIoPool; the hub action block only subscribes and returns. The bytes are streamed from the staging blob into the destination file β€” they are never materialised as a byte[] on the receiver, which is the whole point.

Failure behaviour stays honest

Content Sync Visibility's entire contribution was making a refused sync observable. This change must not trade a loud refusal for a quiet success, so:

What this does not change

Rules

Reconnecting…
The server was updated. Reloading the page to pick up the latest version.