Compare commits

45 Commits
Author SHA1 Message Date
ReaperOfVeriod 54141e9ccb Define and validate restored-session setup state
API dependency and security checks / audit (push) Canceled after 0s
API checks / checks (push) Canceled after 0s
2026-10-25 17:56:08 +01:00
ReaperOfVeriod 2957524af9 preserve guest star media state
API checks / checks (push) Canceled after 0s
API dependency and security checks / audit (push) Canceled after 0s
2026-10-25 07:34:18 +01:00
ReaperOfVeriod 28478cf5fe add guest star invite contract
API checks / checks (push) Canceled after 0s
API dependency and security checks / audit (push) Canceled after 0s
2026-10-25 07:33:25 +01:00
ReaperOfVeriod fb7f13f0ab Document collaboration capability contract
API checks / checks (push) Canceled after 0s
API dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:20:30 +01:00
ReaperOfVeriod 67378bd193 Document overlay alert settings contract
API checks / checks (push) Canceled after 0s
API dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:18:40 +01:00
ReaperOfVeriod 99a1e3e07d Document plugin secret binding contract
API checks / checks (push) Canceled after 0s
API dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:16:30 +01:00
ReaperOfVeriod ceef6b11b2 Document plugin secret requirements
API checks / checks (push) Canceled after 0s
API dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:14:02 +01:00
ReaperOfVeriod 0400b12566 Add typed command argument contract
API checks / checks (push) Canceled after 0s
API dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:12:38 +01:00
ReaperOfVeriod 4efa617e25 Add user command rule contract
API checks / checks (push) Canceled after 0s
API dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:10:22 +01:00
ReaperOfVeriod fece516828 Document command simulation contracts
API checks / checks (push) Canceled after 0s
API dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:03:35 +01:00
ReaperOfVeriod ee7b4c5404 Add plugin lifecycle contracts
API checks / checks (push) Canceled after 0s
API dependency and security checks / audit (push) Canceled after 0s
2026-10-25 06:01:09 +01:00
ReaperOfVeriod c96e61e526 Document API ownership boundary
API dependency and security checks / audit (push) Canceled after 0s
API checks / checks (push) Canceled after 0s
2026-10-25 05:56:07 +01:00
ReaperOfVeriod 97ae23e1f2 Add backup restore contracts
API checks / checks (push) Canceled after 0s
API dependency and security checks / audit (push) Canceled after 0s
2026-10-25 05:55:15 +01:00
ReaperOfVeriod bb9b00295f Add Guest Star settings contract
API checks / checks (push) Canceled after 0s
API dependency and security checks / audit (push) Canceled after 0s
2026-10-25 05:52:19 +01:00
ReaperOfVeriod b89edfafa9 Define shared permissions and version contracts
API checks / checks (push) Canceled after 0s
API dependency and security checks / audit (push) Canceled after 0s
2026-10-25 05:39:57 +01:00
ReaperOfVeriod f1e5148480 add bot invocation contracts 2026-10-25 05:33:11 +01:00
ReaperOfVeriod 1a28d120c2 version server client compatibility contract 2026-10-25 05:32:08 +01:00
ReaperOfVeriod e4e4337852 add collaborator search contract 2026-10-25 05:27:00 +01:00
ReaperOfVeriod 687c6b7cc6 add collaborator contract 2026-10-25 05:24:56 +01:00
ReaperOfVeriod 20086b7776 add API issue and release scaffolding 2026-10-25 05:21:26 +01:00
ReaperOfVeriod 29c63121f4 validate API contracts in CI 2026-10-25 05:20:59 +01:00
ReaperOfVeriod 65018af7a0 add backup inventory contract 2026-10-25 05:20:32 +01:00
ReaperOfVeriod d28b289048 add backup run contract 2026-10-25 05:19:26 +01:00
ReaperOfVeriod 4e8ca67ccc add overlay response contract 2026-10-25 05:12:56 +01:00
ReaperOfVeriod a0d54b426d add bot secrets contract 2026-10-25 05:11:46 +01:00
ReaperOfVeriod 5564a30bf9 add bot command contract 2026-10-25 05:08:12 +01:00
ReaperOfVeriod 2856d04396 add shared chat session contract 2026-10-25 05:06:21 +01:00
ReaperOfVeriod 868e56e5e8 add OAuth scope status contract 2026-10-25 05:05:28 +01:00
ReaperOfVeriod 2e386c1e63 add Guest Star collaboration contract 2026-10-25 05:02:48 +01:00
ReaperOfVeriod 39844e6e51 add chat emote response contract 2026-10-25 05:00:27 +01:00
ReaperOfVeriod 1e58ecd7f0 add server diagnostics contract 2026-10-25 04:54:58 +01:00
ReaperOfVeriod eb2be975c1 add administrator notifications contract 2026-10-25 04:53:29 +01:00
ReaperOfVeriod 8c84e45bf9 feat: add moderation action contract 2026-10-25 04:49:34 +01:00
ReaperOfVeriod c63806b094 feat: add chat send contract 2026-10-25 04:48:01 +01:00
ReaperOfVeriod 06192f4717 feat: add category search contract 2026-10-25 04:46:49 +01:00
ReaperOfVeriod 5f09ad23ee feat: add stream info contracts 2026-10-25 04:45:55 +01:00
ReaperOfVeriod 5b42580245 feat: add activity feed contract 2026-10-25 04:44:15 +01:00
ReaperOfVeriod b22ee5eeab feat: define realtime authentication frame 2026-10-25 04:42:23 +01:00
ReaperOfVeriod c7e57aa015 feat: add readiness contract 2026-10-25 04:40:12 +01:00
ReaperOfVeriod 2760f0234f feat: add Twitch OAuth response contracts 2026-10-25 04:36:50 +01:00
ReaperOfVeriod d5a4027708 feat: define administrator auth contracts 2026-10-25 04:22:32 +01:00
ReaperOfVeriod d555bc0727 feat: define realtime and error contracts 2026-10-25 04:18:32 +01:00
ReaperOfVeriod 90335ce1bd feat: add initial v1 contracts
API dependency and security checks / audit (push) Canceled after 0s
API checks / checks (push) Canceled after 0s
2026-10-25 03:52:40 +01:00
ReaperOfVeriod 94b8a57010 chore: document branch workflow and security checks
API dependency and security checks / audit (push) Canceled after 0s
API checks / checks (push) Canceled after 0s
2026-10-25 03:49:01 +01:00
ReaperOfVeriod 608a8e558e chore: add MIT license
API checks / checks (push) Canceled after 0s
2026-10-25 03:45:44 +01:00
100 changed files with 1316 additions and 2 deletions
+19
View File
@@ -0,0 +1,19 @@
name: Bug report
description: Report a reproducible defect.
title: 'bug: '
labels: [bug]
body:
- type: textarea
id: reproduction
attributes:
label: Reproduction
description: List the smallest deterministic reproduction and expected behavior.
validations:
required: true
- type: textarea
id: verification
attributes:
label: Verification context
description: Include repository, branch, version, and relevant checks. Never include secrets.
validations:
required: true
+18
View File
@@ -0,0 +1,18 @@
name: Feature request
description: Propose a change that is consistent with PLAN.md.
title: 'feature: '
labels: [enhancement]
body:
- type: textarea
id: problem
attributes:
label: Problem and proposed behavior
validations:
required: true
- type: checkboxes
id: plan
attributes:
label: Planning
options:
- label: I checked PLAN.md and this does not silently change an existing decision.
required: true
+23 -1
View File
@@ -9,4 +9,26 @@ jobs:
runs-on: debian-latest runs-on: debian-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: test -f README.md && test -f AGENTS.md && test -f docs/versioning.md - uses: actions/setup-node@v4
with:
node-version: 22
- name: Validate repository documentation
run: test -f README.md && test -f AGENTS.md && test -f docs/versioning.md
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Validate restored-session schema and negative cases
run: |
python -m pip install jsonschema==4.26.0
python tests/session_contract.py
- name: Validate JSON contracts and fixtures
run: |
node --input-type=module <<'NODE'
import { readdir, readFile } from 'node:fs/promises';
for (const directory of ['contracts/v1', 'fixtures/v1']) {
for (const name of await readdir(directory)) {
if (!name.endsWith('.json')) continue;
JSON.parse(await readFile(`${directory}/${name}`, 'utf8'));
}
}
NODE
+19
View File
@@ -0,0 +1,19 @@
name: API release check
on:
workflow_dispatch:
push:
tags: ['v*.*.*']
jobs:
release-check:
runs-on: debian-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Validate release metadata
run: test -s CHANGELOG.md && test -s README.md && test -s docs/versioning.md
- name: Validate JSON
run: node --input-type=module -e "import { readdir, readFile } from 'node:fs/promises'; for (const d of ['contracts/v1','fixtures/v1']) for (const f of await readdir(d)) if (f.endsWith('.json')) JSON.parse(await readFile(d + '/' + f, 'utf8'));"
+16
View File
@@ -0,0 +1,16 @@
name: API dependency and security checks
on:
push:
pull_request:
jobs:
audit:
runs-on: debian-latest
steps:
- uses: actions/checkout@v4
- name: Audit JavaScript dependencies when a lockfile exists
run: |
if test -f package-lock.json; then
npm audit --audit-level=high
fi
+10
View File
@@ -0,0 +1,10 @@
# Changelog
All notable API contract changes are recorded here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Development work is
performed on `dev`; stable release entries are added when the user authorizes a
release.
## [Unreleased]
- Add versioned backup inventory and manual-backup response contracts.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 ReaperOfVeriod
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+50
View File
@@ -4,3 +4,53 @@ Shared versioned HTTP and authenticated WebSocket contracts for `trixie-server`
This repository contains schemas, shared enums, error codes, compatibility fixtures, contract tests, and API documentation only. This repository contains schemas, shared enums, error codes, compatibility fixtures, contract tests, and API documentation only.
The API repository is the boundary between the server authority and the Linux
client presentation layer. It contains no Twitch credentials, database logic,
or UI behavior.
The first contract slice is under `contracts/v1/` and `fixtures/v1/`. It covers
server readiness, health, errors, and the transport-neutral realtime event
envelope so the client and server can be developed and tested together before
Twitch integration. It also defines the first administrator authentication
request and response envelopes; bearer values in fixtures are synthetic only.
Twitch OAuth link and completion response envelopes are also versioned here;
fixtures contain no real credentials or tokens.
`GET /api/v1/auth/session` returns `authenticated: true` and the current
`must_replace_credentials` flag from the administrator account. Restoring a
session must not bypass first-run setup. Older servers omit this flag; clients
must not interpret an absent flag as completed credential replacement.
The shared boundary vocabulary includes the ordered permission levels in
`contracts/v1/permissions.json` and the client/server compatibility result in
`contracts/v1/version-negotiation.json`. Server-owned state is delivered as
the authenticated `realtime-event.json` envelope; clients must ignore event
types they do not understand.
Guest Star channel settings are described by
`contracts/v1/guest-star-channel-settings-response.json`. The browser-source
token is a server-to-authenticated-client value and fixtures use a synthetic
placeholder only.
The Twitch-authoritative collaboration capability probe is described by
`collaboration-capabilities-response.json`; unavailable beta surfaces include
a machine-readable reason without exposing Twitch tokens.
Destructive backup recovery uses the explicit `backup-restore-request.json`
and `backup-restore-response.json` contracts; test fixtures contain no real
database paths or credentials.
The authenticated WebAssembly plugin lifecycle uses the manifest, install,
installed-response, and plugin-secret-binding-request contracts. Fixtures
contain only synthetic package metadata and no executable module or secret.
Command simulation uses `bot-command-simulate-request.json` and
`bot-command-simulate-response.json`. A sample response is the default and
real network requests require an explicit `use_real_request` flag; fixtures
contain only local synthetic data.
Named-user command rules use `bot-command-user-rule.json`; fixtures contain
only synthetic Twitch IDs and literal response templates.
Typed command arguments use `bot-command-argument-definition.json` and remain
literal validated data.
+18
View File
@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/activity-list-response.json",
"title": "Trixie activity feed response",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["event_id", "event_type", "occurred_at", "payload"],
"properties": {
"event_id": { "type": "string", "minLength": 1 },
"event_type": { "type": "string", "pattern": "^[a-z0-9_.-]+$" },
"occurred_at": { "type": "string", "format": "date-time" },
"payload": { "type": "object" }
}
},
"maxItems": 100
}
@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "severity", "title", "message", "seen_at", "resolved_at", "created_at"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"severity": { "type": "string", "enum": ["info", "warning", "error"] },
"title": { "type": "string" },
"message": { "type": "string" },
"seen_at": { "type": ["string", "null"], "format": "date-time" },
"resolved_at": { "type": ["string", "null"], "format": "date-time" },
"created_at": { "type": "string", "format": "date-time" }
}
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.invalid/contracts/v1/auth-login-request.json",
"title": "Trixie Login Request",
"type": "object",
"additionalProperties": false,
"required": ["username", "password"],
"properties": {
"username": { "type": "string", "minLength": 1 },
"password": { "type": "string", "minLength": 1 }
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.invalid/contracts/v1/auth-login-response.json",
"title": "Trixie Login Response",
"type": "object",
"additionalProperties": false,
"required": ["session_token", "must_replace_credentials"],
"properties": {
"session_token": { "type": "string", "minLength": 1 },
"must_replace_credentials": { "type": "boolean" }
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.invalid/contracts/v1/auth-session-response.json",
"title": "Trixie Session Response",
"type": "object",
"additionalProperties": false,
"required": ["authenticated", "must_replace_credentials"],
"properties": {
"authenticated": { "const": true },
"must_replace_credentials": { "type": "boolean" }
}
}
+14
View File
@@ -0,0 +1,14 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["filename", "size_bytes", "modified_at"],
"properties": {
"filename": { "type": "string" },
"size_bytes": { "type": "integer", "minimum": 0 },
"modified_at": { "type": ["string", "null"] }
}
}
}
+17
View File
@@ -0,0 +1,17 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.local/contracts/v1/backup-restore-request.json",
"title": "Backup restore request",
"type": "object",
"required": ["filename", "confirmation"],
"properties": {
"filename": {
"type": "string",
"pattern": "^trixie-[A-Za-z0-9T_.-]+\\.dump$",
"maxLength": 128
},
"confirmation": { "const": "RESTORE" },
"emergency_override": { "type": "boolean", "default": false }
},
"additionalProperties": false
}
+13
View File
@@ -0,0 +1,13 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.local/contracts/v1/backup-restore-response.json",
"title": "Backup restore response",
"type": "object",
"required": ["status", "filename", "reason"],
"properties": {
"status": { "type": "string", "enum": ["ok", "failed"] },
"filename": { "type": "string" },
"reason": { "type": ["string", "null"] }
},
"additionalProperties": false
}
+11
View File
@@ -0,0 +1,11 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"required": ["status", "filename", "reason"],
"properties": {
"status": { "type": "string", "enum": ["ok", "failed"] },
"filename": { "type": ["string", "null"] },
"reason": { "type": ["string", "null"] }
}
}
@@ -0,0 +1,17 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.invalid/contracts/v1/bot-command-argument-definition.json",
"title": "Trixie typed bot command argument",
"type": "object",
"additionalProperties": false,
"required": ["name", "kind", "required"],
"properties": {
"name": { "type": "string", "pattern": "^[A-Za-z0-9_]{1,64}$" },
"kind": { "enum": ["text", "number", "choice", "username", "duration"] },
"required": { "type": "boolean" },
"default": { "type": ["string", "null"], "maxLength": 200 },
"minimum": { "type": ["integer", "null"] },
"maximum": { "type": ["integer", "null"] },
"choices": { "type": "array", "maxItems": 64, "items": { "type": "string", "minLength": 1, "maxLength": 200 } }
}
}
@@ -0,0 +1,14 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.invalid/contracts/v1/bot-command-invoke-request.json",
"title": "Trixie bot command invocation request",
"type": "object",
"additionalProperties": false,
"required": ["name", "user_id", "permission_level"],
"properties": {
"name": { "type": "string", "minLength": 1, "maxLength": 64 },
"user_id": { "type": "string", "minLength": 1, "maxLength": 64 },
"permission_level": { "enum": ["everyone", "subscriber", "moderator", "broadcaster"] },
"variables": { "type": "object", "additionalProperties": { "type": "string" } }
}
}
@@ -0,0 +1,14 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.invalid/contracts/v1/bot-command-invoke-response.json",
"title": "Trixie bot command invocation response",
"type": "object",
"additionalProperties": false,
"required": ["status", "response", "remaining_cooldown_seconds", "error"],
"properties": {
"status": { "enum": ["ok", "cooldown", "not_found", "permission_denied", "error"] },
"response": { "type": ["string", "null"] },
"remaining_cooldown_seconds": { "type": ["integer", "null"], "minimum": 0 },
"error": { "type": ["string", "null"] }
}
}
@@ -0,0 +1,29 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.invalid/contracts/v1/bot-command-simulate-request.json",
"title": "Trixie bot command simulation request",
"type": "object",
"additionalProperties": false,
"required": ["template"],
"properties": {
"template": { "type": "string", "minLength": 1, "maxLength": 2000 },
"variables": { "type": "object", "additionalProperties": { "type": "string", "maxLength": 2000 } },
"web_request": { "type": ["object", "null"] },
"sample_response": {},
"use_real_request": { "type": "boolean", "default": false },
"response_fields": {
"type": "array",
"maxItems": 128,
"items": {
"type": "array",
"prefixItems": [
{ "type": "string", "minLength": 1, "maxLength": 64 },
{ "type": "string", "minLength": 1, "maxLength": 256 }
],
"items": false,
"minItems": 2,
"maxItems": 2
}
}
}
}
@@ -0,0 +1,14 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.invalid/contracts/v1/bot-command-simulate-response.json",
"title": "Trixie bot command simulation response",
"type": "object",
"additionalProperties": false,
"required": ["status", "extracted_variables"],
"properties": {
"status": { "const": "ok" },
"rendered": { "type": ["string", "null"], "maxLength": 2000 },
"extracted_variables": { "type": "object", "additionalProperties": { "type": "string" } },
"error": { "type": ["string", "null"], "maxLength": 500 }
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.invalid/contracts/v1/bot-command-user-rule.json",
"title": "Trixie named-user command rule",
"type": "object",
"additionalProperties": false,
"required": ["user_id"],
"properties": {
"user_id": { "type": "string", "pattern": "^[0-9]{1,64}$" },
"response_template": { "type": ["string", "null"], "minLength": 1, "maxLength": 2000 }
}
}
+26
View File
@@ -0,0 +1,26 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/bot-commands-response.json",
"title": "Trixie bot command list",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "name", "description", "permission_level", "aliases", "tags", "cooldown_seconds", "cooldown_scope", "response_template", "error_template", "enabled", "failure_count", "disabled_reason"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"name": { "type": "string", "pattern": "^[A-Za-z0-9_-]+$" },
"description": { "type": "string" },
"permission_level": { "enum": ["everyone", "subscriber", "moderator", "broadcaster"] },
"aliases": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
"tags": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
"cooldown_seconds": { "type": "integer", "minimum": 0, "maximum": 86400 },
"cooldown_scope": { "enum": ["global", "user", "role"] },
"response_template": { "type": "string" },
"error_template": { "type": ["string", "null"] },
"enabled": { "type": "boolean" },
"failure_count": { "type": "integer", "minimum": 0 },
"disabled_reason": { "type": ["string", "null"] }
}
}
}
+19
View File
@@ -0,0 +1,19 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/bot-secrets-response.json",
"title": "Trixie bot secret metadata",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "name", "description", "used_by", "created_at", "updated_at"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"name": { "type": "string", "pattern": "^[A-Za-z0-9_]+$" },
"description": { "type": "string" },
"used_by": { "type": "array", "items": { "type": "string" } },
"created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" }
}
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "array",
"maxItems": 50,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["name", "url", "provider"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"url": { "type": "string", "format": "uri", "pattern": "^https://" },
"provider": { "type": "string", "enum": ["twitch", "7tv"] }
}
}
}
+13
View File
@@ -0,0 +1,13 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/chat-send-request.json",
"title": "Twitch chat send request",
"type": "object",
"additionalProperties": false,
"required": ["identity", "message"],
"properties": {
"identity": { "enum": ["streamer", "bot"] },
"message": { "type": "string", "minLength": 1, "maxLength": 500 },
"reply_parent_message_id": { "type": "string", "maxLength": 128 }
}
}
+13
View File
@@ -0,0 +1,13 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/chat-send-response.json",
"title": "Twitch chat send response",
"type": "object",
"additionalProperties": false,
"required": ["message_id", "is_sent"],
"properties": {
"message_id": { "type": "string", "minLength": 1 },
"is_sent": { "type": "boolean" },
"drop_reason": { "type": ["string", "null"] }
}
}
@@ -0,0 +1,14 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.local/contracts/v1/collaboration-capabilities-response.json",
"title": "Collaboration capabilities response",
"type": "object",
"required": ["guest_star_available", "shared_chat_available"],
"properties": {
"guest_star_available": { "type": "boolean" },
"guest_star_reason": { "type": ["string", "null"] },
"shared_chat_available": { "type": "boolean" },
"shared_chat_reason": { "type": ["string", "null"] }
},
"additionalProperties": false
}
@@ -0,0 +1,39 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": ["object", "null"],
"additionalProperties": false,
"required": ["id", "guests"],
"properties": {
"id": { "type": "string" },
"guests": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["user_id"],
"properties": {
"user_id": { "type": "string" },
"user_display_name": { "type": ["string", "null"] },
"user_login": { "type": ["string", "null"] },
"slot_id": { "type": ["string", "null"] },
"is_live": { "type": ["boolean", "null"] },
"volume": { "type": ["integer", "null"], "minimum": 0, "maximum": 100 },
"assigned_at": { "type": ["string", "null"], "format": "date-time" },
"audio_settings": { "$ref": "#/$defs/media_settings" },
"video_settings": { "$ref": "#/$defs/media_settings" }
}
}
}
},
"$defs": {
"media_settings": {
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"is_host_enabled": { "type": ["boolean", "null"] },
"is_guest_enabled": { "type": ["boolean", "null"] },
"is_available": { "type": ["boolean", "null"] }
}
}
}
}
@@ -0,0 +1,14 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "login", "display_name"],
"properties": {
"id": { "type": "string" },
"login": { "type": "string" },
"display_name": { "type": "string" }
}
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["twitch_user_id", "display_name", "favorite", "last_invited_at"],
"properties": {
"twitch_user_id": { "type": "string" },
"display_name": { "type": "string" },
"favorite": { "type": "boolean" },
"last_invited_at": { "type": ["string", "null"] }
}
}
}
+13
View File
@@ -0,0 +1,13 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.invalid/contracts/v1/error.json",
"title": "Trixie API Error",
"type": "object",
"additionalProperties": false,
"required": ["code", "message", "request_id"],
"properties": {
"code": { "type": "string", "pattern": "^[a-z0-9_]+$" },
"message": { "type": "string", "minLength": 1 },
"request_id": { "type": "string", "minLength": 1 }
}
}
+17
View File
@@ -0,0 +1,17 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/game-search-response.json",
"title": "Twitch category search response",
"type": "array",
"maxItems": 25,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "name", "box_art_url"],
"properties": {
"id": { "type": "string", "minLength": 1 },
"name": { "type": "string", "minLength": 1 },
"box_art_url": { "type": "string", "format": "uri" }
}
}
}
@@ -0,0 +1,24 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.local/contracts/v1/guest-star-channel-settings-response.json",
"title": "Guest Star channel settings response",
"type": "object",
"required": [
"is_moderator_send_live_enabled",
"slot_count",
"is_browser_source_audio_enabled",
"group_layout",
"browser_source_token"
],
"properties": {
"is_moderator_send_live_enabled": { "type": "boolean" },
"slot_count": { "type": "integer", "minimum": 1, "maximum": 6 },
"is_browser_source_audio_enabled": { "type": "boolean" },
"group_layout": {
"type": "string",
"enum": ["TILED_LAYOUT", "SCREENSHARE_LAYOUT", "HORIZONTAL_LAYOUT", "VERTICAL_LAYOUT"]
},
"browser_source_token": { "type": "string", "minLength": 1 }
},
"additionalProperties": false
}
@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.local/contracts/v1/guest-star-invites-response.json",
"title": "Guest Star invites response",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["user_id", "invited_at", "status", "is_video_enabled", "is_audio_enabled"],
"properties": {
"user_id": { "type": "string", "minLength": 1 },
"invited_at": { "type": "string", "format": "date-time" },
"status": { "type": "string", "enum": ["INVITED", "ACCEPTED", "READY"] },
"is_video_enabled": { "type": "boolean" },
"is_audio_enabled": { "type": "boolean" }
}
}
}
+11
View File
@@ -0,0 +1,11 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.invalid/contracts/v1/health.json",
"title": "Trixie Health",
"type": "object",
"additionalProperties": false,
"required": ["status"],
"properties": {
"status": { "type": "string", "enum": ["ok"] }
}
}
@@ -0,0 +1,15 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/moderation-action-request.json",
"title": "Twitch moderation action",
"type": "object",
"additionalProperties": false,
"required": ["action", "user_id"],
"properties": {
"action": { "enum": ["ban", "timeout", "unban", "delete_message"] },
"user_id": { "type": "string", "minLength": 1, "maxLength": 64 },
"duration_seconds": { "type": "integer", "minimum": 1, "maximum": 1209600 },
"reason": { "type": "string", "maxLength": 500 },
"message_id": { "type": "string", "maxLength": 128 }
}
}
@@ -0,0 +1,12 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/moderation-action-response.json",
"title": "Twitch moderation action response",
"type": "object",
"additionalProperties": false,
"required": ["action", "applied"],
"properties": {
"action": { "type": "string" },
"applied": { "const": true }
}
}
+19
View File
@@ -0,0 +1,19 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/overlay-response.json",
"title": "Trixie public overlay",
"type": "object",
"additionalProperties": false,
"required": ["slug", "html", "css", "javascript", "enabled", "disabled_design", "alert_group_window_ms", "alert_group_size", "alert_display_ms"],
"properties": {
"slug": { "type": "string", "pattern": "^[a-z0-9-]+$" },
"html": { "type": "string" },
"css": { "type": "string" },
"javascript": { "type": "string" },
"enabled": { "type": "boolean" },
"disabled_design": { "type": "string" },
"alert_group_window_ms": { "type": "integer", "minimum": 0, "maximum": 5000 },
"alert_group_size": { "type": "integer", "minimum": 1, "maximum": 20 },
"alert_display_ms": { "type": "integer", "minimum": 500, "maximum": 60000 }
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.invalid/contracts/v1/permissions.json",
"title": "Trixie Permission Level",
"description": "The ordered permission levels used by chat commands and moderation controls.",
"type": "string",
"enum": ["everyone", "subscriber", "moderator", "broadcaster"]
}
+12
View File
@@ -0,0 +1,12 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.local/contracts/v1/plugin-install-request.json",
"title": "Plugin install request",
"type": "object",
"required": ["manifest", "wasm_base64"],
"properties": {
"manifest": { "$ref": "plugin-manifest.json" },
"wasm_base64": { "type": "string", "minLength": 1, "maxLength": 5592406 }
},
"additionalProperties": false
}
@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.local/contracts/v1/plugin-installed-response.json",
"title": "Installed plugin response",
"type": "object",
"required": ["id", "plugin_id", "name", "version", "manifest", "enabled", "installed_at", "updated_at"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"plugin_id": { "type": "string" },
"name": { "type": "string" },
"version": { "type": "string" },
"manifest": { "$ref": "plugin-manifest.json" },
"enabled": { "type": "boolean" },
"installed_at": { "type": "string" },
"updated_at": { "type": "string" }
},
"additionalProperties": false
}
+17
View File
@@ -0,0 +1,17 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.local/contracts/v1/plugin-manifest.json",
"title": "Trixie WebAssembly plugin manifest",
"type": "object",
"required": ["id", "name", "version", "api_version", "permissions", "entrypoint"],
"properties": {
"id": { "type": "string", "pattern": "^[a-z0-9.-]+$", "maxLength": 64 },
"name": { "type": "string", "minLength": 1, "maxLength": 128 },
"version": { "type": "string", "minLength": 1, "maxLength": 64 },
"api_version": { "type": "string", "maxLength": 32 },
"permissions": { "type": "array", "maxItems": 16, "items": { "enum": ["chat.read", "chat.send", "overlay.emit", "twitch.read", "twitch.manage"] } },
"required_secrets": { "type": "array", "maxItems": 16, "items": { "type": "string", "pattern": "^[A-Za-z0-9_]{1,100}$" } },
"entrypoint": { "type": "string", "minLength": 1, "maxLength": 64 }
},
"additionalProperties": false
}
@@ -0,0 +1,12 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.local/contracts/v1/plugin-secret-binding-request.json",
"title": "Plugin secret binding request",
"type": "object",
"required": ["secret_name", "approved"],
"properties": {
"secret_name": { "type": "string", "pattern": "^[A-Za-z0-9_]{1,100}$" },
"approved": { "type": "boolean" }
},
"additionalProperties": false
}
+11
View File
@@ -0,0 +1,11 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/readiness.json",
"title": "Trixie readiness response",
"type": "object",
"additionalProperties": false,
"required": ["status"],
"properties": {
"status": { "enum": ["ready", "database_unavailable"] }
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/realtime-auth-frame.json",
"title": "Trixie realtime authentication frame",
"type": "object",
"additionalProperties": false,
"required": ["frame_type", "token"],
"properties": {
"frame_type": { "const": "authenticate" },
"token": { "type": "string", "minLength": 1 }
}
}
+14
View File
@@ -0,0 +1,14 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.invalid/contracts/v1/realtime-event.json",
"title": "Trixie Realtime Event",
"type": "object",
"additionalProperties": false,
"required": ["event_id", "event_type", "occurred_at", "payload"],
"properties": {
"event_id": { "type": "string", "minLength": 1 },
"event_type": { "type": "string", "pattern": "^[a-z0-9_.-]+$" },
"occurred_at": { "type": "string", "format": "date-time" },
"payload": { "type": "object" }
}
}
@@ -0,0 +1,13 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"required": ["status", "database", "twitch_identity_count", "unseen_notification_count", "realtime"],
"properties": {
"status": { "type": "string", "enum": ["healthy"] },
"database": { "type": "string", "enum": ["connected"] },
"twitch_identity_count": { "type": "integer", "minimum": 0 },
"unseen_notification_count": { "type": "integer", "minimum": 0 },
"realtime": { "type": "string", "enum": ["available"] }
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.invalid/contracts/v1/server-info.json",
"title": "Trixie Server Info",
"type": "object",
"additionalProperties": false,
"required": ["api_version", "server_version", "minimum_client_version", "maximum_client_version", "status"],
"properties": {
"api_version": { "type": "string", "const": "v1" },
"server_version": { "type": "string", "minLength": 1 },
"minimum_client_version": { "type": "string", "minLength": 1 },
"maximum_client_version": { "type": "string", "minLength": 1 },
"status": { "type": "string", "enum": ["ready"] }
}
}
@@ -0,0 +1,23 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/shared-chat-session-response.json",
"title": "Twitch Shared Chat session response",
"type": ["object", "null"],
"additionalProperties": false,
"required": ["session_id", "host_broadcaster_id", "participants", "created_at", "updated_at"],
"properties": {
"session_id": { "type": "string", "minLength": 1 },
"host_broadcaster_id": { "type": "string", "minLength": 1 },
"participants": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["broadcaster_id"],
"properties": { "broadcaster_id": { "type": "string", "minLength": 1 } }
}
},
"created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" }
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/stream-info-request.json",
"title": "Twitch stream information update",
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"properties": {
"title": { "type": "string", "maxLength": 140 },
"game_id": { "type": "string", "maxLength": 64 },
"language": { "type": "string", "maxLength": 10 },
"tags": { "type": "array", "maxItems": 10, "items": { "type": "string", "maxLength": 25 } },
"content_classification_labels": { "type": "array", "maxItems": 10, "items": { "type": "string", "maxLength": 64 } }
}
}
+18
View File
@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/stream-info-response.json",
"title": "Twitch stream information",
"type": "object",
"additionalProperties": false,
"required": ["broadcaster_id", "broadcaster_name", "game_id", "game_name", "title", "language", "tags", "content_classification_labels"],
"properties": {
"broadcaster_id": { "type": "string", "minLength": 1 },
"broadcaster_name": { "type": "string", "minLength": 1 },
"game_id": { "type": "string" },
"game_name": { "type": "string" },
"title": { "type": "string" },
"language": { "type": "string" },
"tags": { "type": "array", "items": { "type": "string" } },
"content_classification_labels": { "type": "array", "items": { "type": "string" } }
}
}
@@ -0,0 +1,23 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/twitch-oauth-link-response.json",
"title": "Twitch OAuth authorization link response",
"type": "object",
"additionalProperties": false,
"required": ["identity", "scopes", "authorization_url", "expires_in_seconds"],
"properties": {
"identity": { "enum": ["streamer", "bot", "moderator"] },
"scopes": {
"type": "array",
"items": { "type": "string", "minLength": 1 },
"minItems": 1,
"uniqueItems": true
},
"authorization_url": {
"type": "string",
"format": "uri",
"pattern": "^https://id\\.twitch\\.tv/oauth2/authorize\\?"
},
"expires_in_seconds": { "type": "integer", "const": 600 }
}
}
+13
View File
@@ -0,0 +1,13 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/twitch-oauth-result.json",
"title": "Twitch OAuth completion response",
"type": "object",
"additionalProperties": false,
"required": ["identity", "twitch_user_id", "display_name"],
"properties": {
"identity": { "enum": ["streamer", "bot", "moderator"] },
"twitch_user_id": { "type": "string", "minLength": 1 },
"display_name": { "type": "string", "minLength": 1 }
}
}
@@ -0,0 +1,16 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.reaperofveriod.nl/contracts/v1/twitch-oauth-status-response.json",
"title": "Twitch OAuth scope status response",
"type": "object",
"additionalProperties": false,
"required": ["identity", "connected", "required_scopes", "granted_scopes", "missing_scopes", "reauthorization_required"],
"properties": {
"identity": { "enum": ["streamer", "bot", "moderator"] },
"connected": { "type": "boolean" },
"required_scopes": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
"granted_scopes": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
"missing_scopes": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
"reauthorization_required": { "type": "boolean" }
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://trixie.invalid/contracts/v1/version-negotiation.json",
"title": "Trixie Version Negotiation",
"type": "object",
"additionalProperties": false,
"required": ["api_version", "client_version", "server_version", "compatible"],
"properties": {
"api_version": { "type": "string", "const": "v1" },
"client_version": { "type": "string", "minLength": 1 },
"server_version": { "type": "string", "minLength": 1 },
"compatible": { "type": "boolean" },
"reason": { "type": "string", "minLength": 1 }
}
}
+23 -1
View File
@@ -1,4 +1,26 @@
# Contract Testing # Contract Testing
Fixtures cover HTTP responses, authenticated WebSocket events, errors, permissions, version negotiation, reconnects, and server-owned state transitions. Fixtures are deterministic, synthetic, and secret-free. Fixtures cover HTTP responses, authenticated WebSocket events, errors, permissions,
version negotiation, reconnects, and server-owned state transitions. Fixtures are
deterministic, synthetic, and secret-free. Every new contract must include at
least one valid fixture and a negative case where validation rules are important.
The current realtime envelope is deliberately transport-neutral: the server may
deliver it over an authenticated WebSocket or a future compatible transport, but
the event identity and payload rules remain the same. `event_id` is the stable
deduplication key for at-least-once delivery.
Browser clients authenticate the WebSocket with the first text frame described
by `realtime-auth-frame.json`. Session tokens are sent in that frame rather
than in a URL query parameter. The server closes the connection when the frame
is missing, malformed, or invalid, and sends `connection.ready` only after
successful validation.
The restored-session contract has Draft 2020-12 validation for both credential
replacement states, missing fields, wrong types, and unexpected properties:
```sh
uv run --with jsonschema==4.26.0 python tests/session_contract.py
```
This isolated validation dependency is tooling only, not an application dependency.
+9
View File
@@ -5,4 +5,13 @@
- Clients must tolerate unknown fields and events. - Clients must tolerate unknown fields and events.
- Breaking changes require a new major version, fixtures, migration notes, and coordinated releases. - Breaking changes require a new major version, fixtures, migration notes, and coordinated releases.
- Each release records minimum compatible client and server versions. - Each release records minimum compatible client and server versions.
- `GET /api/v1/server/info` publishes `minimum_client_version` and
`maximum_client_version`; clients must use these fields to detect an
incompatible pairing before enabling authenticated controls.
- Permission values are shared through `contracts/v1/permissions.json`.
They are ordered from least to most privileged: `everyone`, `subscriber`,
`moderator`, and `broadcaster`.
- The normalized result of a compatibility check is described by
`contracts/v1/version-negotiation.json`; an incompatible result includes a
human-readable `reason` while a compatible result may omit it.
+8
View File
@@ -0,0 +1,8 @@
[
{
"event_id": "synthetic-event-1",
"event_type": "channel.follow",
"occurred_at": "2026-10-25T12:00:00Z",
"payload": { "user_name": "SyntheticViewer" }
}
]
@@ -0,0 +1,11 @@
[
{
"id": "00000000-0000-0000-0000-000000000001",
"severity": "warning",
"title": "IP permanently rate-limited",
"message": "A source IP reached the login failure limit and requires review.",
"seen_at": null,
"resolved_at": null,
"created_at": "2026-10-25T12:00:00Z"
}
]
+4
View File
@@ -0,0 +1,4 @@
{
"session_token": "fixture-session-token-never-use",
"must_replace_credentials": true
}
@@ -0,0 +1,4 @@
{
"authenticated": true,
"must_replace_credentials": false
}
+4
View File
@@ -0,0 +1,4 @@
{
"authenticated": true,
"must_replace_credentials": true
}
+7
View File
@@ -0,0 +1,7 @@
[
{
"filename": "trixie-20261025T030000Z.dump",
"size_bytes": 4096,
"modified_at": "2026-10-25T03:00:01Z"
}
]
+5
View File
@@ -0,0 +1,5 @@
{
"status": "ok",
"filename": "trixie-20261025T030000Z.dump",
"reason": null
}
+5
View File
@@ -0,0 +1,5 @@
{
"status": "ok",
"filename": "trixie-20261025T030000Z.dump",
"reason": null
}
@@ -0,0 +1,9 @@
{
"name": "count",
"kind": "number",
"required": true,
"default": null,
"minimum": 1,
"maximum": 5,
"choices": []
}
@@ -0,0 +1,6 @@
{
"status": "ok",
"response": "Hello Alice",
"remaining_cooldown_seconds": null,
"error": null
}
@@ -0,0 +1,7 @@
{
"template": "Hello ${user_name}; status=${status}",
"variables": { "user_name": "PreviewUser" },
"sample_response": { "status": "ok" },
"use_real_request": false,
"response_fields": [["status", "$.status"]]
}
@@ -0,0 +1,6 @@
{
"status": "ok",
"rendered": "Hello PreviewUser; status=ok",
"extracted_variables": { "status": "ok" },
"error": null
}
+4
View File
@@ -0,0 +1,4 @@
{
"user_id": "123456789",
"response_template": "Welcome back, ${user_name}!"
}
+17
View File
@@ -0,0 +1,17 @@
[
{
"id": "00000000-0000-4000-8000-000000000001",
"name": "uptime",
"description": "Show uptime",
"permission_level": "everyone",
"aliases": [],
"tags": ["utility"],
"cooldown_seconds": 2,
"cooldown_scope": "user",
"response_template": "Online",
"error_template": null,
"enabled": true,
"failure_count": 0,
"disabled_reason": null
}
]
+10
View File
@@ -0,0 +1,10 @@
[
{
"id": "00000000-0000-4000-8000-000000000002",
"name": "API_Key",
"description": "External API key",
"used_by": [],
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
]
+12
View File
@@ -0,0 +1,12 @@
[
{
"name": "Kappa",
"url": "https://static-cdn.jtvnw.net/emoticons/v2/25/static/dark/2.0",
"provider": "twitch"
},
{
"name": "catJAM",
"url": "https://cdn.7tv.app/emote/60ae8f6f625f5a2a4d4e2b6f/2x.webp",
"provider": "7tv"
}
]
+5
View File
@@ -0,0 +1,5 @@
{
"message_id": "synthetic-message-id",
"is_sent": true,
"drop_reason": null
}
@@ -0,0 +1,6 @@
{
"guest_star_available": true,
"guest_star_reason": null,
"shared_chat_available": true,
"shared_chat_reason": null
}
@@ -0,0 +1,24 @@
{
"id": "guest-star-session-1",
"guests": [
{
"user_id": "123456",
"user_display_name": "GuestOne",
"user_login": "guestone",
"slot_id": "1",
"is_live": true,
"volume": 85,
"assigned_at": "2026-10-25T12:01:00Z",
"audio_settings": {
"is_host_enabled": true,
"is_guest_enabled": true,
"is_available": true
},
"video_settings": {
"is_host_enabled": true,
"is_guest_enabled": false,
"is_available": true
}
}
]
}
@@ -0,0 +1,7 @@
[
{
"id": "123456",
"login": "example_streamer",
"display_name": "ExampleStreamer"
}
]
+8
View File
@@ -0,0 +1,8 @@
[
{
"twitch_user_id": "123456",
"display_name": "ExampleStreamer",
"favorite": true,
"last_invited_at": "2026-10-25T12:00:00Z"
}
]
+5
View File
@@ -0,0 +1,5 @@
{
"code": "unauthorized",
"message": "Authentication is required.",
"request_id": "req_fixture_unauthorized"
}
+7
View File
@@ -0,0 +1,7 @@
[
{
"id": "123",
"name": "Synthetic Game",
"box_art_url": "https://static-cdn.jtvnw.net/ttv-boxart/123-52x72.jpg"
}
]
@@ -0,0 +1,7 @@
{
"is_moderator_send_live_enabled": true,
"slot_count": 4,
"is_browser_source_audio_enabled": false,
"group_layout": "TILED_LAYOUT",
"browser_source_token": "fixture-token-never-use"
}
@@ -0,0 +1,9 @@
[
{
"user_id": "144601104",
"invited_at": "2026-10-25T12:00:00Z",
"status": "READY",
"is_video_enabled": true,
"is_audio_enabled": false
}
]
@@ -0,0 +1,4 @@
{
"action": "timeout",
"applied": true
}
+11
View File
@@ -0,0 +1,11 @@
{
"slug": "followers",
"html": "<div id=\"alert\"></div>",
"css": "#alert { color: white; }",
"javascript": "",
"enabled": true,
"disabled_design": "transparent",
"alert_group_window_ms": 200,
"alert_group_size": 5,
"alert_display_ms": 4000
}
@@ -0,0 +1,17 @@
{
"id": "00000000-0000-4000-8000-000000000001",
"plugin_id": "example.plugin",
"name": "Example",
"version": "1.0.0",
"manifest": {
"id": "example.plugin",
"name": "Example",
"version": "1.0.0",
"api_version": "v1",
"permissions": ["chat.read"],
"entrypoint": "trixie_start"
},
"enabled": true,
"installed_at": "2026-10-25T00:00:00Z",
"updated_at": "2026-10-25T00:00:00Z"
}
@@ -0,0 +1,4 @@
{
"secret_name": "VTS_API_TOKEN",
"approved": true
}
+3
View File
@@ -0,0 +1,3 @@
{
"status": "ready"
}
+4
View File
@@ -0,0 +1,4 @@
{
"frame_type": "authenticate",
"token": "synthetic-session-token"
}
+9
View File
@@ -0,0 +1,9 @@
{
"event_id": "evt_fixture_activity_001",
"event_type": "activity.follow",
"occurred_at": "2026-01-01T00:00:00Z",
"payload": {
"username": "fixture_viewer",
"display_name": "Fixture Viewer"
}
}
@@ -0,0 +1,7 @@
{
"status": "healthy",
"database": "connected",
"twitch_identity_count": 2,
"unseen_notification_count": 0,
"realtime": "available"
}
+7
View File
@@ -0,0 +1,7 @@
{
"api_version": "v1",
"server_version": "0.1.0-dev",
"minimum_client_version": "0.1.0",
"maximum_client_version": "0.x",
"status": "ready"
}
@@ -0,0 +1,7 @@
{
"session_id": "shared-session-1",
"host_broadcaster_id": "1234",
"participants": [{ "broadcaster_id": "1234" }, { "broadcaster_id": "5678" }],
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
+10
View File
@@ -0,0 +1,10 @@
{
"broadcaster_id": "synthetic-broadcaster-id",
"broadcaster_name": "SyntheticStreamer",
"game_id": "123",
"game_name": "Synthetic Game",
"title": "A synthetic stream",
"language": "en",
"tags": ["Synthetic"],
"content_classification_labels": []
}
@@ -0,0 +1,6 @@
{
"identity": "bot",
"scopes": ["channel:bot", "user:read:chat", "user:write:chat"],
"authorization_url": "https://id.twitch.tv/oauth2/authorize?client_id=public-example-client&redirect_uri=http%3A%2F%2Flocalhost%3A8787%2Fapi%2Fv1%2Ftwitch%2Foauth%2Fcallback&response_type=code&scope=channel%3Abot+user%3Aread%3Achat+user%3Awrite%3Achat&state=synthetic-state",
"expires_in_seconds": 600
}
@@ -0,0 +1,5 @@
{
"identity": "streamer",
"twitch_user_id": "synthetic-twitch-user-id",
"display_name": "SyntheticStreamer"
}
@@ -0,0 +1,8 @@
{
"identity": "streamer",
"connected": false,
"required_scopes": ["user:read:chat"],
"granted_scopes": [],
"missing_scopes": ["user:read:chat"],
"reauthorization_required": true
}
@@ -0,0 +1,6 @@
{
"api_version": "v1",
"client_version": "0.1.0",
"server_version": "0.1.0",
"compatible": true
}
+24
View File
@@ -0,0 +1,24 @@
"""Validate restored-session fixtures and rejection cases against Draft 2020-12."""
import json
from pathlib import Path
from jsonschema import Draft202012Validator
root = Path(__file__).resolve().parents[1]
schema = json.loads((root / "contracts/v1/auth-session-response.json").read_text())
Draft202012Validator.check_schema(schema)
validator = Draft202012Validator(schema)
for name in ["auth-session.authenticated.json", "auth-session.bootstrap.json"]:
validator.validate(json.loads((root / "fixtures/v1" / name).read_text()))
for invalid in [
{"authenticated": True},
{"authenticated": False, "must_replace_credentials": False},
{"authenticated": True, "must_replace_credentials": "false"},
{"authenticated": True, "must_replace_credentials": False, "unexpected": True},
]:
assert not validator.is_valid(invalid), "Invalid session response was accepted"
print("Session schema: two valid fixtures and four rejection cases passed")