# `.easel` transcript format, version 1 An `.easel` file is UTF-8 JSON Lines: one session header, followed by ordered messages, artifact references and selected progress events. Every line ends in LF, including the final line. It is plain text, appendable, and queryable with `jq`; it is not a ZIP archive or an opaque provider session. ```jsonl {"type":"session","format":"aesthetic.easel","version":1,"id":"demo-session","createdAt":"2026-09-15T12:00:00.000Z","metadata":{"medium":"sound","projectId":"project-one","title":"Bell phrase"},"consent":{"sharing":"private"},"provenance":{"application":"easel","version":"0.7"}} {"type":"message","id":"turn-one","seq":1,"at":"2026-09-15T12:00:01.000Z","role":"user","text":"Make a bell phrase","backend":"ac","model":"glm"} {"type":"artifact","id":"revision-one","seq":2,"at":"2026-09-15T12:00:04.000Z","artifactId":"sound-one","medium":"sound","revision":1} {"type":"message","id":"turn-two","seq":3,"at":"2026-09-15T12:00:05.000Z","role":"assistant","text":"The bell phrase is ready.","backend":"ac","model":"glm"} ``` ```sh jq -r 'select(.type == "message") | "\(.role): \(.text)"' session.easel jq -c 'select(.type == "artifact")' session.easel ``` IDs are stable strings of 1–80 ASCII letters, digits, hyphens or underscores. Record sequence numbers increase monotonically; upload batches can omit earlier sequence numbers. The session header has no sequence number. Record timestamps are ISO date strings. Session `metadata` permits only `medium`, `projectId` and `title`; project IDs are opaque IDs, never filesystem paths. Media are `picture`, `sound`, `piece`, `paper`, and `gameboy`. Message roles are only `user` or `assistant`; text is capped at 32,768 characters. Optional `backend` and `model` describe who produced a final message. Token chunks, system prompts, tool output, credentials, contacts, mail, arbitrary files, and raw provider payloads are not accepted record fields. The user's own final turn text can still contain sensitive details they typed; a whitelist cannot infer all private information inside prose. An artifact record contains only its artifact ID, medium and revision number. It does not embed an image, sound, PDF, program source, path, or file contents. Event names are `turn-start`, `turn-complete`, `turn-interrupted`, `render-start`, `render-complete`, `render-failed`, and `model-change`; optional status is `working`, `complete`, `interrupted`, or `failed`. Provider state needed for a live restart remains separate and private. A transcript alone is not a complete provider checkpoint or artifact bundle. `transcript-format.mjs` provides `validateHeader`, `validateRecord`, `serializeTranscript`, `parseTranscript`, and `redactTranscriptText`. Unknown fields and duplicate/out-of-order record IDs/sequences are rejected. Documents are capped at 8 MiB. Exports are strict about the final newline. Only an explicit `parseTranscript(text, {recoverPartial:true})` may discard an unfinished final line after a crash; malformed complete lines remain errors. ## Required company sharing Before using Aesel, each signed-in account must acknowledge disclosure version 4: future user messages, assistant replies, and artifact revision references are shared with authorized AC staff for product improvement, with indefinite retention until owner deletion. Declining exits before creating a session or transmitting conversation content. Existing private messages are not backfilled. Account changes require a separate acknowledgment. The disclosure is available through `/sharing`; there is no optional private-use mode. A `.easel` journal remains on disk. Uploads use authenticated batches of at most 100 records / 256 KiB. Recognizable credentials are redacted on both client and server. Failed uploads remain queued; a new generation cannot start until its user message has been journaled and the pending upload succeeds. `/transcript export FILE.easel` exports the local transcript; `/transcript delete` deletes the uploaded copy, while future messages continue under required sharing. New clients use consent disclosure version 4. Format readers retain support for historical versions 1–3, including older clients and queued uploads. Raw reads require an explicit server-side staff subject allowlist; absent configuration denies all reads. Owners can delete their uploaded transcripts but cannot read arbitrary company records. Expiration is a soft marker: records remain readable after 30 days until explicitly deleted. ## Company access and retention The endpoint uses AC's server-verified subject for ownership. It has no public read route. GET requires an explicit subject in `EASEL_TRANSCRIPT_STAFF_SUBS` (comma-separated Auth0 subjects); an email, handle, client-supplied owner field, or unverified staff claim cannot grant access. Staff reads require a named owner and session and return at most 100 records, with an `afterSeq` cursor. DELETE always scopes to the authenticated owner and accepts `{sessionId}` or explicit `{all:true}`. Records live in Mongo's `easel-transcripts-private` collection. Each row has server-assigned receipt and expiry dates. `expiresAt` marks 30 days after receipt, but no TTL index deletes records. Initialization removes the legacy `easel_transcript_expiry` TTL index and creates a regular expiration index; run `ensureTranscriptIndexes(db)` during deployment to migrate existing storage. Staff reads include expired records and return `expiredSeqs` for the current page, computed at read time without changing the portable record format. Uploads report `retentionDays: null` and `expirationDays: 30`. Retries do not move the expiration marker or mutate an accepted stable record ID. A partial batch failure is retryable with identical IDs. The unique owner/session/sequence index prevents two different events from claiming the same position. These are company-private application records for improving Aesel. They are not sent to PostHog or automatically forwarded to model providers or training jobs. This prototype supplies storage and access controls, not a training pipeline, company review UI, or a promise of end-to-end encryption.