Compare commits

...
14 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
34 changed files with 422 additions and 8 deletions
+7
View File
@@ -14,6 +14,13 @@ jobs:
node-version: 22 node-version: 22
- name: Validate repository documentation - name: Validate repository documentation
run: test -f README.md && test -f AGENTS.md && test -f docs/versioning.md 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 - name: Validate JSON contracts and fixtures
run: | run: |
node --input-type=module <<'NODE' node --input-type=module <<'NODE'
+36
View File
@@ -4,6 +4,10 @@ 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 The first contract slice is under `contracts/v1/` and `fixtures/v1/`. It covers
server readiness, health, errors, and the transport-neutral realtime event server readiness, health, errors, and the transport-neutral realtime event
envelope so the client and server can be developed and tested together before envelope so the client and server can be developed and tested together before
@@ -12,9 +16,41 @@ request and response envelopes; bearer values in fixtures are synthetic only.
Twitch OAuth link and completion response envelopes are also versioned here; Twitch OAuth link and completion response envelopes are also versioned here;
fixtures contain no real credentials or tokens. 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 The shared boundary vocabulary includes the ordered permission levels in
`contracts/v1/permissions.json` and the client/server compatibility result in `contracts/v1/permissions.json` and the client/server compatibility result in
`contracts/v1/version-negotiation.json`. Server-owned state is delivered as `contracts/v1/version-negotiation.json`. Server-owned state is delivered as
the authenticated `realtime-event.json` envelope; clients must ignore event the authenticated `realtime-event.json` envelope; clients must ignore event
types they do not understand. 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.
+3 -2
View File
@@ -4,8 +4,9 @@
"title": "Trixie Session Response", "title": "Trixie Session Response",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": ["authenticated"], "required": ["authenticated", "must_replace_credentials"],
"properties": { "properties": {
"authenticated": { "const": true } "authenticated": { "const": true },
"must_replace_credentials": { "type": "boolean" }
} }
} }
+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
}
@@ -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,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 }
}
}
@@ -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
}
@@ -14,10 +14,26 @@
"properties": { "properties": {
"user_id": { "type": "string" }, "user_id": { "type": "string" },
"user_display_name": { "type": ["string", "null"] }, "user_display_name": { "type": ["string", "null"] },
"user_login": { "type": ["string", "null"] },
"slot_id": { "type": ["string", "null"] }, "slot_id": { "type": ["string", "null"] },
"is_live": { "type": ["boolean", "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,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" }
}
}
}
+5 -2
View File
@@ -4,13 +4,16 @@
"title": "Trixie public overlay", "title": "Trixie public overlay",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": ["slug", "html", "css", "javascript", "enabled", "disabled_design"], "required": ["slug", "html", "css", "javascript", "enabled", "disabled_design", "alert_group_window_ms", "alert_group_size", "alert_display_ms"],
"properties": { "properties": {
"slug": { "type": "string", "pattern": "^[a-z0-9-]+$" }, "slug": { "type": "string", "pattern": "^[a-z0-9-]+$" },
"html": { "type": "string" }, "html": { "type": "string" },
"css": { "type": "string" }, "css": { "type": "string" },
"javascript": { "type": "string" }, "javascript": { "type": "string" },
"enabled": { "type": "boolean" }, "enabled": { "type": "boolean" },
"disabled_design": { "type": "string" } "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 }
} }
} }
+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
}
+8
View File
@@ -16,3 +16,11 @@ 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 is missing, malformed, or invalid, and sends `connection.ready` only after
successful validation. 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.
+2 -1
View File
@@ -1,3 +1,4 @@
{ {
"authenticated": true "authenticated": true,
"must_replace_credentials": false
} }
+4
View File
@@ -0,0 +1,4 @@
{
"authenticated": true,
"must_replace_credentials": true
}
+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,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}!"
}
@@ -0,0 +1,6 @@
{
"guest_star_available": true,
"guest_star_reason": null,
"shared_chat_available": true,
"shared_chat_reason": null
}
@@ -1,4 +1,24 @@
{ {
"id": "guest-star-session-1", "id": "guest-star-session-1",
"guests": [] "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 @@
{
"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
}
]
+4 -1
View File
@@ -4,5 +4,8 @@
"css": "#alert { color: white; }", "css": "#alert { color: white; }",
"javascript": "", "javascript": "",
"enabled": true, "enabled": true,
"disabled_design": "transparent" "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
}
+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")