---
type: reference
title: Der Replay-Vertrag
summary: Der verpflichtende run/seed/verdict-Vertrag, der ein Spiel abspielbar macht, was die run-Funktion empfängt und zurückgibt, wie der Seed einen Trace an eine Runde bindet und wie der Server ihn abspielt.
---

Der Replay-Vertrag ist die eine verpflichtende Fläche, auf die sich ein **abspielbares** Spiel mit der Plattform einigt. Er wird als `@caputchin/replay-contract` veröffentlicht: ein bewusst winziges, abhängigkeitsfreies Paket (es wird in ein versiegeltes Replay-Isolate geladen, also zieht es nichts herein). Alles andere an deinem Spiel, die Engine, der Renderer, das Trace-Format, gehört dir.

Ein Spiel, das ein konformes `run` ausliefert, ist **abspielbar** und kann so einen Site-Key **gaten**; ein Spiel ohne eines ist trotzdem einbettbar, aber nur als UX (es zeigt **Nicht abspielbar**). Sieh dir [Ein Spiel an den Marketplace ausliefern](/docs/marketplace-game-development/overview#replayable-vs-not-replayable) für diese Unterscheidung an.

## Die `run`-Funktion

Ein abspielbares Spiel liefert ein JS- oder WASM-Modul, das eine Funktion namens `run` exportiert:

```ts
run(seed, config, trace) -> { passed, score, durationMs }
```

```ts
type RunFn<C = unknown> = (
  seed: Seed,
  config: C | null,
  trace: Uint8Array | string,
) => Verdict | Promise<Verdict>
```

| Argument | Quelle | Bedeutung |
|---|---|---|
| `seed` | Server | Der Pro-Runde-[Seed](#the-seed). Das server-eigene Setup der Runde. |
| `config` | Server | Die Gameplay-Config, **nullable und opak**: die Plattform inspiziert sie nie, und sie wird beim Replay serverseitig neu aufgelöst, nie vom Client behauptet. `null` bedeutet "nutz die eigenen Standards des Runs". Beim MVP übergibt der Server `null`; Pro-Site-Config ist eine aufgeschobene Phase. |
| `trace` | Client | Der opake Blob, den der Client des Spielers emittierte, den allein diese Funktion interpretiert. Die Plattform parst oder typisiert ihn nie; er ist rohe Bytes oder ein String, nur durch ein Größen-Cap und das Isolate-CPU-Limit begrenzt. |

Die Argument-Reihenfolge kodiert das Vertrauensmodell: `seed` und `config` sind das Server-Setup der Runde; `trace` ist die Eingabe des Spielers. **Gate-beeinflussende Werte (eine Bestehens-Schwelle, Leben) sind sicher aus `config` zu lesen, wären aber ein Bypass, wenn aus dem Client-`trace` gelesen.**

Das Modul muss die Funktion unter dem Namen `run` exportieren (die Konstante `RUN_EXPORT_NAME`). Der Replay-Host ruft sie auf und awaitet das Ergebnis (die WASM-Instanziierung ist beim ersten Aufruf asynchron).

## Der Seed [#the-seed]

```ts
type Seed = readonly [number, number, number, number]   // 128 bits, 4 u32 words, MSW first
```

Der Seed sind die niedrigen 128 Bit von `SHA-256(sessionId : gameId : roundIndex)`, getragen als vier vorzeichenlose 32-Bit-Wörter, höchstwertiges Wort zuerst. Das `deriveSeed(sessionId, gameId, roundIndex)` des Pakets berechnet ihn; der Server leitet ihn sowohl beim Ausgeben der Runde als auch erneut beim Abspielen ab, sodass er nie als vertrauenswürdige Client-Eingabe über die Leitung reitet.

Entscheidend ist, der Seed **bindet einen Trace an eine Session, ein Spiel und eine Runde**: einen fremden oder früheren Trace unter einem anderen Seed abzuspielen liefert `passed: false`. Diese Bindung ist, wie Trace-Injektion und Replay-Angriffe verteidigt werden, also musst du jede Zufälligkeit daraus seeden, damit das Live-Spiel und das Server-Replay übereinstimmen.

## Das Urteil

```ts
interface Verdict {
  readonly passed: boolean      // drives the verification decision
  readonly score: number        // game-defined, any finite number
  readonly durationMs: number   // finite, non-negative
}
```

`passed` ist das einzige Feld, das die CAPTCHA-Entscheidung treibt; `score` und `durationMs` werden im ausgegebenen Token getragen (und einem künftigen Scoreboard). Das `parseVerdict(value)` des Pakets validiert einen nicht-vertrauenswürdigen Rückgabewert und **behandelt ein fehlerhaftes Ergebnis als abgelehnte Runde, nie als bestehende**, sodass ein `run`, das wirft oder Müll zurückgibt, geschlossen fehlschlägt.

## Determinismus ist die ganze Anforderung [#determinism-is-the-whole-requirement]

Die eine harte Regel des Vertrags: `run` muss **rein und deterministisch über zwei Laufzeiten** sein, der Browser des Spielers und das Server-Isolate. Identische `(seed, config, trace)` müssen in beiden ein identisches Urteil liefern. Die häufigen Wege, das zu brechen, alle von der Publish-Selbstprüfung als `run-not-conforming` gefangen:

- `Date.now()`, `performance.now()`, `Math.random()` oder andere nicht-deterministische Globals lesen;
- externen Zustand lesen (DOM, Netzwerk, Speicher), den das Replay-Isolate nicht bereitstellt;
- sich auf Fließkomma-Mathematik verlassen, die sich zwischen Laufzeiten unterscheidet.

Wie du Determinismus erreichst (Festkomma, WASM-Spec-Floats oder IEEE-754 plus ein Neutralisierungs-Shim), ist deine Wahl; die Plattform hostet nur das Replay. Das [Engine-Kit](/docs/marketplace-game-development/engine-kit) stellt fertige deterministische Primitive bereit, wenn du lieber nicht deine eigenen rollst.

## Wie der Server es nutzt

```mermaid
sequenceDiagram
  participant P as Player browser
  participant S as Caputchin server
  participant R as Replay isolate
  S->>S: deriveSeed(sessionId, gameId, round)
  S->>P: issue round with seed
  P->>P: play, recording the trace
  P->>S: submit trace on pass
  S->>S: re-derive the same seed
  S->>R: run(seed, config, trace)
  R-->>S: verdict { passed, score, durationMs }
  S->>S: parseVerdict, decide
```

Das Artefakt, das das Isolate lädt, ist dein `run`-Bundle, entweder das Live-`entry` oder das dedizierte [`run`-Artefakt](/docs/marketplace-game-development/the-caputchin-json-manifest#the-run-artifact) aus dem Manifest.

## Siehe auch

- [Das engine-kit](/docs/marketplace-game-development/engine-kit): ein konformes `run` aus einem schlichten Reducer produzieren, mit für dich gehandhabtem Determinismus.
- [Das caputchin.json-Manifest](/docs/marketplace-game-development/the-caputchin-json-manifest#the-run-artifact): das `run`-Artefakt und seine Module deklarieren.
- [Ein Marketplace-Spiel bauen](/docs/marketplace-game-development/build-a-marketplace-game): den Trace aufzeichnen, den dieser Vertrag abspielt.
- [Publish-Fehler-Referenz](/docs/marketplace-game-development/publish-failed-reference): das `run-not-conforming`-Selbstprüfungs-Ergebnis.
```
