Storage & retention
Storage is one of the larger billing line items for live-streaming workloads, especially when you use the 24/7 DVR model. FrameWorks lets you run DVR continuously and control cost with retention defaults and per-asset overrides: completed assets expire on a tenant default, and individual DVR / clip / VOD assets can be kept longer or shorter.
What gets stored, when
Section titled “What gets stored, when”| Asset type | Created when | Storage starts at |
|---|---|---|
| DVR | A record: true stream goes live (or startDVR) | First segment is written |
| Clip | createClip against a live stream or DVR recording | Asynchronous materialization |
| VOD asset | createVodUpload → completeVodUpload | Multipart upload completes |
DVR is the largest contributor for most accounts because a single 24/7 ingest can produce many hours of media per day.
Resuming a VOD upload
Section titled “Resuming a VOD upload”Keep the opaque session ID returned by createVodUpload and pass it unchanged
to upload status, completion and abort operations. The session stays with its
original storage-owning cluster even if your cluster preference changes while
the upload is running. Do not construct a session ID from an S3 multipart ID or
a playback ID. If the owner is unavailable, retry with the same session when it
recovers; the upload is not silently moved to another cluster.
Live DVR window vs retention
Section titled “Live DVR window vs retention”These are separate concepts and they’re often conflated:
- Live DVR window is how far back a live viewer can seek while the stream is still recording. It’s bounded by your tier and cluster so live HLS playlists stay small. This is not retention — the segments behind the window are still written to storage.
- Continuous archive is the full segment timeline of a stream session. It can be hours, days, or months long if the ingest stays live.
- Retention is how long a completed recording, clip, or VOD upload is kept. Retention is what controls long-term storage cost.
- Active DVR is not deleted by retention. Retention starts after the recording finalizes.
Per-class system defaults
Section titled “Per-class system defaults”Each asset class has its own default. VOD uploads are user-created library assets and default to keep forever. DVR recordings and clips default to 30 days because live recordings are typically ephemeral and clips are stream-derived.
| Asset class | System default |
|---|---|
| VOD upload | Keep forever |
| DVR | 30 days |
| Clip | 30 days |
Tier cap
Section titled “Tier cap”Every tier carries a single retention cap (bounds.maxRecordingRetentionDays on mediaRetentionPolicy). On paid tiers it is 0 — no cap. Free has a finite cap as an anti-abuse guardrail. The cap applies differently per level:
- Tenant default (
setMediaRetentionPolicy): a value above the cap is rejected with anInvalidArgumenterror.0(“keep forever”) is stored as the cap. - Per-stream and per-asset overrides: values above the cap, and
0, are clamped to the cap, so a Free tenant who sets a per-asset override of 365 days lands at the Free cap instead. - System default: clamped to the cap when the retention is resolved.
Cascade
Section titled “Cascade”Effective retention for a new DVR, clip, or VOD asset is resolved in this order:
- Per-asset override (set via
updateMediaRetentionon a finalized asset) - Per-stream override (DVR / clip only — set via
setStreamRetentionOverrides) - Per-class tenant default (set via
setMediaRetentionPolicy) - Per-class system default (VOD: keep forever; DVR/clip: 30 days)
- Tier cap (clamps the result;
0= no cap on paid tiers)
The resolved value is snapshotted onto the artifact at creation / DVR start. Subsequent overrides update the artifact directly. Resetting a per-asset override re-anchors to the asset’s ended_at, so a 60-day-old clip reset to a 30-day default goes back to the same horizon it would have had under the cascade — not 30 days from today.
Tenant default policy
Section titled “Tenant default policy”Read the per-class defaults plus the values the cascade resolves to today and the tier cap:
query MediaRetentionPolicy { mediaRetentionPolicy { defaultVodRetentionDays # null = inherit system default defaultDvrRetentionDays defaultClipRetentionDays effectiveVodRetentionDays # what cascade resolves to today (0 = forever) effectiveDvrRetentionDays effectiveClipRetentionDays bounds { maxRecordingRetentionDays # 0 = no cap (paid baseline) } updatedBy updatedAt }}Set a tenant default for one asset class:
mutation SetMediaRetentionPolicy($input: SetMediaRetentionPolicyInput!) { setMediaRetentionPolicy(input: $input) { ... on MediaRetentionPolicy { effectiveVodRetentionDays effectiveDvrRetentionDays effectiveClipRetentionDays } ... on ValidationError { message field } }}Variables — “keep VOD forever, expire DVR after 90 days, expire clips after 14”:
{ "input": { "targetType": "VOD", "days": 0 } }{ "input": { "targetType": "DVR", "days": 90 } }{ "input": { "targetType": "CLIP", "days": 14 } }Clear a class default (fall back to system default):
{ "input": { "targetType": "DVR", "clear": true } }The webapp surfaces these on /account/storage under “Storage retention defaults”.
Per-stream override
Section titled “Per-stream override”DVR and clip retention can be overridden per stream. VOD uploads are not stream-bound and have no per-stream knob.
mutation SetStreamRetentionOverrides($input: SetStreamRetentionOverridesInput!) { setStreamRetentionOverrides(input: $input) { ... on StreamRetentionOverrides { streamId dvrRetentionDaysOverride clipRetentionDaysOverride } ... on ValidationError { message field } }}Each pair (value, clearXxxOverride) governs one column. To clear a previously-set override, pass clearDvrRetentionOverride: true (likewise for clip). Unset fields are left alone.
Per-asset override
Section titled “Per-asset override”Apply an override to a single finalized DVR, clip, or VOD asset. Override beats per-stream beats tenant default. Set targetType to DVR, CLIP, or VOD:
mutation UpdateRetention($input: UpdateMediaRetentionInput!) { updateMediaRetention(input: $input) { ... on EffectiveRetention { retentionDays retentionUntil source } ... on ValidationError { message field } ... on NotFoundError { message } }}Variables (DVR, 90 days): { "input": { "targetType": "DVR", "targetId": "<dvr id or hash>", "retentionDays": 90 } }
Variables (clip, keep forever): { "input": { "targetType": "CLIP", "targetId": "<clip id or hash>", "retentionDays": 0 } }
Variables (VOD, absolute): { "input": { "targetType": "VOD", "targetId": "<vod id or hash>", "retentionUntil": "2026-12-31T23:59:59Z" } }
Active recordings / in-flight uploads are rejected — overrides apply only to finalized assets. retentionDays: 0 is honored only on uncapped (paid) tiers; Free clamps it up to the cap.
Clear the override and recompute from the cascade:
mutation ResetRetention($input: ResetMediaRetentionOverrideInput!) { resetMediaRetentionOverride(input: $input) { ... on EffectiveRetention { retentionDays retentionUntil source } }}Effective retention on an asset
Section titled “Effective retention on an asset”DVRRequest.effectiveRetention, Clip.effectiveRetention, and VodAsset.effectiveRetention return the resolved horizon for a finalized asset (null while active for DVR):
query DVRRetention($id: ID!) { storageArtifactsConnection(input: { kinds: [DVR], streamId: $id }) { nodes { hash expiresAt effectiveRetention { retentionDays retentionUntil source } storageCost { perDay perMonth currency } } }}storageCost is the marginal monthly amount the asset is adding to the tenant’s bill at the current tier. Null when pricing is unavailable (self-hosted or third-party-marketplace cluster — operator-absorbed).
Agent / MCP
Section titled “Agent / MCP”Agents manage retention via five MCP tools (cost-affecting; destructive-adjacent for update_asset_retention):
Retention writes require a tenant owner or admin with billing:write; reading
the effective policy remains available with the retention read permission.
| Tool | Effect |
|---|---|
get_retention_policy | Reads tenant per-class defaults + tier cap + effective values. |
set_retention_policy | Writes a per-class tenant default (target_type, days, or clear). |
set_stream_retention_overrides | Writes per-stream DVR / clip overrides; clear_*_override flags reset a class to the tenant default. |
update_asset_retention | Per-asset override on a finalized DVR / clip / VOD (target_type + target_id; days=0 = keep forever). |
reset_asset_retention | Clears a per-asset override and re-anchors to the asset’s ended_at via the cascade. |
See agents/mcp for the full tool catalog.
Cost awareness
Section titled “Cost awareness”Retention is the main storage-cost control for always-on DVR, clips, and VOD. The storageCost field on each asset surfaces the marginal $/month it adds to the bill, so customers can decide whether to shorten retention or delete specific assets to recover cost. Shorter retention reduces stored bytes sooner; longer retention keeps more archive available. Use /account/storage to review current usage and adjust defaults before enabling long-running 24/7 DVR at scale.
Tenant policy changes emit media.retention_policy.changed. Per-asset changes emit media.retention.override_applied and media.retention.override_reset with the asset hash and new retention horizon.
Related
Section titled “Related”- Recordings, Clips & VOD — DVR / clip / VOD lifecycle
- Billing — tier pricing and storage line items
- Platform capabilities — where retention is exposed