# Architecture

av-tools is a static, local-first React application. The browser receives code
and the two self-hosted FFmpeg cores; media bytes do not leave the origin
through an application backend. Toolbox Portal assembles the already-built
artifact and never compiles its source.

## Layers and boundaries

```text
Toolbox AppShell / standalone context
  └─ React application
      ├─ Quick Convert and sequential Edit workflows
      ├─ project + typed preset state
      ├─ media inspection / preview / result views
      ├─ bounded application queue + resource policy
      └─ typed command plans
          └─ EngineManager
              ├─ @ffmpeg/ffmpeg wrapper worker
              ├─ ST or MT same-origin core
              └─ per-job virtual filesystem

Browser storage boundary
  ├─ object URLs (ephemeral previews)
  ├─ IndexedDB (validated user presets)
  └─ OPFS cache (optional derivatives; never source in the current app)
```

- `src/toolbox/` owns the one typed manifest and suite integration.
- `src/media/` normalizes ffprobe JSON, duration/timecode, stream selection,
  playback MIME decisions and safe names.
- `src/project/` owns the versioned, non-destructive project document and
  migrations. A source is represented by identity/metadata and must be
  reattached after load; the current application does not persist source bytes.
- `src/presets/` defines typed built-ins and validates user presets against
  detected capabilities.
- `src/commands/` converts validated operations to a `CommandPlan`. A plan has
  generated input/output paths, argument arrays, expected outputs,
  capabilities and cleanup intent; it is not a shell string.
- `src/ffmpeg/` selects/loads the engine, discovers capabilities, mounts
  inputs, executes one job, reads outputs, sanitizes diagnostics and cleans.
- `src/jobs/` provides typed queue, progress, cancellation, recovery and
  durable-metadata infrastructure. The mounted application scheduler is a
  bounded in-memory promise chain around `EngineManager`.
- `src/waveform/`, `src/storage/` and `src/export/` keep bounded analysis,
  persistence and browser-download responsibilities outside the UI.

The DOM receives normalized values as text/attributes. It never renders media
metadata as HTML. Command builders never accept arbitrary virtual paths or a
raw command field.

## FFmpeg lifecycle

1. Inspect secure-context, worker, `crossOriginIsolated` and
   `SharedArrayBuffer` availability.
2. Select MT only when all MT prerequisites are true (or ST when explicitly
   selected). A failed MT load is terminated and retried once with ST.
3. Load wrapper/core assets via base-relative, same-origin URLs.
4. Query version/build configuration/formats/codecs/encoders/filters
   sequentially and publish immutable capability state.
5. For one job, validate resource estimates and structured settings, allocate
   a generated directory, mount/write inputs, run a generated argument array,
   read only declared outputs, and re-probe applicable results.
6. Return result bytes and an export report, then unmount/delete virtual files.
7. Retain the idle engine for the next sequential job; terminate it on mode
   changes, unrecoverable errors or cancellation.

The FFmpeg instance is a single process. `maxConcurrentFFmpegJobs` is one and
no probe/export operations overlap on it. The application-facing scheduler is
also bounded: it retains at most eight scheduled operations in memory,
including the active operation, and advances its promise chain after either
success or failure. Queue state is deliberately not persisted across a page
reload.

## Project and edit model

A project is versioned JSON containing source descriptors, a sequential clip
timeline, non-destructive clip in/out and transform/audio settings, stream
selection, metadata/chapter/subtitle edits and export preset references. Clips
do not overlap. There is no arbitrary compositing graph, collaborative state or
server-side document.

The structural-export panel maps the selected clip's current range to either a
stream-copy or preset-backed trim plan. It maps the visible timeline order to
either a compatibility-checked stream-copy concat plan or a normalized
re-encode plan. Normalized concat requires the user to choose what happens when
some clips lack audio; unchecked fast compatibility fails closed.

Migrations are explicit version-to-version functions. Unknown future versions,
invalid shapes and unsafe source references fail with actionable validation
errors. Source bytes are not embedded in normal project JSON. Reattachment
matches stable descriptors and asks the user before associating a local file.

## Command plans and filesystem

Plans use arrays such as `['-i', generatedInput, ...]`; no shell evaluates
them. Filtergraph builders escape values for their exact FFmpeg grammar.
Generated paths contain a job id plus bounded safe basename and never reuse a
user-controlled directory. Expected output paths are declared before
execution, so the result reader cannot enumerate arbitrary output.

Small inputs may be copied into FFmpeg's in-memory filesystem. Where supported,
WORKERFS mounts `File`/`Blob` inputs to avoid an initial JavaScript copy; it
does not remove WASM address-space/output constraints. OPFS is optional for
regenerable derivatives and uses asynchronous origin-private file handles with
an in-memory fallback. See `docs/STORAGE.md`.

## Result flow

Output bytes become a Blob with an operation-derived filename and MIME type.
The UI creates a short-lived object URL only when the browser can preview that
container/codec, exposes an explicit save/download action, and includes probe
data plus warnings in the export report. Revoking a preview does not invalidate
the retained Blob until the result is cleared. Batch derivatives may be
assembled into a bounded ZIP.

For unsupported direct playback, a preview proxy is a disposable H.264/AAC MP4
derivative rather than a source mutation. Duration and maximum width are
bounded by validated user controls. Its cache identity includes the source
fingerprint and proxy settings, so only an exact matching derivative is reused;
cache failure falls back to normal generation.

## Cancellation and recovery

ffmpeg.wasm cancellation terminates the active wrapper/core worker rather than
pretending a native FFmpeg process can receive a signal. The job becomes
cancelled, outstanding promises are rejected, object URLs/mounts are released
where possible, and that engine generation is discarded. The next job creates
a fresh engine and re-runs capability discovery. A cancellation is complete
only after the stale worker cannot publish a result.

## Integration boundaries

`AppShell` owns suite chrome, theme/personalization, help and app switching.
av-tools passes `manifestUrl="./toolbox-app.json"` and works when context is
missing, malformed or unavailable. It does not embed another switcher.

The manifest advertises secure context, workers, IndexedDB and top-level
navigation. It does not require cross-origin isolation because ST is a valid
fallback. Portal receives only the deterministic ZIP/checksum and places it
under a locked target such as `apps/av/`.
