Sample mattersEvery client, case, and document on this deployment is invented for demonstration. This is purposefully a demo environment.

A document's bytes and authorization are separate

Inside the documents bucket, the storage key is content-addressed: store::documents::ingest_bytes writes bytes at blobs/<sha256>. The Surreal asset row is the Project-scoped record: it carries the Project association, provenance, visibility, and the reference to that object. The portal checks that row before streaming a document, so clients never receive bucket credentials or direct object URLs.

The ingest deduplication decision is scoped to the Project, even though the key shape is not: an existing matching asset on the same Project avoids a second write, while a matching asset on another Project does not authorize reuse of its row. Because two rows can name the same content-addressed key, a governed expunge checks whether another asset row still references the object before deleting its bytes. That reference check keeps a deletion in one Project from removing bytes still needed by another.

This is why Project growth does not require a documents bucket per Project. The bucket is a private deployment lane; the Surreal record supplies the Project boundary, and the application supplies the authorization boundary. The applications lane uses a different shape: each published portal is under its Project-code prefix and the publisher's IAM condition enforces that prefix.