# Storage and cleanup

av-tools separates source bytes, engine files, derivatives and project
metadata. The default policy is deliberately non-persistent for source media.

## Object URLs

Browser preview and result playback use `URL.createObjectURL(File|Blob)`.
Object URLs reveal bytes only to the current browser context but retain the
underlying object until revoked. Each owner revokes its previous URL when a
source/result changes and all URLs on project close/unmount. A preview failure
does not trigger an upload or external fallback.

## FFmpeg virtual filesystem

Every execution receives a generated job directory. Inputs are copied to the
in-memory filesystem or mounted read-only through WORKERFS where browser/core
support is verified. Outputs are always in generated paths. The runner reads
only declared outputs, then deletes output/input/control files and unmounts.

WORKERFS can avoid an initial JavaScript byte copy; FFmpeg decoding and output
still consume WASM/browser memory. Cancelling terminates the engine, so cleanup
also discards that entire virtual filesystem.

Each probe/export records its actual input mode and byte accounting. A
successful WORKERFS mount reports zero copied input bytes; if mounting fails,
the bounded compatibility fallback reports the exact bytes copied into engine
memory. This is runtime evidence rather than a speculative preflight claim.

## IndexedDB

The mounted application currently stores validated user presets in IndexedDB.
The codebase also contains a durable job-metadata store, but the application
queue described in the UI is deliberately in memory and does not instantiate
that store. Project documents are validated, versioned JSON that the user
explicitly saves and loads; they are not currently auto-saved to IndexedDB.
Media source bytes are not written there by default.

Clearing site data removes these records. Private browsing and quota eviction
may remove them earlier; the app must treat storage as recoverable cache, not
the only copy of an important user preset.

## Origin-private file system

OPFS is optional for bounded preview proxies, waveform peaks, thumbnails and
other regenerable derivatives. The current implementation uses asynchronous
origin-private file handles and falls back to a 128 MiB least-recently-used
in-memory cache if OPFS is unavailable or fails. A derivative key includes a
source fingerprint, operation, relevant settings and a cache-schema version;
waveform keys additionally identify the selected stream and analysis settings.

Preview-proxy settings are part of that identity. The current UI validates a
1–300 second maximum duration and a 480, 720, 960, 1280 or 1920 pixel maximum
width. A matching local derivative is reused without FFmpeg execution; changing
the source or either setting produces a different cache lookup. A cache miss or
cache failure falls back to generation and never mutates/persists the source.

The application works without OPFS. The storage panel reports the browser's
origin-wide quota estimate when available and offers per-entry or global cache
deletion. The current OPFS layer relies on browser quota enforcement rather
than pre-reserving space; a failed cache write switches subsequent cache work
to the bounded in-memory fallback. OPFS writes close their
`FileSystemWritableFileStream` on success and abort it on error.

OPFS is private to the current **origin**. Moving an app from one hostname,
scheme or port to another does not move cached data. Toolbox and standalone
deployments at different origins have different caches even if their files are
identical.

## Source persistence policy

Normal import retains the user-selected `File` only in the active page. Saved
projects contain source descriptors/fingerprints and prompt for reattachment;
they do not embed source bytes. If a later UI offers “keep source on this
device,” it must be explicit, show estimated/quota usage, identify the origin,
allow per-source deletion and never be silently enabled by a project import.

Downloads are explicit user actions. The File System Access API, when
available, writes only after the user selects a target; Blob download remains
the fallback.

## Cleanup matrix

| Resource                 | Normal completion                                    | Cancellation/error                                       | User control            |
| ------------------------ | ---------------------------------------------------- | -------------------------------------------------------- | ----------------------- |
| source/result object URL | retained only while displayed, then revoked          | revoked when owning state clears                         | clear source/result     |
| FFmpeg job files/mount   | delete/unmount in finalization                       | engine termination discards FS; stale generation ignored | cancel job/reset engine |
| result Blob              | retained until replaced/cleared/download page closes | failed partial output is not published                   | clear result            |
| IndexedDB user presets   | retained intentionally                               | transaction rollback                                     | delete/reset presets    |
| OPFS derivative          | retained until expiry or explicit deletion           | failed write is not indexed; cache switches to memory    | delete item/clear all   |

The storage screen reports approximate usage; browser quota figures remain
estimates and are not a durability promise.
