Enable storage
Declare the versioned service inmarina.json:
marina.json
src/app.js
fetch with a small test
double for the services your handler uses. Marina injects the real services
when it runs the deployed app; there is no runtime package to install or cloud
credential to copy into the project.
Adding storage to an existing app is a new runtime capability and may require a
publisher to approve the deployment before it becomes live.
Operations
put accepts a string, Uint8Array, ArrayBuffer, or ReadableStream, with
these options:
Object metadata contains the logical key, byte size, ETag, upload time, content
type when set, and custom string metadata.
list returns metadata only; call
head for one object’s metadata, or get or getStream for its body.
Use the returned cursor until truncated is false:
Large objects and streams
get holds the whole object in memory. getStream does not, so use it to
serve large objects:
size fails without storing anything:
Byte ranges
Passrange to get or getStream to read part of an object:
A ranged read also returns
range: { offset, length }, the bytes actually
returned. A range that runs past the end of the object is shortened. A range
that starts past the end of a non-empty object fails with INVALID_INPUT.
The bytes and range always come from one version of the object; if the
object keeps being replaced during the read, the read fails with a retryable
CONFLICT error.
Checksums
Whenput includes sha256, storage computes the digest of the bytes it
received and fails with INVALID_INPUT if it differs. A corrupted or
truncated upload never replaces the existing object.
Files larger than one request
A request body sent to an app can be at most 100 MB. To store a larger file, such as a 1 GB build:- Upload it in fixed-size parts (8 MiB works well), one request per part,
each stored with
put(key, request.body, { size, sha256 }). Retry a failed part on its own. - Record the list of parts in your own data, such as another object or the database.
- Serve the file from a route that reads each part with
getStreamand writes the parts to the response in order. For an HTTPRangerequest, read only the parts the range covers, usingrangeon the first and last part.
Keys and isolation
Keys are relative logical names such asreports/2026/summary.json. Marina
isolates every key to the current app. Storage v1 enforces these rules:
- Keys are at most 900 UTF-8 bytes and use
/as the separator. - Object keys cannot be empty, absolute, or end in
/. - Empty,
., and..path segments, backslashes, and control characters are rejected. - List prefixes may be empty or end in
/, but otherwise follow the same rules.
Namespace isolation does not replace application authorization. If an object belongs to a
particular user, verify the authenticated user before reading or writing it. Avoid using an
untrusted URL path directly as an object key even though Marina blocks traversal patterns.
Limits and usage guidelines
Each request to an app can carry a body of at most 100 MB, and an app has
128 MB of memory shared by the requests it is handling. Stream large bodies
instead of buffering them: several buffered objects at once can exceed the
memory limit even when each is under 64 MiB.
Keep secrets and sensitive user data out of object keys and custom metadata.
Treat names and metadata as identifiers rather than an encryption boundary.
Storage belongs to the app, not to one artifact version. Redeploying, rolling
back, or renaming the app keeps the same data. Deleting an app does not
automatically erase its objects in storage v1, so delete objects explicitly
before deleting the app when removal is required.
Handle runtime errors
Runtime failures have the nameMarinaRuntimeError, a stable code, and a
retryable hint:
INVALID_INPUT, UNDECLARED, or a non-retryable
QUOTA_EXCEEDED error without changing the request.