Skip to content

Commit 1105a2e

Browse files
authored
docs: add README architecture and recovery diagrams (#9)
## Summary - replace the README's ASCII architecture sketch with a Mermaid component and data-flow diagram - add a recovery state machine for persisted cooldowns, same-event resumption, and terminal-only replacement - add a provisioning and deployment flow covering YouTube/OAuth setup, host installation, visible validation, systemd enablement, and reboot verification - document the writable OBS service-file seed required by `--create-stream`, route failed validation back through provisioning, and defer service activation until validation succeeds - include the approved design and implementation plan for maintainers The dedicated normal-production lifecycle diagram was intentionally omitted. The existing numbered production loop remains the concise source for that flow. ## Impact Documentation only. Runtime code, configuration, systemd units, API behavior, retry policy, and release metadata are unchanged. ## Validation - diagram contract: exactly three Mermaid blocks, one `flowchart TB`, one `stateDiagram-v2`, and one `flowchart TD` - `markdownlint-cli2 '**/*.md'`: 0 errors across 11 files - `ruff check .`: passed - `pytest -q`: 89 passed - Python compile and executable-script checks: passed - secret-pattern and `git diff --check` scans: passed - Mermaid rendering: all three diagrams produced valid SVG and PNG output; visual QA passed ## Review Focus - diagram labels and arrow direction match the current implementation - recovery diagram remains recovery-focused and does not duplicate the omitted production lifecycle - deployment order is useful to operators without replacing the detailed OAuth instructions
1 parent fa7c621 commit 1105a2e

3 files changed

Lines changed: 683 additions & 22 deletions

File tree

README.md

Lines changed: 150 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,28 +28,55 @@ The intended outcome is an appliance-like encoder that can be deployed on a Rasp
2828

2929
## Architecture
3030

31-
```text
32-
camera -> FFprobe -> FFmpeg -> reusable YouTube ingest
33-
| |
34-
| progress | stream health
35-
v v
36-
youtube-autoencoder supervisor
37-
|
38-
| bounded JSON commands
39-
v
40-
youtube-autoencoder-api
41-
|
42-
OAuth, reconciliation, transitions, privacy
43-
|
44-
v
45-
YouTube Data API v3
46-
|
47-
v
48-
one marked YouTube Live broadcast
31+
```mermaid
32+
flowchart TB
33+
subgraph ArchitectureInputs["Source and compatibility inputs"]
34+
direction LR
35+
Camera["RTSP camera"]
36+
ObsScene["Optional OBS scene"]
37+
ObsService["Optional OBS service profile"]
38+
end
39+
40+
subgraph ArchitectureHost["Encoder host"]
41+
direction TB
42+
Systemd["systemd service"]
43+
Supervisor["youtube-autoencoder supervisor"]
44+
Probe["FFprobe source validation"]
45+
Encoder["FFmpeg media pipeline"]
46+
Helper["youtube-autoencoder-api"]
47+
State["Durable state and locks"]
48+
OAuth["OAuth client and token"]
49+
end
50+
51+
subgraph ArchitectureYouTube["YouTube"]
52+
direction LR
53+
Ingest["Reusable liveStream ingest"]
54+
Api["YouTube Data API v3"]
55+
Broadcast["One marked liveBroadcast and watch page"]
56+
end
57+
58+
Systemd -->|"start and restart"| Supervisor
59+
ObsScene -->|"source discovery"| Supervisor
60+
ObsService -->|"ingest compatibility"| Supervisor
61+
Supervisor -->|"probe"| Probe
62+
Camera -->|"RTSP media"| Probe
63+
Probe -->|"source health"| Supervisor
64+
Supervisor -->|"spawn and supervise"| Encoder
65+
Camera -->|"video"| Encoder
66+
Encoder -->|"progress"| Supervisor
67+
Encoder -->|"RTMPS media"| Ingest
68+
Supervisor -->|"bounded JSON commands"| Helper
69+
Helper <-->|"read and write"| State
70+
OAuth -->|"authorization"| Helper
71+
Helper <-->|"lifecycle and health"| Api
72+
Ingest -->|"stream health"| Api
73+
Api <-->|"create, bind, transition, verify"| Broadcast
4974
```
5075

5176
The supervisor owns local process health, polling, backoff, and publication timing. The API helper owns OAuth, remote resource reconciliation, durable lifecycle state, and serialized mutations. YouTube remains authoritative; the local state file is a recovery cache and is revalidated before mutations.
5277

78+
For the detailed reconciliation algorithm, lifecycle states, and test strategy, see the [idempotent lifecycle recovery design](docs/superpowers/specs/2026-07-10-idempotent-youtube-lifecycle-design.md).
79+
5380
### Components
5481

5582
| Component | Path | Responsibility |
@@ -105,6 +132,41 @@ OBS compatibility mode can also read and update:
105132

106133
### Recovery Behavior
107134

135+
Every recovery path first preserves ownership and retry state. Media must be fresh and YouTube ingest active before reconciliation can create or transition anything.
136+
137+
```mermaid
138+
stateDiagram-v2
139+
state "Startup or restart" as RecoveryStartup
140+
state "Managed stream generation" as RecoveryGeneration {
141+
state "Probe source and start FFmpeg" as RecoveryMedia
142+
state "Require fresh active ingest" as RecoveryIngest
143+
state "Reconcile exact ownership markers" as RecoveryReconcile
144+
state "Create and bind unlisted generation" as RecoveryCreate
145+
state "Resume one nonterminal event" as RecoveryManaged
146+
state "Testing, live, and publication gates" as RecoveryGates
147+
state "Verified public stream" as RecoveryStable
148+
state "Public stream with API cooldown" as RecoveryPublicFallback
149+
150+
[*] --> RecoveryMedia
151+
RecoveryMedia --> RecoveryIngest : media progress fresh
152+
RecoveryIngest --> RecoveryReconcile : YouTube ingest active
153+
RecoveryReconcile --> RecoveryManaged : one marked nonterminal event
154+
RecoveryReconcile --> RecoveryCreate : none, terminal, or missing
155+
RecoveryCreate --> RecoveryManaged : insert and bind verified
156+
RecoveryManaged --> RecoveryGates
157+
RecoveryGates --> RecoveryStable : two healthy live observations and privacy readback
158+
RecoveryStable --> RecoveryPublicFallback : API unavailable, media healthy
159+
RecoveryPublicFallback --> RecoveryStable : API recovers
160+
}
161+
state "Persist classified cooldown" as RecoveryBackoff
162+
163+
[*] --> RecoveryStartup
164+
RecoveryStartup --> RecoveryBackoff : retry deadline active
165+
RecoveryBackoff --> RecoveryStartup : deadline expires or host restarts
166+
RecoveryStartup --> RecoveryGeneration : no active deadline
167+
RecoveryGeneration --> RecoveryBackoff : recoverable failure, preserve ownership
168+
```
169+
108170
| Failure | Expected behavior |
109171
| --- | --- |
110172
| Camera offline before stream start | Source probe fails; no new broadcast is created; source backoff is persisted. |
@@ -136,6 +198,48 @@ This verifies the YouTube account, OAuth token, reusable stream, ingest URL, bro
136198

137199
## Installation
138200

201+
Provision the YouTube control plane and the encoder host in this order. The detailed console steps and commands remain in the sections that follow.
202+
203+
```mermaid
204+
flowchart TD
205+
DeploymentChannel["Enable YouTube Live on the target channel"]
206+
DeploymentProject["Enable YouTube Data API v3"]
207+
DeploymentAudience["Configure a compatible OAuth audience"]
208+
DeploymentClient["Create a TV or Limited Input OAuth client"]
209+
DeploymentRuntime["Install FFmpeg, Python, and project scripts"]
210+
DeploymentConfig["Create private encoder, OAuth, and writable service files"]
211+
DeploymentAuthorize["Authorize the channel account"]
212+
DeploymentCamera["Configure camera source and ingest profile"]
213+
DeploymentStreamDecision{"Reusable stream already configured?"}
214+
DeploymentProvision["Run visible test with --create-stream"]
215+
DeploymentValidate["Run unlisted visible validation"]
216+
DeploymentValidationDecision{"Validation succeeds?"}
217+
DeploymentDiagnose["Fix OAuth, source, ingest, or quota issue"]
218+
DeploymentEnable["Enable the systemd service"]
219+
DeploymentReboot["Reboot the encoder host"]
220+
DeploymentVerify["Verify encoder and remote-management recovery"]
221+
DeploymentOperate["Unattended operation"]
222+
223+
DeploymentChannel --> DeploymentProject
224+
DeploymentProject --> DeploymentAudience
225+
DeploymentAudience --> DeploymentClient
226+
DeploymentClient --> DeploymentRuntime
227+
DeploymentRuntime --> DeploymentConfig
228+
DeploymentConfig --> DeploymentAuthorize
229+
DeploymentAuthorize --> DeploymentCamera
230+
DeploymentCamera --> DeploymentStreamDecision
231+
DeploymentStreamDecision -->|"No"| DeploymentProvision
232+
DeploymentProvision --> DeploymentValidate
233+
DeploymentStreamDecision -->|"Yes"| DeploymentValidate
234+
DeploymentValidate --> DeploymentValidationDecision
235+
DeploymentValidationDecision -->|"No"| DeploymentDiagnose
236+
DeploymentDiagnose --> DeploymentStreamDecision
237+
DeploymentValidationDecision -->|"Yes"| DeploymentEnable
238+
DeploymentEnable --> DeploymentReboot
239+
DeploymentReboot --> DeploymentVerify
240+
DeploymentVerify --> DeploymentOperate
241+
```
242+
139243
Install runtime packages:
140244

141245
```bash
@@ -164,7 +268,6 @@ Install the system service for a dedicated user named `encoder`:
164268
```bash
165269
sudo install -m 0644 systemd/youtube-autoencoder@.service /etc/systemd/system/youtube-autoencoder@.service
166270
sudo systemctl daemon-reload
167-
sudo systemctl enable --now youtube-autoencoder@encoder.service
168271
```
169272

170273
For a user service instead:
@@ -173,9 +276,10 @@ For a user service instead:
173276
mkdir -p ~/.config/systemd/user
174277
cp systemd/user/youtube-autoencoder.service ~/.config/systemd/user/
175278
systemctl --user daemon-reload
176-
systemctl --user enable --now youtube-autoencoder.service
177279
```
178280

281+
The unit is installed but deliberately left disabled until OAuth, source, ingest, and visible-stream validation succeed.
282+
179283
For Raspberry Pi specific notes, see `docs/raspberry-pi.md`.
180284

181285
## Configuration Model
@@ -214,7 +318,17 @@ YTA_OBS_SCENE_FILE=/home/encoder/.config/obs-studio/basic/scenes/Untitled.json
214318
YTA_OBS_SOURCE_NAME=Camera RTSP
215319
```
216320

217-
The OBS service file supplies the reusable YouTube RTMPS server and stream key. A fresh reusable stream can be provisioned by running the visible test with `--create-stream`; the helper then updates this file before starting the test encoder.
321+
The OBS service file supplies the reusable YouTube RTMPS server and stream key. It must exist, be writable by the service user, and contain a non-empty `settings.key` before `--create-stream` runs. For a fresh deployment, keep the service disabled and seed a placeholder key:
322+
323+
```json
324+
{
325+
"settings": {
326+
"key": "provision-new-stream"
327+
}
328+
}
329+
```
330+
331+
The placeholder is not a YouTube stream key. `--create-stream` uses it to confirm that no existing stream matches, then replaces it with the new reusable stream key and writes the ingest server before starting the test encoder. A missing file or empty key does not enter the creation path.
218332

219333
## YouTube API and OAuth Provisioning
220334

@@ -327,7 +441,7 @@ If you already have an OBS-compatible `service.json` with a YouTube stream key:
327441
youtube-autoencoder-api status
328442
```
329443

330-
For a fresh setup, make sure the service user can write the configured OBS service file. Then provision the reusable stream through an API-managed visible test after the rest of the encoder config is in place:
444+
For a fresh setup, create the writable OBS service file with the placeholder `settings.key` shown in the Configuration Model. Then provision the reusable stream through an API-managed visible test after the rest of the encoder config is in place:
331445

332446
```bash
333447
YTA_INSTANCE_ID=encoder-hostname youtube-autoencoder-api run-visible-test \
@@ -336,6 +450,20 @@ YTA_INSTANCE_ID=encoder-hostname youtube-autoencoder-api run-visible-test \
336450

337451
This validates OAuth, reusable-stream provisioning, idempotent broadcast reconciliation, stream binding, ingest detection, transitions to `testing` and `live`, and explicit completion. The normal unattended service never completes on exit.
338452

453+
### 8. Enable the Service
454+
455+
After visible validation succeeds, enable the system service:
456+
457+
```bash
458+
sudo systemctl enable --now youtube-autoencoder@encoder.service
459+
```
460+
461+
For a user service instead:
462+
463+
```bash
464+
systemctl --user enable --now youtube-autoencoder.service
465+
```
466+
339467
### Common Authorization Problems
340468

341469
| Symptom | Likely cause | Fix |

0 commit comments

Comments
 (0)