egatari-spec

Egatari 1.0 — Editor’s Draft

Status: Editor’s Draft, 2026-10-17. Not stable. Anything may change before 1.0 is frozen. Editors: Gendo Kumoi (Nagaoka University of Technology). Reference implementation: motion-academic-slides (producer, player, editor). License of this document: CC BY 4.0. Schemas, fixtures and tools are MIT (LICENSE). Repository: https://github.com/egatari/egatari-spec · Site: https://egatari.org/

Egatari (絵語り, “narrating with pictures”) is a file format for narrated, animated lessons. One Egatari file is a single HTML document that

  1. plays offline in a current web browser as a self-narrating lesson, a live presentation, or a lecture handout, and
  2. carries its own authoring source, so that an Egatari editor can reopen the file without the original folder, change one sentence or one step’s narration, replace that step’s audio, and save the same kind of file again.

The name is written Egatari (or 絵語り). The file extension is .ega.html.

0. Conventions

The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, NOT RECOMMENDED, MAY, and OPTIONAL are to be interpreted as described in BCP 14 [RFC 2119] [RFC 8174] when, and only when, they appear in all capitals.

JSON structures are normatively defined by the JSON Schemas in schema/. Where prose and schema disagree, the schema wins and the disagreement is a bug in this document.

“Step key” means "<slideKey>:<stepKey>", where stepKey is a non-negative integer in decimal or the literal concl. Slide keys may themselves contain :; a step key is split at its last :.

1. Model

1.1 Position

A lesson is a sequence of slides. Each slide has steps 0..n and optionally a final concl step. The position is the triple (i, k, t): slide index, step, and seconds since the step began.

1.2 Display modes

Mode Who drives Time Typical use
talk (default for a bare file) the presenter, step by step real time of each step live presentation, class
learn the learner: start, pause, previous/next, jump, playback rate advances along the timeline (§5.5) with narration audio self-study, LMS (default in LMS bindings and hosted players)
video nobody (no controls) advances along the timeline recording and video export

Preview of later steps is an option of every interactive mode, not a mode: steps not yet reached are either hidden (default) or shown faintly. The current step renders and animates as usual; only content of later steps is shown faintly, and components keep rendering their current step. ?mode=lecture is a legacy alias for talk with preview on.

In learn mode, a player MUST offer playback rates of at least 0.75, 1, 1.25, 1.5 and 2, keeping the pitch of speech; the timeline clock advances at the same rate, so the screen stays a function of (i, k, t). Presenter-only tools (presenter view, laser) SHOULD be hidden.

When embedded by a host (§9.4, §10), the host MAY hide the player’s own controls (controls=0) and provide equivalent ones itself (chapter list, playback rate, pause), and MAY restrict forward navigation as a course policy. The requirement to offer controls is then met by the host.

1.3 Narration

Each step MAY have narration: an ordered list of turns, each with a role (expert or novice; single-speaker lessons use expert only) and text. Text MAY contain boundaries — cue (a synchronization point for an animated component) and mark (an attention mark on a target element) — and readings (display text with a separate spoken form, e.g. φ read as ファイ). The authoring syntax is defined in §8.

2. File structure

An Egatari file is an HTML document (text/html; charset=utf-8). The RECOMMENDED file name extension is .ega.html. A file has two layers:

Layer Purpose Required for
Runtime layer compiled slides, runtime script, styles, fonts, and the runtime JSON blocks (§4) playing
Archive layer the index (§3) and blob blocks: authoring source and media editing

A file with only the runtime layer is a play-only Egatari file. A file with both is a portable Egatari file.

All JSON blocks are <script> elements with a non-executable type and a unique id. Before embedding (and before hashing), every U+003C < in serialized JSON MUST be written as the six ASCII characters \u003c. Serialized JSON uses LF line endings only. (A non-executable type does not stop the HTML parser from ending the element at a literal </script.)

2.1 Readable layer without script

A conforming document MUST also contain, as ordinary DOM outside any <script>, for every step of every slide: the display text of its narration. It SHOULD contain, for every slide, either a static image of the final state or text equivalent to it. Producers MAY also include a final-state image per step, so that hosts can show stills without running a browser. This layer serves archiving, accessibility, search, and recovery when no script runs.

Structure: one <section id="egatari-readable"> containing, per slide, <article data-slide-key="…"> with the slide title, and per step <div data-step-key="…"> with one <p data-role="expert|novice"> per turn (display text). Images, if any, are <img alt="…"> inside the article or step. The runtime hides this section when it starts; without script it stays visible. Within an article, step divs are in step order (numbers ascending, concl last). Narration addressed to a slide key that matches no slide has no article: such steps are omitted from the readable layer and reported. Tier A edits update the affected div elements (§7.2).

3. Archive layer

3.1 Index

<script type="application/vnd.egatari+json" id="egatari-index" data-sha256="…">{…}</script>
Member Req. Meaning
format MUST the string "egatari"
version MUST {major, minor} of this specification the index follows (1.0 → {"major":1,"minor":0})
profile MUST "core" or "extended" (§6)
requires MUST feature identifiers a reader must understand to edit (§3.5). May be empty
contracts MUST {source, runtime} integer versions of the authoring syntax and of the runtime behavior (§7)
generator MUST {name, version} of the producing software
docId MUST a UUID identifying the lesson across edits; editors MUST preserve it
entry MUST archive path of the slide source (normally deck.html)
sourceSha256 MUST hash of the source set the runtime layer was built from (§3.4)
files MUST archive path → blob id
blobs MUST blob id → {mime, bytes, sha256, chunks}
audio MAY step key → audio record (§5.2)
components MAY components used (§6)
themes MAY themes used, same shape as components (§6)
metadata MUST descriptive metadata (§3.6)
excluded MAY informative list of categories the producer deliberately left out

3.2 Blob blocks

<script type="application/octet-stream" id="egatari-b-s0-0">PCFkb2N0eXBlIGh0bWw+…</script>

3.3 Archive paths

Keys of files are archive paths: /-separated, relative, UTF-8, NFC. A reader MUST reject the whole file if any path

Case-insensitive comparison uses the key NFC(lower(upper(NFC(path)))), where upper and lower are the locale-independent Unicode case mappings (as ECMAScript toUpperCase/toLowerCase). A path is at most 1024 bytes and 10 segments. Readers MUST build every key-indexed structure from untrusted input with maps that have no inherited keys.

Paths under audio/ are reserved for narration audio; their blobs MUST have an audio media type from §5.1. audio/manifest.json MUST NOT appear in files: it is derived from index.audio when an archive is unpacked. Producers SHOULD include a root file egatari.json ({"docId", "metadata"?}) so that an unpacked folder keeps its docId; when present, its docId MUST equal the index’s. Editors MAY add it to a document that lacks it (this changes sourceSha256, which the edit updates anyway). An archive MUST include the slide source (entry), narration.md if the lesson has narration, and the complete dependency closure needed to rebuild the runtime layer (images, data, styles, deck-local components and themes, macros, timeline.json, a voice configuration without private paths). Registry components (§6) are excluded from the closure; they are resolved by exact name, version and hash. Metadata embedded in images (EXIF, XMP) SHOULD be stripped when an image is first added to a lesson rather than at export, so that rebuilds stay byte-identical. It MUST NOT include: absolute or machine-specific paths, credentials, editor review notes, caches, backups, unreferenced audio, private reference-voice recordings, or personal identifiers not deliberately entered as content.

3.4 Source hash

sourceSha256 is computed as SHA-256 over the UTF-8 bytes of the lines "<path>\t<sha256>\n", one per entry of files excluding paths under audio/, sorted by path in code-point order. Producers MUST also write the same value into the runtime layer’s deck-meta.sourceSha256 (§4). Readers MUST recompute it from files and the blob hashes; a mismatch with the index value is treated like a mismatch with deck-meta. An editor that finds any two of these values different MUST warn that the compiled lesson and its source disagree and MUST NOT perform a tier A edit (§7.2).

3.5 Features

requires lists feature identifiers (^[a-z0-9-]+$). Version 1.0 defines sources-v1 (archive layer present) and audio-v1 (§5.2 records). A reader MUST refuse to edit or re-export a file whose requires contains an identifier it does not implement; it MAY still play the file and extract its source (§9.3) when the index and blobs validate. Source extraction is not re-export. Optional information that a reader may ignore MUST NOT be listed in requires.

3.6 Metadata

metadata MUST contain title and lang (BCP 47). It MAY contain license (SPDX identifier or URL), copyright (e.g. © 2026 Gendo Kumoi), attribution (the credit line the rights holder asks reusers to show), creators (display names the author chose to publish), description, accessibility (schema.org terms such as accessMode, accessibilityFeature, accessibilityHazard), curriculum (array of curriculum codes, e.g. Japanese 学習指導要領 codes from the MEXT education data standard), completion (§9.2), and the members of §3.6.1. Producers MUST NOT add timestamps, device identifiers, account e-mail addresses, or file-system locations.

3.6.1 Derivation, authorship and audience

These OPTIONAL members support lessons made for one learner, by learners, or by software agents.

3.7 Play-only derivative

A play-only derivative is made from a portable file by removing the index and every blob whose chunks are not referenced by the runtime layer, keeping the chunks of audio referenced as egatari:<blobId>. In deck-meta, portable becomes false and sourceSha256 is removed; docId is kept. Players resolve egatari:<blobId> by decoding the chunks egatari-b-<blobId>-0, -1, … in order until the next one is absent.

The archive layer — including extensions — is readable by anyone who holds a portable file. A host that must keep information from learners (e.g. quiz answers in an extension) MUST serve learners a play-only derivative and keep the portable original for authorized editors.

4. Runtime layer

The runtime layer contains the compiled slides (<section class="slide" data-key="…"> elements), the runtime, and these JSON blocks (type="application/json"):

id Content
deck-meta {format:"egatari", version, contracts, generator, docId, title, lang, portable, sourceSha256?, completion?, tracking, license?, copyright?, attribution?} — schema schema/deck-meta.schema.json. tracking lists the tracking adapters the runtime implements (e.g. ["scorm12","postmessage","local"]), so that packagers and hosts need not read the archive
deck-narration {slideKey: {stepKey: [{role, text}]}} — display text
deck-marks {stepKey: [{type:"cue"} \| {type:"mark", id, effect, hold} \| {type:"clear"}]} — boundaries, only for steps with marks
deck-audio {stepKey: {src, dur, cues}} — src is a data: URI or egatari:<blobId>; dur seconds; cues the time of every boundary in order (§5.3); sync (auto, manual, none; default auto); optional mime (the audio media type, so that players of play-only derivatives need not sniff) — schema schema/deck-audio.schema.json
deck-timing {stepKey: {dur?, hold?, lead?, pad_after?}} — explicit overrides

Every slide MUST carry a data-key: the slide’s id attribute if present, otherwise its normalized heading text (legacy). Producers SHOULD give every slide an explicit, stable id (§8.1), so that editing a title does not break narration and audio associations.

A portable file SHOULD reference audio as egatari:<blobId> so that audio bytes are stored once, in the archive layer. A player resolves egatari:<blobId> by decoding that blob.

5. Audio and timing

5.1 Formats

Format Producer Player
MP3 (audio/mpeg), mono, 44.1 kHz RECOMMENDED for new audio MUST play
AAC-LC in MP4 (audio/mp4) MAY (existing audio SHOULD be kept as is) SHOULD play
WAV PCM 16-bit (audio/wav) MAY, emergency only MUST play

Producers SHOULD normalize speech to −20 LUFS integrated, −1.5 dBTP, and trim silence at the ends (−45 dB, keeping 0.05 s), and SHOULD insert 0.35 s of silence between turns, but not at boundaries inside a turn nor at the end of the step. Encoder delay (priming) MUST be compensated so that time 0 of dur and cues is the first audible sample a player presents.

5.2 Audio records

index.audio[stepKey] describes the step’s audio (schema: schema/egatari-audio.schema.json):

Member Req. Meaning
file MUST archive path under audio/
dur, cues MUST as in deck-audio
kind MUST tts, recorded, or imported
inputHash MUST §5.4; null when unknown (e.g. audio imported from an older build), which players and editors treat as stale
recipe MUST for tts {engine, model, voice, seed, dictionary, processing}; voice is a public identifier, never a path; dictionary an identifier of the effective reading dictionary and rules (SHA-256 RECOMMENDED); processing a free-form profile string, RECOMMENDED as key=value pairs separated by ;
recipeDigest MAY the producer’s content-address of input + recipe (e.g. the reference implementation’s 12-hex segDigest)
alternatives MAY [{file, mime, startOffset}] other encodings of the same take

Editors MUST NOT replace recorded audio unless the user explicitly selects that step.

5.3 Boundaries and cues

The boundaries of a step, listed in order across its turns, correspond one-to-one with cues. Players align component cues(k) with the cue boundaries only, and start marks at cue + lead. When counts do not match, or sync is none, players MUST fall back to unsynchronized deterministic timing (no component stretching; marks every 0.9 s after the step’s motion ends). Producers of recorded audio MUST NOT reuse cue times from a previous take; until the user places them, the record (and its deck-audio entry) MUST have "sync":"none" and cues: [].

5.4 Staleness and identity

Three hashes serve three purposes and MUST NOT be substituted for one another:

  1. inputHash — lowercase hex SHA-256 of the UTF-8 bytes of the canonical string JSON.stringify(turns.map(t => [t.role, t.ttsParts, t.bounds.map(token)])) (ECMAScript JSON.stringify, no whitespace), where role is "expert" or "novice", ttsParts are the spoken text pieces between boundaries (readings substituted, trimmed, empty pieces kept), and token is "cue", "mark:clear", or "mark:<id>|<effect>" followed by "|hold=<n>" when a hold is given (<n> formatted by ECMAScript Number.prototype.toString). The slide key is not part of the hash. Test vectors: fixtures/input-hash/. Audio is stale when the current narration’s inputHash differs from the record’s. Editors MUST show stale steps and MUST NOT silently delete or regenerate their audio. When a step’s narration is removed altogether, its audio is removed with it, with a warning.
  2. recipe / recipeDigest — records how the audio was generated. Identical recipes do not imply identical bytes: GPU-based synthesis is not bit-reproducible across hardware. Readers MUST NOT use audio bytes to decide whether a step needs regeneration.
  3. sha256 of the blob — integrity only.

5.5 Timeline

The length of step (i, k) is

dur = override.dur ?? max(anim, audio ? lead + audio.dur + pad_after : max(hold, marksEnd + pad_after))

with defaults lead = 0.25, pad_after = 0.6, hold = 1.2 seconds; anim is the step’s motion length including the slide transition (0.35 s) or morph (0.9 s) at step 0. video mode MUST use this function; so MUST any video export.

6. Components and profiles

Animated figures are custom elements (Web Components) following the component contract (§8.4). Each entry of index.components is {tag, name, version, sha256, source} where source is registry or embedded. A component’s sha256 covers its code file (index.js) only. Each entry of index.themes is {name, version, sha256, source}; a theme’s sha256 is SHA-256 over the lines "<relative path>\t<sha256>\n" of its files, sorted by path. version is the registry release for registry entries; embedded entries without a version use "local".

Profile Components Editor behavior
Core Only registry components: identified by name, version and the SHA-256 of their code in a public component registry. The archive contains no component code. The editor supplies its own trusted copy and verifies the hash. A component it does not have is shown as its final-state image or text equivalent.
Extended embedded components: code is in the archive (pinned at export). The editor MUST run component code only in a sandbox with an opaque origin and a policy that forbids network access, and MUST tell the user that the lesson contains its own interactive parts before any of that code runs. “Forbids network access” means at least connect-src 'none', with every other resource type limited to data: and blob:.

The runtime layer of either profile contains the component code needed to play offline. An Extended document MAY mix registry and embedded components. A document is Extended if any archived file other than registry components contains executable script (a <script> element without a data type, or a JavaScript file); otherwise it is Core. Readers MUST NOT rely on the declared profile: they MUST determine whether code can run from the validated content — at least: <script> elements of any non-data type; event-handler attributes (on*); javascript:/vbscript: URLs in any URL-valued attribute, including SVG animation values; srcdoc; embedding elements (iframe, object, embed, frame) and meta http-equiv=refresh; JavaScript files; and HTML or SVG documents the source includes or references. Registry components that evaluate expressions written in attributes are a code path even in a Core document (trusted code, untrusted data): such components MUST restrict what those expressions can do (e.g. arithmetic only), and readers SHOULD treat expressions outside that restriction as code. Classification MUST be performed on the expanded source — after every include or preprocessing step, covering every dependency read as text regardless of its file extension — and on the same bytes that are then rendered; a dependency that cannot be read makes the document count as containing code. and treat a document whose content contradicts its declared profile as Extended or reject it. If an editor cannot obtain a trusted copy of a registry component with the recorded hash, it MUST disable tier B and C edits for that document and MAY still perform tier A edits.

7. Versioning and editing

7.1 Versions

7.2 Edit tiers

An editor MUST NOT execute a builder or runtime supplied by the document in its own trusted context.

Tier Change Required behavior
A (default) narration text, audio, timing overrides (timeline.json / deck-timing; durations themselves are computed, §5.5) only Do not rebuild. Only these regions may change: the affected entries of the runtime JSON blocks; the affected entries of the readable layer (§2.1); sourceSha256 (index and deck-meta) and the index hash; archive entries, audio records and blobs; and append-only font supplements — new <style id="egatari-font-supplement-<n>"> elements carrying glyphs for characters not covered by the embedded fonts. When saving under a new docId (§7.3), deck-meta.docId, the index docId, metadata.derivedFrom and egatari.json are also tier A regions. Every byte outside these regions, including existing font data, MUST stay identical.
B slide content (text, math, figures) Rebuild with the editor’s runtime of the same contracts.runtime, using the archived (pinned) components and themes. Before saving, compare the final state of every slide before and after; show the user every slide that changed besides the edited ones.
C contracts.runtime differs from the editor’s Only on explicit request (“upgrade”), with the same comparison as B. Without upgrade, only tier A edits are allowed.

Re-export MUST copy untouched blobs byte-for-byte and MUST preserve docId (unless saving as a new lesson, §7.3).

Font supplements. <n> in egatari-font-supplement-<n> is canonical decimal, one greater than the largest existing supplement (starting at 0); a supplement follows the last existing font style element. It contains @font-face rules with the same family, weight and style as each embedded face that needs the new characters. Producers SHOULD declare unicode-range on every embedded @font-face; editors MAY add a supplement for any character whose coverage they cannot establish from those ranges.

7.3 Rights

Egatari cannot prevent anyone from modifying a file (§11). It carries the rights holder’s terms so that conforming software states them and keeps credit intact:

8. Authoring source (contract version 1)

This section will incorporate, normatively, the contract of the reference implementation: docs/contract.md §1–§5, §7, §8 (position and time, modes, components, narration syntax, timeline, attention marks, morphs). Until then, that document is normative for contracts.source = 1 and contracts.runtime = 1.

8.1 Slide identifiers

8.2 Narration syntax (summary)

## <heading> {#<id>}
[0] text…
[2] Expert: text with {cue} and {mark:ID|circle|hold=2} and {φ|ファイ}
    Novice: question…
[concl] text…

Extension lines. Inside a step’s block, a line of the form @x-<name>-<field> <value> (after optional indentation; <name> as in §9A, <field> matching ^[a-z][a-z0-9-]{0,31}$, <value> the rest of the line, single-line UTF-8) carries extension data for that step. Extension lines:

8.3 Readings

{display|reading} stores the reading in kana (or the lesson language’s ordinary spelling). Producers MAY export readings as SSML <sub alias>, W3C PLS lexicon entries, or HTML <ruby>.

8.4 Component contract (summary)

steps, props, mount(ctx), duration(k), cues(k), render(k, t, ctx); render gives the same output for the same (k, t); colors only through theme CSS variables.

9. Player requirements

9.1 Playback

A conforming player MUST: render any (i, k, t) deterministically; implement the talk, learn and video modes and the preview option (§1.2); play the formats in §5.1; start audio only after a user gesture (a visible start/resume control); pause when the page is hidden; honor prefers-reduced-motion by rendering final states; and work from file:// with no network access.

9.1A Learner data

Notes, ink, bookmarks and other data a learner creates MUST NOT be written into the Egatari file. Players MAY keep them in local storage keyed by docId, and SHOULD let the learner export them (e.g. the final state of each slide with the learner’s notes, as PDF or Markdown).

9.2 Tracking

A player SHOULD expose progress through a tracking adapter, detected in this order: SCORM 1.2 API (window.API in a parent frame), then postMessage to the parent, then local storage. Events: started, step ({slideKey, stepKey, index, total}), completed, suspend ({location, data}). The SCORM 1.2 binding is defined in bindings/scorm12.md. A player lists the adapters it implements in deck-meta.tracking; completion rules are read from deck-meta.completion so that play-only files can be tracked. Completion defaults to “every step visited”; metadata.completion MAY set {"visitedFraction": number} or {"reachedEnd": true}.

9.3 Source extraction

A player SHOULD offer to save the archive layer as a ZIP of files plus audio/manifest.json, so the source remains recoverable without any editor.

9.4 Embedding

A host page (a learning platform, an LMS plugin, a video tool) MAY embed an Egatari file in an <iframe> and drive it. A player that supports embedding MUST, when loaded with the query parameter embed=1, accept messages from window.parent via postMessage (objects with a string member egatari) and ignore messages from any other source. Query parameters mode, controls=0 and preview=1 set the initial state. Every command MAY carry an id; the player echoes it in the reply or acknowledgement. Times are seconds as floating-point numbers.

Commands (host → player):

Message Effect / reply
{egatari:"seek", id?, slide, step, t} go to (slide, step, t); slide is a slide key or index, step a step key, t seconds or null for ∞. Reply seeked
{egatari:"play", id?} / {egatari:"pause", id?} start / pause timeline playback with audio. If the browser refuses to start audio, reply playBlocked
{egatari:"rate", id?, value} playback rate (§1.2)
{egatari:"mode", id?, mode} / {egatari:"preview", id?, on} / {egatari:"controls", id?, visible} display mode, preview of later steps, the player’s own controls
{egatari:"snapshot", id, slide, step, t, maxWidth?} reply {egatari:"snapshot", id, png} with a data:image/png of that position (t null = ∞), without changing the visible position

Events (player → host; target origin *; payloads contain no private data):

Message When
{egatari:"ready", version, docId, sourceSha256?, timeline:[{slideIndex, slideKey, stepKey, slideTitle, dur}]} after loading
{egatari:"seeked", id?, slideKey, stepKey, t} after a seek has been applied
{egatari:"position", slideKey, stepKey, t, cause} on every step change (cause: play, seek, control), and at most 4 times per second during playback
{egatari:"stepEnded", slideKey, stepKey} when a step has played to its end during playback (not when skipped)
{egatari:"state", playing, rate} whenever playing state or rate changes, including by the player’s own controls
{egatari:"playBlocked", id?} audio could not start for lack of a user gesture
{egatari:"turn", slideKey, stepKey, index} OPTIONAL: when narration turn index starts
{egatari:"ended"} after the last step of the timeline
{egatari:"error", id?, code, message} a command failed or the file cannot be played

The first start of audio SHOULD happen through the player’s own start control, because browsers (notably Safari on iPad) may not pass a user gesture in the host page into the frame; hosts SHOULD allow autoplay on the frame and MUST handle playBlocked. Since a sandboxed frame has the origin null, hosts MUST identify the player by event.source === iframe.contentWindow. The same messages serve the postMessage tracking adapter (§9.2). Embedding commands are unprivileged: they can only do what a viewer could do with the controls. Narration text is not sent at run time; hosts read deck-narration from the file.

9A. Extensions

Platforms may need information that Egatari does not define (e.g. questions to ask the learner at a step, grouping of steps into chapters). Such information is carried as extensions without changing Egatari itself.

9B. Signatures (feature signatures-v1, draft)

Draft. Not part of the 1.0 conformance requirements until this note is removed. Interoperability with C2PA Content Credentials is planned for a later version (statement → claim and hard binding, derivedFrom → ingredient, affiliation → identity assertion).

A rights holder MAY sign a lesson so that players can show who signed it and whether it has changed since. Signatures make genuine copies verifiable; they cannot prevent modification, and an attacker can remove the signature block entirely. signatures-v1 is ignorable and MUST NOT be listed in requires.

9B.1 Block

<script type="application/vnd.egatari+json" id="egatari-signatures">{"signatures":[…]}</script>

Each entry is a JWS in JSON Serialization (RFC 7515 §7.2.2 flattened form) whose payload is the UTF-8 bytes of a statement (carried as is, so no canonicalization is needed):

Statement member Meaning
type "egatari-signature/1"
docId the lesson’s docId
signedAt RFC 3339 time claimed by the signer
signer {name, affiliation?, orcid?, keyId} — claims, verified only through §9B.3
fileSha256 SHA-256 of the whole file with the egatari-signatures element (from <script to </script> inclusive) removed
indexSha256 the index’s data-sha256, if portable
sourceSha256 as §3.4, if portable
derivedFrom copy of metadata.derivedFrom, plus signatureDigest of the original’s signature when known
supersedes digest of an earlier signature by the same signer on a previous revision

Algorithms: EdDSA with Ed25519 (RECOMMENDED) and ES256 (P-256). Verifiers MUST support both. The protected header carries alg and kid; the JWK of the public key MAY be included as jwk.

9B.2 Verification and display

A player that implements this feature computes fileSha256 and verifies each signature, then shows exactly one of:

Tier A edits by the signer SHOULD be followed by a new signature with supersedes. Edits by anyone else leave the old signature in place, which then shows modified since signing. (A later revision may sign a frame hash excluding tier A regions plus per-step narration and audio hashes, so that players can say which steps changed.)

9B.3 Identity levels

Level Evidence Shown as
L0 key only (did:key), name self-declared “signed with key , name as declared"
L1 the key fingerprint is published by the signer on a site they control (did:web), ORCID or researchmap; checked when online “, key confirmed at "
L2 an attestation by an organization (university, or an Egatari key registry) binding the key to the person and affiliation, embedded and verifiable offline “, , confirmed by "

Any organization MAY issue L2 attestations; this specification names none. Which attesting organizations a player or host trusts is its own configuration (e.g. a host for one university trusts that university), and a player MUST show the attesting organization’s name rather than a generic “verified”.

Signing certificates that contain personal data beyond name and affiliation (e.g. national ID card certificates) MUST NOT be embedded. Authors SHOULD also publish each lesson’s docId and their key fingerprint on an official distribution page, so that copies can be checked against it.

10. Conformance classes

Class Requirement summary
Document — Core / Extended valid runtime layer; if portable, valid archive layer of that profile; readable layer (§2.1)
Player — Core / Extended §9.1; resolves egatari: audio; Extended players run embedded component code
Editor safe import (§11), tier A at least, §7, preserves unknown members and docId
Producer writes conforming documents; §3.3 exclusions; §5 audio rules
LMS Binding packages a document for a learning platform per a binding document (SCORM 1.2 in 1.0)
Host a platform that accepts and plays Egatari files: §10.1

Conformance is tested with the fixtures in fixtures/.

10.1 Host

A conforming host MUST:

  1. Validate uploaded files on the server per §11 without executing them (limits, hashes, requires), and reject unsupported requires with a clear message. Hosts whose platform limits are lower than §11’s recommendations MUST stream uploads rather than buffer them.
  2. Play files through §9.4 in an iframe with sandbox="allow-scripts" and without allow-same-origin, and serve the file itself with a Content-Security-Policy that sandboxes it and forbids network access even when its URL is opened directly, e.g. sandbox allow-scripts; default-src 'none'; img-src data: blob:; media-src data: blob:; style-src 'unsafe-inline' data:; script-src 'unsafe-inline' blob:; font-src data:.
  3. Store the original bytes unmodified and return them unchanged to users authorized to edit. It MAY serve learners a play-only derivative (§3.7), and MUST do so when extensions carry information to be withheld from learners.
  4. Play files that carry no extensions for that host.
  5. Record progress from §9.2/§9.4 events, keeping Egatari completion (deck-meta.completion) distinct from the host’s own grading.
  6. Never write learner data into files (§9.1A).

A host MAY supply its own controls and restrict navigation (§1.2). Conformance covers Egatari content only; other kinds of material a platform offers are out of scope.

11. Security considerations

12. Media type and identification

References

Appendix A. Open issues

  1. Blocker for 1.0: normative text of §8 (import and freeze docs/contract.md), component/theme dependency manifests and hash scope, schemas for every runtime block, archive↔runtime consistency rules.
  2. Public component registry: location, naming, hash publication.
  3. Whether a lesson may embed a custom reference voice (size, privacy).
  4. Tolerances for loudness and cue timing across browsers.
  5. File-picker availability on file://; download as the baseline save path.
  6. signatures-v1: what is signed (index and runtime layer), interaction with tier A edits and derived lessons, key identity (e.g. did:web, GitHub keys, Sigstore, C2PA certificates), timestamps and long-term verification.
  7. Document fixtures (fixtures/README.md), including tier A edits that introduce new glyphs and SCORM lifecycle cases, must exist before freezing.