# Chromacode 2D

Chromacode 2D ist eine vollständig clientseitige Webanwendung für ein eigenes
farbcodiertes 2D-Datenformat. Ein Datenmodul trägt eines von 16 Farbsymbolen und damit
genau vier Bit. Dateien werden im Browser in einen oder mehrere PNG-/SVG-Frames
umgewandelt und lassen sich aus Bildern oder mit der Smartphone-Kamera bytegenau
rekonstruieren.

Die produktive Installation liegt unter:

- Pfad: `/var/www/jk-z.de/html/colorcode/`
- URL: `https://jk-z.de/colorcode/`

Es gibt keinen Upload, kein Backend, keine Datenbank, keine Cookies und keine
Serverlaufzeit. Dateiinhalt, Hashing, ECC, Bildanalyse und Downloads bleiben im Browser.

Die normative, sprachunabhängige Protokollbeschreibung steht in [FORMAT.md](FORMAT.md).

## Funktionen

- eigenes `CC2D`-Binärformat, Version 1; kein QR-Code
- vier eigene gerichtete X-Finder mit unterschiedlichen Eck-IDs
- vier Module breite Quiet Zone und Timing-Spuren
- 16 obligatorische `2 × 2`-Farbkalibrierungsfelder
- CIELAB-/Delta-E-Klassifikation gegen die im konkreten Scan gemessenen Farben
- getrimmtes Mehrfachsampling innerhalb jedes Moduls, ohne Randpixel
- Confidence-Bewertung und Übergabe unsicherer Bytes als Reed-Solomon-Erasures
- systematische, verkürzte RS-Codes über GF(256), vier Schutzstufen
- blockweises und räumliches Interleaving gegen lokale Schäden
- CRC32 für Header, Chunk und Gesamtdatei sowie SHA-256 für die Endabnahme
- automatische Multi-Frame-Aufteilung, beliebige Scanreihenfolge und Duplikaterkennung
- PNG, SVG und clientseitiges unkomprimiertes ZIP ohne Fremdbibliothek
- Fotoimport auf dem Desktop
- rückseitige Live-Kamera, Perspektivkorrektur, Stabilitätsprüfung und Overlay
- Debug-Modus mit entzerrtem Bild, Referenz-RGB, Geometrie, Confidence und ECC-Diagnose
- keine dynamische Ausführung oder automatische Vorschau rekonstruierter Inhalte

## Schnellstart

### Datei kodieren

1. `https://jk-z.de/colorcode/` öffnen.
2. Datei wählen oder auf das Auswahlfeld ziehen.
3. Raster und ECC-Stufe einstellen. Die Oberfläche berechnet Kapazität und Framezahl.
4. **Farbcodes erzeugen** wählen.
5. Einzelne Frames als PNG/SVG oder alle PNGs als ZIP speichern. Bei mehreren Frames
   kann **Auto-Anzeige** sie nacheinander auf einem zweiten Bildschirm zeigen.

### Datei dekodieren

Auf dem Desktop können ein oder mehrere PNG-/JPEG-/WebP-Bilder in beliebiger Reihenfolge
gewählt werden. Auf einem Smartphone wird **Kamera starten** verwendet. Der Scanner
übernimmt einen Frame erst nach zwei aufeinanderfolgenden, identischen Dekodierungen.
Duplikate werden ignoriert. Ein Dateidownload erscheint ausschließlich nach gültiger
Chunk-CRC, Gesamt-CRC und SHA-256.

## Kapazität

Die folgende Tabelle wird direkt aus der v1-Implementierung berechnet. Die Nutzlast gilt
für den Beispielnamen `file.bin` und MIME `application/octet-stream`; längere UTF-8-
Metadaten reduzieren die Nutzlast bytegenau. `Datenfelder` sind Farbfelder, `Codewort`
und Nutzlast sind Bytes.

| Raster | Datenfelder | RS-Blöcke | ECC | ECC-Bytes | Nutzlast/Frame |
|---:|---:|---:|---|---:|---:|
| 48 × 48 | 1.572 | 4 | Low | 64 | 586 B |
| 48 × 48 | 1.572 | 4 | Medium | 128 | 522 B |
| 48 × 48 | 1.572 | 4 | High | 192 | 458 B |
| 48 × 48 | 1.572 | 4 | Extreme | 256 | 394 B |
| 64 × 64 | 3.236 | 7 | Low | 112 | 1.370 B |
| 64 × 64 | 3.236 | 7 | Medium | 224 | 1.258 B |
| 64 × 64 | 3.236 | 7 | High | 336 | 1.146 B |
| 64 × 64 | 3.236 | 7 | Extreme | 448 | 1.034 B |
| 80 × 80 | 5.412 | 11 | Low | 176 | 2.394 B |
| 80 × 80 | 5.412 | 11 | Medium | 352 | 2.218 B |
| 80 × 80 | 5.412 | 11 | High | 528 | 2.042 B |
| 80 × 80 | 5.412 | 11 | Extreme | 704 | 1.866 B |
| 96 × 96 | 8.100 | 16 | Low | 256 | 3.658 B |
| 96 × 96 | 8.100 | 16 | Medium | 512 | 3.402 B |
| 96 × 96 | 8.100 | 16 | High | 768 | 3.146 B |
| 96 × 96 | 8.100 | 16 | Extreme | 1.024 | 2.890 B |

Die 104 festen Headerbytes, Dateiname und MIME sind bereits abgezogen. Die Anzahl
Codewortbytes vor diesem Abzug beträgt 786, 1.618, 2.706 beziehungsweise 4.050 Bytes.
Robustheit ist bewusst wichtiger als theoretische Dichte: Auf einem Telefon sollte ein
Modul im Kamerabild mindestens ungefähr 5–6 Pixel breit sein. Für Bildschirm-zu-Kamera
sind `64 × 64 / High` oder `48 × 48 / Extreme` gute Ausgangspunkte.

## Palette

Die Zuordnung ist zentral in `js/palette.js` definiert und in FORMAT.md normativ:

| Wert | Farbe | Wert | Farbe | Wert | Farbe | Wert | Farbe |
|---:|:---:|---:|:---:|---:|:---:|---:|:---:|
| 0 | `#7F1D1D` | 4 | `#65A30D` | 8 | `#1D4ED8` | C | `#DB2777` |
| 1 | `#EF4444` | 5 | `#22C55E` | 9 | `#60A5FA` | D | `#F9A8D4` |
| 2 | `#F97316` | 6 | `#0F766E` | A | `#581C87` | E | `#854D0E` |
| 3 | `#FACC15` | 7 | `#22D3EE` | B | `#A855F7` | F | `#94A3B8` |

Reines Schwarz und Weiß bleiben Finder, Separators und Quiet Zone vorbehalten. Die
Palette kombiniert weit auseinanderliegende Farbtöne und Helligkeitsstufen. Entscheidend
ist nicht der Soll-RGB-Abstand, sondern die Klassifikation gegen alle 16 im selben Foto
gemessenen Referenzen. Der Decoder rechnet sRGB nach CIELAB (D65) um und bewertet den
Abstand zur zweitbesten Klasse als Confidence.

## Formatübersicht

Alle Mehrbytewerte sind Big Endian. Der RS-geschützte Frame beginnt mit einem festen
104-Byte-Header:

- Magic `CC2D`
- Version, ECC-ID, Headerlänge, Rastergröße und Flags
- 16-Byte-File-ID
- ursprüngliche Dateigröße
- SHA-256 und CRC32 der Gesamtdatei
- Frameindex, Gesamtzahl, Chunkoffset und Chunklänge
- Chunk-CRC32
- UTF-8-Längen für Dateiname und MIME
- Header-CRC32, Dateiname, MIME, Payload und Nullpadding

Jedes Byte wird High-Nibble zuerst auf zwei Farbfelder abgebildet. Reed-Solomon arbeitet
mit maximal 255 Byte langen Blöcken und 16/32/48/64 Paritätsbytes pro Block. Unsichere
Farbfelder werden byteweise als Erasures behandelt; grundsätzlich gilt
`2 × unbekannte Fehler + Erasures ≤ Paritätsbytes`. Codewörter werden zwischen Blöcken
Round-Robin und danach über eine teilerfremde Rasterpermutation verteilt.

## Scanner-Pipeline

1. Das Bild wird für die Findersuche performant verkleinert.
2. Dunkle, wenig gesättigte zusammenhängende Ringkomponenten werden gesucht.
3. Vier ähnlich große Kandidaten bilden ein konvexes Viereck.
4. Alle v1-Rastergrößen und vier Rotationen werden anhand der vier X-Finder bewertet.
5. Aus den Markerzentren wird eine projektive Homographie gelöst.
6. Die 16 Referenzfarben werden jeweils aus vier Modulen und mehreren inneren Samples
   robust gemittelt.
7. Jedes Datenfeld wird in CIELAB gegen diese scanlokalen Referenzen klassifiziert.
8. Unsichere Nibbles werden zu Byte-Erasures, das Raster wird deinterleaved und RS-
   korrigiert.
9. Header- und Chunk-CRC werden geprüft; der Frame wird nach File-ID gesammelt.
10. Nach allen Frames werden Offsetdeckung, Gesamt-CRC32 und SHA-256 geprüft.

Die Kamera analysiert ungefähr drei Bilder pro Sekunde. Das Overlay zeigt Außenkontur,
Finderzentren, Raster und Confidence. Ein Frame muss zweimal stabil erkannt werden.

## Projektstruktur

```text
colorcode/
├── index.html
├── css/style.css
├── FORMAT.md
├── README.md
├── package.json
├── js/
│   ├── constants.js            Protokollkonstanten und Presets
│   ├── palette.js              Palette, sRGB → CIELAB, Klassifikation
│   ├── binary.js               UTF-8, u64 und sichere Metadaten
│   ├── crc32.js / crypto.js    CRC32, SHA-256 und File-ID-Zufall
│   ├── header.js               binärer v1-Frameheader
│   ├── ecc.js                  GF(256), RS-Encoder/-Decoder, Erasures
│   ├── interleave.js           Block- und räumliches Interleaving
│   ├── layout.js               Finder, Timing, Kalibrierung, Datenmaske
│   ├── encoder.js / decoder.js logischer Framecodec
│   ├── frames.js               reihenfolgeunabhängiger Sammler
│   ├── renderer.js / zip.js    Canvas, SVG, PNG und Store-ZIP
│   ├── perspective.js          Homographie und robustes Sampling
│   ├── finder.js               Komponenten- und Geometriesuche
│   ├── color-calibration.js    scanlokale Farbmodelle
│   ├── image-decoder.js        vollständige Bildpipeline
│   ├── scanner.js              getUserMedia, Overlay und Stabilität
│   └── ui.js                   sichere DOM- und Downloadsteuerung
└── tests/
    ├── test.mjs                automatisierte Node-Tests
    ├── simulation.js           Kamera-/Bildsimulation
    └── browser-tests.html      Browser- und Canvas-Selbsttest
```

Es werden keine externen Bibliotheken verwendet. Die RS-, ZIP-, Homographie- und
Farbimplementierungen gehören zum Projekt, wodurch keine CDN- oder Lieferkettenabhängigkeit
entsteht.

## Transportprotokoll Version 2

Version 2 ergänzt v1, ersetzt es aber nicht. Die Anwendung bietet damit drei klar
getrennte Betriebsarten:

- **STATIC** verwendet weiterhin den bestehenden `CC2D`-Codec v1, einzelne
  selbstbeschreibende Bilder, Bildimport und den bisherigen Kamerascanner.
- **STREAM** verwendet `CCS2`, zeigt automatisch SYNC-/DATA-/PARITY-Frames und sammelt
  sie nach einmaligem Kamera-Lock ohne manuelles Weiterschalten.
- **LINK** verwendet dasselbe optische Transportformat und definiert zusätzlich
  HELLO, CAPABILITIES, P-256-Pairing, selektive ACKs, NACKs und Flow Control. Die
  Control-/Handshake-Engine und Simulation sind implementiert; ein reales
  bidirektionales optisches Gerätepaar wurde auf diesem Server nicht getestet.

Unbekannte Magic- oder Versionswerte werden abgelehnt. Die vollständige bytegenaue,
für unabhängige Rust-/Python-/C-Implementierungen geeignete Norm steht in
[FORMAT.md](FORMAT.md).

### Architecture

```text
Application / UI
  └─ File & Stream layer       Blob.slice, BigInt-Offets, inkrementelles SHA-256
      └─ Crypto layer          AES-GCM, PBKDF2 oder P-256/HKDF, Farbpermutation
          └─ Transport layer   Session, Chunks, Generationen, ACK/Flow Control
              └─ ECC layer     MDS-Erasure-Code + lokales Frame-RS
                  └─ Optical   SYNC/DATA, Sequenzmarker, Interleaving, Homographie
                      └─ Color  16 physische sRGB-Farben, scanlokales CIELAB-Modell
```

Der v2-Basisheader ist fest **136 Byte** lang und Big Endian. Er enthält unter anderem
128-Bit-Session-/File-IDs, Stream-ID, Frame-Typ, u64-Generation, u64-Sequenz,
u64-Chunkindex, u64-Offset und u64-Datei-/Streamgrößen sowie Header- und Payload-CRC32.
Typabhängige Informationen folgen als sortierte TLVs.

### Large file handling und Storage

Der Sender liest mit `Blob.slice()` nur eine Generation, verschlüsselt Chunks einzeln
und verwirft gerenderte Frames nach der Anzeige. Er hält weder die ganze Datei noch
alle Frames oder den gesamten Ciphertext im RAM. SHA-256 wird durch die mitgelieferte
inkrementelle Implementierung über die gelesenen Klartextchunks berechnet.

Empfangene Generationen liegen in einem standardmäßig acht Generationen breiten Reorder-Fenster; die Duplikaterkennung ist auf 4.096 Sequenzeinträge begrenzt. Der Empfänger schreibt verifizierte Chunks bevorzugt direkt in einen vom Benutzer
gewählten File-System-Access-Handle, sonst in eine OPFS-Temporärdatei. Der begrenzte
RAM-Fallback akzeptiert höchstens 256 MiB. Protokolloffsets sind u64/`BigInt`;
Browser-Datei-APIs werden bis zu ihrer exakten `Number`-Grenze verwendet. Der
synthetische 20-GiB-Test liest maximal 1 KiB bis 4 KiB je Prüfoperation und legt keine
20-GiB-Datei an.

Pause/Fortsetzen funktioniert während der laufenden Seite. Beim Pausieren speichert
die Sender-UI Parameter und Generation in IndexedDB. Wird dieselbe unveränderte Datei
nach einem Reload erneut gewählt, kann der Sender mit derselben Session ab dem
Generationanfang fortsetzen; ein Passwort muss erneut eingegeben werden und Schlüssel
werden nicht persistiert. Ein gleichzeitiger kompletter Reload des Empfängers kann
derzeit nicht automatisch den inkrementellen Hash-/Kamerazustand wiederaufnehmen und
muss den Transfer neu beginnen.

### Optical synchronization und Rolling Shutter

Ein SYNC-Frame besitzt vier gerichtete X-Finder, alle 16 Farbcluster,
Raster-/Session-/Crypto-Parameter und wird am Anfang doppelt sowie konfigurierbar
periodisch gesendet. Nach SYNC wechselt die Kamera nach `LOCKED`, verwendet dieselbe
Homographie und aktualisiert das Farbmodell über acht DATA-Anchors. Bei einem
periodischen SYNC wird die Vollkalibrierung erneuert.

Fünf räumlich getrennte 24-Modul-Marker tragen jeweils 12 Sequenzbits plus deren
Komplement. Oben, unten, links, rechts und Mitte müssen übereinstimmen. Ein während
des Displaywechsels oder durch Rolling Shutter aus zwei Sequenzen zusammengesetztes
Kamerabild wird deshalb vor Header-/ECC-Verarbeitung verworfen. Der Sender rendert den
nächsten Frame vollständig in einen unsichtbaren Canvas und tauscht beide Puffer erst
in `requestAnimationFrame`. Der Scanner fordert zwei aufeinanderfolgende identische,
vollständig CRC-/ECC-gültige optische Messungen an.

### ECC und Frameverlust

Innerhalb jedes Bildes arbeitet weiterhin der verkürzte Reed-Solomon-Code über
GF(256), inklusive Confidence-Erasures und räumlichem Interleaving. Vollständig
fehlende Bilder werden generationenweise durch einen systematischen MDS-Code über
diesem Feld behandelt. Bei `K=16, R=4` reichen beliebige 16 der 20 Shards; bis zu vier
vollständige Frames dürfen fehlen. Es handelt sich bewusst nicht um einen ungetesteten
Fountain-Code: Die normalisierte Vandermonde-Matrix ist deterministisch, MDS und in
den Verlusttests mit beliebigen Zeilenauswahlen geprüft.

### Cryptography, Pairing und Farbpermutation

Optional wird jeder Klartextchunk unabhängig mit AES-256-GCM und 128-Bit-Tag
verschlüsselt. Die 96-Bit-Nonce ist:

```text
randomSessionPrefix[4] || chunkIndex_u64_be[8]
```

Die authentifizierten Zusatzdaten binden Version, Typ, Flags, Session-/Stream-/File-ID,
Generation, Frame-/Chunkindex, Offset, Längen und Originalgröße. Passwortbetrieb nutzt
mindestens 16 zufällige Saltbytes, PBKDF2-HMAC-SHA-256 mit standardmäßig 600.000
Iterationen und danach HKDF. Argon2id wurde nicht eingebaut, weil ohne externe
WebAssembly-Abhängigkeit keine gleichmäßig verfügbare Browserimplementierung besteht.

LINK-Pairing verwendet Web-Crypto-ECDH P-256, SPKI-Public-Keys und HKDF-SHA-256.
Getrennte HKDF-Infos erzeugen Payload-, Headerauthentifizierungs-, Farbpermutations-
und reserviertes Zukunftsmaterial. SESSION_ACCEPT bestätigt den abgeleiteten Schlüssel
mit einem 16-Byte-HMAC-Tag über den Handshake-Transkript. Die Session-Farbpermutation
ist ein deterministischer Fisher-Yates-Shuffle mit HMAC-Stream und Rejection Sampling.
Sie ist nur Verschleierung; die Sicherheit beruht ausschließlich auf AES-GCM.

### v2-Nutzkapazität

Die Tabelle gilt für DATA-Frames mit lokaler Stufe **High** (48 Paritätsbytes je
RS-Block). `Netto` zieht den 136-Byte-Header ab, `AES-Netto` zusätzlich den 16-Byte-
GCM-Tag. Generationparität und periodische SYNC-Frames sind noch nicht abgezogen.

| Raster | Total | Reserved | DATA-/Anchor-Module | Raw | Frame-ECC | Netto | AES-Netto |
|---:|---:|---:|---:|---:|---:|---:|---:|
| 48 × 48 | 2.304 | 686 | 1.618 | 809 B | 192 B | 481 B | 465 B |
| 64 × 64 | 4.096 | 750 | 3.346 | 1.673 B | 336 B | 1.201 B | 1.185 B |
| 80 × 80 | 6.400 | 814 | 5.586 | 2.793 B | 528 B | 2.129 B | 2.113 B |
| 96 × 96 | 9.216 | 878 | 8.338 | 4.169 B | 816 B | 3.217 B | 3.201 B |

Bei `K=16/R=4`, 5 FPS und dem wirksamen Zwei-Sekunden-Limit (SYNC je 10 Payloadframes) liegen die idealisierten AES-Nettoraten ungefähr bei 1,65/4,21/7,50/11,37 KiB/s. Bei 30 FPS wären es unter idealen
Bedingungen ungefähr 10,4/26,4/47,2/71,4 KiB/s. Diese Werte sind keine zugesicherte
Kameradatenrate; reale Exposure-, Fokus-, Moiré-, PWM- und Rolling-Shutter-Verluste
können deutlich darunter liegen. Die Oberfläche berechnet ETA aus der aktuellen Rate.

### Benchmarking und Performance

Der integrierte Benchmark erzeugt synthetische v2-Frames, führt lokalen optischen
Grid-Encode/Decode aus und zeigt Mittelwert, p95, nachhaltige FPS und logischen
Durchsatz. Auto-Tuning prüft Raster 48/64/80 und 5/10/15 FPS, ohne so zu tun, als sei
ein CPU-Grid-Test bereits eine Kameraabnahme.

Referenzmessung in dieser Serverumgebung mit Node.js 22.14.0, High ECC, jeweils 40
Frames; sie ist nur reproduzierbare CPU-Orientierung:

| Raster | Encode Ø | Grid-Decode Ø | p95 Decode |
|---:|---:|---:|---:|
| 48 | 0,691 ms | 0,909 ms | 3,421 ms |
| 64 | 0,763 ms | 0,855 ms | 3,338 ms |
| 80 | 0,885 ms | 1,615 ms | 10,585 ms |
| 96 | 1,366 ms | 1,917 ms | 5,554 ms |

Eine zusätzliche synthetische 48er-Perspektivmessung benötigte 128,1 ms für die
vollständige SYNC-Suche (davon Finder 105,7 ms) und durchschnittlich 24,8 ms für den
LOCKED-Pfad (Marker 1,4 ms, Anchor-Kalibrierung 0,2 ms, Sampling 20,8 ms, logischer
Decode 2,4 ms). Das sind keine Smartphone-/Browsermesswerte.

### Erweiterte Projektstruktur

```text
js/protocol/     v2-Typen, 136-Byte-Header/TLV, Session, Capabilities, State Machines
js/transport/    Chunker, Generation-MDS, ACK/Flow Control, Scheduler, LINK-Handshake
js/crypto-v2/    AES-GCM, Nonce, PBKDF2/HKDF, P-256, Farbpermutation, Streaming-SHA
js/optical/      SYNC-/DATA-Layout, Sequenzmarker, Calibration Tracker, LOCKED-Decoder
js/storage/      OPFS/FSA/RAM-Ziel und IndexedDB-Resume-Metadaten
js/stream/       Encoder, Receiver, Double Buffer, Kamera-Scanner, Benchmark
js/worker/       optischer Encoder-Worker mit direktem Fallback
tests/test-v2.mjs 45 automatisierte v2-Tests
```

## Tests

Auf dem Server:

```bash
cd /var/www/jk-z.de/html/colorcode
npm test
```

`npm` installiert hierbei nichts; das Skript startet zuerst die 15 v1-Regressionen und danach 45 v2-Tests. Die v1-Suite deckt ab:

1. Byte/Nibble/Farbe-Roundtrip
2. Header und Byteordnung
3. verschiedene Größen und leere Dateien
4. alle Bytewerte `00…FF`
5. UTF-8-Dateinamen
6. CRC32-Prüfvektor
7. alle RS-Stufen mit gemischten Fehlern und Erasures
8. beschädigte sowie fehlende Farbfelder
9. Multi-Frame-Reihenfolge und Duplikate
10. Farbverschiebung, Helligkeit und Kontrast
11. vollständige Simulation mit Perspektive, Farbstich, Rauschen, Unschärfe und
    beschädigten Zellen

Zusätzlich führt `tests/browser-tests.html` einen Canvas- und Web-Crypto-Roundtrip im
aktuell geöffneten Browser aus.

Der Entwicklungsstand wurde mit Node.js 22.14.0 getestet; 15/15 v1- und 45/45 v2-Tests bestehen. In dieser Serverumgebung ist kein GUI-Browser installiert. Daher wurden Chrome
Android, Safari iOS, Chrome/Firefox/Edge Desktop nicht physisch automatisiert ausgeführt;
die Browser-Testseite und der unten beschriebene reale Kameratest sind Teil der Abnahme.

## Manueller End-to-End-Abnahmetest

1. Eine zufällige Datei erzeugen, z. B. Linux/macOS:

   ```bash
   head -c 4096 /dev/urandom > chromacode-test.bin
   sha256sum chromacode-test.bin
   ```

2. Auf Gerät A `https://jk-z.de/colorcode/` öffnen, Datei auswählen und zunächst
   `48 × 48 / Extreme` oder `64 × 64 / High` wählen.
3. Codes erzeugen. Bei mehreren Frames **Auto-Anzeige** verwenden oder die PNGs auf
   einem gut beleuchteten, reflexionsarmen zweiten Display öffnen.
4. Auf einem aktuellen Smartphone dieselbe URL über HTTPS öffnen und Kamera erlauben.
5. Jeden Frame vollständig zeigen, bis die Fortschrittsanzeige `n / n` meldet. Reihenfolge
   ist egal; der Signalton bestätigt die Übernahme.
6. Rekonstruierte Datei herunterladen. Die Anwendung zeigt nur dann
   **Prüfsumme OK**, wenn CRC32 und SHA-256 stimmen.
7. Download lokal prüfen:

   ```bash
   sha256sum chromacode-test.bin ~/Downloads/chromacode-test.bin
   cmp chromacode-test.bin ~/Downloads/chromacode-test.bin
   ```

   Beide SHA-256-Werte müssen identisch sein; `cmp` darf keine Ausgabe liefern.

Unter Windows kann `Get-FileHash .\chromacode-test.bin -Algorithm SHA256` für Original
und Download verwendet werden.

## Manueller PC→Smartphone-STREAM-v2-Test

1. Für die erste reale Abnahme eine zufällige Datei von 100–500 KiB verwenden und
   ihren SHA-256 außerhalb des Browsers notieren.
2. PC und Smartphone öffnen `https://jk-z.de/colorcode/`. Am PC zum Abschnitt
   **Protocol Version 2** scrollen.
3. Am Smartphone **STREAM-Scanner starten**, OPFS wählen und Kamera erlauben. Bei
   Safari ohne geeignete Persistenz für den ersten Test den RAM-Fallback verwenden.
4. Am PC die Datei im v2-Bereich wählen. Empfohlener Start: `64×64`, lokale ECC
   `High`, `K=16`, `R=4`, `5 FPS`, SYNC-Intervall `20`. Optional auf beiden Seiten
   dasselbe Passwort eingeben.
5. **STREAM starten** und anschließend **Vollbild** wählen. Das Smartphone so halten,
   dass Quiet Zone und alle vier Finder vollständig sichtbar bleiben. Spiegelung,
   digitales Zoom und bewegte UI neben dem Code vermeiden.
6. Der Smartphone-Status muss von SEARCHING über SYNCING nach LOCKED wechseln. DATA-
   und PARITY-Frames werden danach automatisch gesammelt; erkannte Duplikate und
   Frameverluste brauchen keine manuelle Aktion.
7. Falls DEGRADED/RECALIBRATING erscheint: ruhiger halten, mehr Umgebungslicht nutzen,
   Displayreflexion vermeiden oder auf 2–5 FPS beziehungsweise ein 48er-Raster
   zurückgehen. Der nächste periodische SYNC stellt Geometrie und Farbmodell wieder her.
8. Nach END erscheint ausschließlich bei identischem SHA-256 der Status
   **SHA-256 VERIFIED** und der Downloadbutton. Datei herunterladen und extern mit
   `sha256sum`, `cmp` oder `Get-FileHash` gegen das Original prüfen.
9. Erst danach schrittweise 10/15 FPS oder größere Raster im Benchmark testen. Die
   nominelle Bildrate ist nicht die erfolgreiche Kameraframerate.

Für einen Frameverlusttest kann der Code kurz verdeckt werden, jedoch nicht länger als
die gewählte Generationparität auffangen kann. Bei `16+4` dürfen pro Generation
höchstens vier unabhängige Shards fehlen. Der PC→Smartphone-Test ist in dieser
headless Serverumgebung nicht physisch durchgeführt worden; der automatisierte Test
prüft denselben Chunk→Crypto→Generation-ECC→optischer Gridcodec→Decode→Decrypt→SHA-
Datenweg, ersetzt aber keine reale Kameraabnahme.

## Browseranforderungen

- ES2022-Module, Typed Arrays, BigInt/DataView und TextEncoder/TextDecoder
- Web Crypto API (`crypto.subtle`, `getRandomValues`)
- Canvas 2D und `createImageBitmap` für Bildimport
- `navigator.mediaDevices.getUserMedia` für Live-Kamera
- sicherer HTTPS-Kontext für Web Crypto und Kamera

Zielbrowser sind aktuelle Versionen von Chrome Android, Safari iOS, Chrome Desktop,
Firefox Desktop und Edge Desktop. Ohne Kamera oder ohne Berechtigung bleibt der
Bildimport vollständig verwendbar. Bei HTTP wird die Kamera verständlich deaktiviert.

## Installation und Betrieb

Die Anwendung ist statisch. Ein Webserver muss lediglich den Ordner unter
`/colorcode/` ausliefern und für `.js` den MIME-Type `text/javascript` kennen. Es sind
keine Schreibrechte für den Webserver, kein Node-Prozess und kein Buildschritt nötig.
Die Content-Security-Policy im HTML verbietet fremde Scripts, Verbindungen und Objekte.

Für Kameraeinsatz muss das Zertifikat für `jk-z.de` gültig sein. Nach Änderungen kann
die Browser-Testseite unter `https://jk-z.de/colorcode/tests/browser-tests.html` geöffnet
werden.

## Bekannte Grenzen

- Eine echte Kamera-/Display-/Druckerkette lässt sich nicht vollständig synthetisch
  beweisen. Stark lokales Mischlicht, farbige Reflexionen, Clipping oder ein kleiner
  Druckerfarbraum können mehrere der 16 Klassen gleichzeitig kollabieren lassen.
- JPEG sollte nur mit hoher Qualität verwendet werden; PNG ist für gespeicherte Frames
  vorzuziehen.
- Sehr große Dateien erzeugen absichtlich viele Frames. Das Format optimiert reale
  Scanbarkeit und Integrität, nicht maximale theoretische Dichte.
- Live-Findersuche ist auf ein vollständig sichtbares, annähernd quadratisches Symbol mit
  allen vier Markern ausgelegt. Starke Verdeckung der Finder ist nicht korrigierbar.
- Diese RAM-Grenze gilt für STATIC v1. STREAM v2 schreibt bevorzugt in OPFS oder einen expliziten Datei-Handle; ein kompletter Empfänger-Reload setzt den Kamera- und Hashzustand derzeit dennoch zurück.
- Der ZIP-Export ist unkomprimiert (Store), damit keine Fremdbibliothek nötig ist.
- iOS kann akustisches Feedback je nach Autoplay-Regel unterdrücken; die visuelle
  Bestätigung bleibt erhalten.

## Sicherheit

Dateinamen werden auf einen sicheren Basenamen reduziert und ausschließlich per
`textContent` ausgegeben. MIME-Typen werden validiert. Rekonstruierte Bytes werden nie
automatisch angezeigt oder ausgeführt, sondern nur als Blob zum expliziten Download
angeboten. Es gibt kein `eval`, kein HTML aus Dateimetadaten und keine Interpretation des
Dateiinhalts.

