File
Every node and relation can have at most one file attached, addressed through the element’s own id at /<uuid>/file (see the file endpoints). There is no separate identity for the file itself - it exists only as long as the element it is attached to exists, and is deleted automatically as a side effect of DELETE /<uuid>.
Access
A file has no access rules of its own. All access operations (read, create, update, delete) resolve to whatever access the current user has on the owning element - see the access model for how that is determined.
Size limits
A file can be anywhere from zero bytes up to file.maxFileSizeInBytes, an operator-configured setting (see the configuration reference) that defaults to 10 GiB.
This setting is bounded by the storage backend in use. Ember Nexus currently supports S3-compatible storage only, and S3 imposes its own hard technical maximum object size of 5 TiB, on top of separate hard limits on chunk size and count that only matter for resumable uploads - see Limits in the upload reference for those. Operator configuration is validated against all of these technical limits at server startup, and a configuration that violates any of them is a startup error, not a runtime one - the server refuses to start rather than accept requests it can’t actually service.
A request whose body (direct upload) or declared/reassembled length (resumable upload) exceeds maxFileSizeInBytes is rejected with 400 Bad Request.
Metadata
Uploading a file sets two properties on the owning element automatically: a hasFile boolean, and a file object holding everything else. Here is a Data node with a file attached, as returned by GET /<uuid>:
{ "type": "Data", "id": "7ac83b11-3c48-4ecd-930c-97e9dbf47ef3", "data": { "created": "2025-09-27T10:39:51+00:00", "updated": "2025-11-03T08:12:04+00:00", "name": "image.jpg", "hasFile": true }, "file": { "contentLength": 63933, "extension": "jpg", "mimeType": "image/jpeg", "hash": { "sha256": "3a7bd3e2360a3d8f2f7929d2e83beb2b5b9e5b0e3e6b4c9a1a0e3c1b0e4d3f2b" } }}This example happens to be a node, but relations carry a file exactly the same way - hasFile and file are ordinary element properties, and every endpoint on this page works identically regardless of whether <uuid> addresses a node or a relation.
The file object itself - the same shape shown above, collapsed to just that property:
{ "contentLength": 63933, "extension": "jpg", "mimeType": "image/jpeg", "hash": { "sha256": "3a7bd3e2360a3d8f2f7929d2e83beb2b5b9e5b0e3e6b4c9a1a0e3c1b0e4d3f2b" }}contentLength- the file’s size in bytes.extension- taken from the filename in theContent-Dispositionrequest header the file was uploaded with, orbinif none could be determined. There is no separately stored filename:GET /<uuid>/filereconstructs the downloaded file’sContent-Dispositionheader from the element’s ownnameproperty (or its id, if the element has none) plus this extension.mimeType- the content type detected from the uploaded bytes.hash.sha256- the file’s SHA-256 hash as a lowercase hex string (see Verifying integrity below - theRepr-Digestheader uses a different encoding of the same hash).
hasFile and file can not be set or changed directly through the element’s own data - only replacing the file (or deleting it, which removes both properties again) changes them, as a side effect.
Conditional requests
File endpoints support If-Match and If-None-Match against an ETag derived from the file’s content, the same mechanism used by element endpoints - see the caching and conditional requests guide for how ETags behave in general. PUT /<uuid>/file is the endpoint where this matters most in practice, since it silently replaces an existing file - If-Match lets a concurrent writer detect that the file changed since it was last read.
Verifying integrity
Every download’s response includes a Repr-Digest header with the file’s SHA-256 hash, in RFC 9530 structured-field format (sha-256=:[base64]:), letting a client verify the downloaded bytes without a separate request or checksum file. It is omitted for files uploaded before this header existed, since no hash was stored for them at the time.
The same mechanism works in reverse on upload: a client can send a Repr-Digest header (or Content-Digest, as a fallback name) with the expected SHA-256 digest. For a direct upload (POST /<uuid>/file/PUT /<uuid>/file), the server hashes the request body before writing anything to storage and compares it to the supplied digest; a mismatch fails with 400 Bad Request and nothing is written or replaced. For a resumable upload, the digest is checked on the completing PATCH /upload/<uuid> request against the hash of the fully reassembled file - see the upload reference for how that hash is computed incrementally across chunks.
Only SHA-256 is supported; a digest header naming a different algorithm is rejected the same way as a mismatched hash.
Partial downloads with Range
GET /<uuid>/file accepts a Range header to download part of a file instead of the whole thing, following RFC 9110 §14. Only a single byte range is supported per request, in one of three forms: bytes=<start>-<end>, bytes=<start>- (from start to the end of the file), or bytes=-<suffixLength> (the last suffixLength bytes).
A satisfiable range responds with 206 Partial Content and a Content-Range header describing what was actually returned. An end beyond the file’s actual length is clamped to the file’s length rather than rejected. Accept-Ranges: bytes is always present on responses from this endpoint, even a full, non-ranged 200 OK, so a client can detect range support ahead of time without a prior request.
A comma-separated list of ranges (multi-range requests) is not supported and fails with 400 Bad Request. A range whose start lies beyond the end of the file, or that spans zero bytes, fails with 416 Range Not Satisfiable.
Ambiguous 404s
GET /<uuid>/file responds with 404 Not Found both when the element does not exist (or is not accessible to the current user) and when the element exists but simply has no file attached - the two cases are indistinguishable from the response alone, by design, so that the existence of an element can’t be inferred from whether it happens to have a file.