# SpinStream Artifact Container Spec

## Artifact Container Definition

This definition is derived from the implemented export/mint pipeline in `script.js`, the mint proxy route in `stripe-checkout/index.js`, and ledger recording workers.

### 1) Container structure

The frontend export pipeline builds a multipart payload containing the artifact container components:

- `index.html` (canonical exported HTML snapshot)
- `html` (same HTML, backward-compatibility field)
- `meta.json` (attached under `file`)
- `metadata.json` (same JSON attached under `metadata`)
- `asset:<role>:<fileName>` for each uploaded/exported asset
- optional `ownerKey`

This is assembled in `freezeAndUpload()` and POSTed to `/api/mint`.

#### On-storage layout at finals
The expected minted layout is:

```text
/finals/{slug}/
  index.html
  meta.json
  assets/{...}
```

Notes from implementation:
- `script.js` treats the returned URL as `finalUrl`/`mintedUrl` and derives cover at `/finals/{slug}/cover.png`.
- The repo’s `/api/mint` route is a proxy to an external mint worker (`MINT_WORKER_ORIGIN`), so exact backend write semantics for *all* files are external, but frontend and docs consistently operate on `index.html + meta.json + assets/*`.

### 2) Artifact identity mechanism

Artifact identity in this codebase has three distinct parts:

1. **Mint ID (frontend identity token)**
   - Primary source: `mintIdInput` (user-provided, normalized/validated).
   - Fallback generation: `generateMintId()` uses timestamp components + `Math.random()` suffix.
   - Persisted in DOM attributes (`body.dataset.mintId`) via `ensureMintMetadata()`.

2. **Final URL / slug (backend identity path)**
   - Frontend receives minted URL from mint response fields (`finalUrl`, `mintedUrl`, etc.).
   - Slug generation itself is **not** implemented in this repository; it occurs in the external mint worker behind `/api/mint`.

3. **Timestamp metadata**
   - `ensureMintMetadata()` sets `mintedAt` from `mintDateInput` if valid, otherwise current time.
   - `mintDate` is also captured in mint metadata fields and meta tags.

### 3) Deterministic export mechanism

Export is deterministic relative to finalized DOM state + selected files at mint time:

- `buildInlineExportHtml()` explicitly snapshots a cloned sanitized DOM (`document.documentElement.cloneNode(true)` then `outerHTML`).
- It injects explicit exported-state marker(s):
  - `data-exported="true"`
  - `data-mint-state="minted"`
- It applies sanitization guards (`assertNoHelperIdentifiers`, `verifyExportSanitization`) and aborts mint on sanitization failure.
- It hides editor-only controls and enforces exported view behavior via injected export-only CSS and metadata tags.

Determinism boundaries:
- Deterministic container assembly is evident in frontend export code.
- Mint ID fallback uses randomness (`Math.random()`), so ID creation is not purely deterministic when auto-generated.
- Backend slug derivation and any server-side transforms are external to this repo.

### 4) Metadata schema

Metadata exists in three layers:

1. **Runtime metadata object** from `getMintMetadata()`:
   - `mintId`, `title`, `artist`, `description`, `mintDate`, `type`, `tags`, `tagsRaw`, `license`, `links`, `coverImageFile`, `coverImageName`.

2. **Embedded HTML metadata** in exported `index.html` via `addExportMetadataToClone()` meta tags:
   - `spinstream:mint-id`
   - `spinstream:mint-title`
   - `spinstream:mint-artist`
   - `spinstream:mint-date`
   - `spinstream:mint-description`
   - `spinstream:mint-type`
   - `spinstream:mint-tags`
   - `spinstream:mint-license`
   - `spinstream:mint-links`

3. **Mint worker payload metadata JSON** (`meta.json` and `metadata.json` carry same structure):

```json
{
  "artistName": "...",
  "mintMetadata": { "...": "..." },
  "assetRoles": { "fileName": "role" },
  "exportAssetPaths": { "role": "assets/fileName" }
}
```

Additionally, exported `index.html` includes a lock-gate script that fetches `./meta.json` and checks `status`/`expiresAt` to determine lock-screen rendering.

### 5) Ledger integration

Ledger anchoring path expects:

```json
{ "artifactHash": "0x...64hex", "mintedUrl": "https://..." }
```

Two worker surfaces implement record calls:

- `workers/spinstream-ledger/src/index.js`
  - accepts `/api/record-ledger`, `/record-ledger`, `/ledger/record-ledger`, `/`
  - validates required fields
  - executes `contract.record(artifactHash, mintedUrl)`
  - returns tx hash, block number, and ledger timestamp from Polygon block data

- `stripe-checkout/index.js` (`/api/record-ledger`)
  - validates hash/url format
  - executes `contract.record(artifactHash, mintedUrl)` with timeout handling
  - returns chain metadata and tx details

Important implementation fact:
- Artifact hash **generation** is not present in `script.js` mint/export path in this repository snapshot. The ledger workers consume `artifactHash`; they do not derive it from artifact bytes locally.

### 6) Viewer structure (`index.html` as artifact viewer)

The finalized artifact viewer is `index.html` served from the minted finals URL.

Viewer behavior comes from export output + runtime script behaviors:

- Export enforces artifact mode with `data-exported="true"` and hides creation UI.
- Export preserves/rewrites asset references to stable paths (e.g., `assets/...` for uploaded media, absolute paths for system assets as needed).
- Export keeps script wiring so the page remains interactive as a viewer, while export markers and CSS suppress editor controls.
- Lock-gate logic in exported HTML can replace body content with an expiration screen using metadata from `meta.json`.

Net effect:
- `index.html` is both the artifact payload and the primary artifact viewer, with state controlled by explicit export markers and metadata, not editor-only mode assumptions.
