aiwg
Version:
Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo
181 lines (166 loc) • 6.39 kB
YAML
apiVersion: ops.aiwg.io/v1
kind: OpsPlaybook
metadata:
name: "{pipeline-name}"
labels:
domain: stream-operations
type: pipeline
spec:
description: "Full streaming pipeline: ingest → transcode → relay"
targets:
groups: ["{stream-hosts}"]
vars:
ingest_url: "{ingest-url}"
# e.g. rtmp://localhost:1935/live/stream or srt://0.0.0.0:9000
relay_url: "{relay-url}"
# Internal relay between transcode and multi-platform output
profile: "{profile-name}"
expected_bitrate_kbps: "{expected-kbps}"
bitrate_tolerance_pct: 15
health_timeout_s: 5
transcode_unit: "{transcode-unit}.service"
relay_unit: "{relay-unit}.service"
steps:
- id: verify-input
name: "Verify ingest source is reachable and delivering signal"
capability: stream-health-check
inputs:
source: "{{ ingest_url }}"
timeout: "{{ health_timeout_s }}s"
check_video: true
check_audio: true
on_failure: abort
# Do not start transcode if input is not delivering a valid signal.
# A missing or silent input produces silent failures downstream.
- id: validate-encoder-config
name: "Validate encoder configuration before starting transcode"
capability: stream-config-validate
depends_on: [verify-input]
inputs:
config_target: "{{ transcode_unit }}"
checks:
- no_literal_credentials
- env_file_populated
- hardware_accel_device_exists
- output_endpoint_reachable
on_failure: abort
- id: start-transcode
name: "Start transcoder with selected quality profile"
capability: stream-transcode
depends_on: [validate-encoder-config]
inputs:
input: "{{ ingest_url }}"
profile: "{{ profile }}"
output: "{{ relay_url }}"
unit: "{{ transcode_unit }}"
retry:
max_attempts: 3
backoff: "10s"
# On failure: check systemd journal before retry
# journalctl -u {{ transcode_unit }} --since '-30s' --no-pager
on_failure: abort
- id: verify-transcode-output
name: "Verify transcoded relay is delivering expected quality"
capability: stream-health-check
depends_on: [start-transcode]
inputs:
source: "{{ relay_url }}"
expected_bitrate: "{{ expected_bitrate_kbps }}"
tolerance_pct: "{{ bitrate_tolerance_pct }}"
check_video: true
check_framerate: true
check_audio: true
probe_duration_s: 10
# Wait 10 seconds of relay output before measuring — allows encoder to stabilize
on_failure: abort
- id: approve-relay-cutover
name: "Gate: operator approval before pushing to live platforms"
capability: ops-gate
depends_on: [verify-transcode-output]
inputs:
type: automated
# Set to 'manual' to require explicit operator confirmation before relay starts
message: "Transcode verified at {{ expected_bitrate_kbps }}kbps ± {{ bitrate_tolerance_pct }}%. Starting multi-platform relay."
blast_radius: medium
# medium = visible to all platform subscribers
# high = use for first-time platform additions or key rotations
on_failure: abort
- id: start-relay
name: "Start multi-platform relay to all output destinations"
capability: stream-relay
depends_on: [approve-relay-cutover]
inputs:
source: "{{ relay_url }}"
unit: "{{ relay_unit }}"
destinations:
- "{platform-1-rtmp-url}"
# Never embed stream keys in destination URLs. Use environment variable substitution:
# rtmp://${PLATFORM_1_INGEST}/live/${PLATFORM_1_KEY_FILE}
- "{platform-2-srt-url}"
# srt://${PLATFORM_2_HOST}:${PLATFORM_2_PORT}?passphrase=${SRT_PASSPHRASE_FILE}
reconnect_on_failure: true
reconnect_delay_s: 5
retry:
max_attempts: 3
backoff: "10s"
on_failure: escalate
- id: verify-relay-destinations
name: "Verify each output platform is receiving stream"
capability: stream-health-check
depends_on: [start-relay]
inputs:
check_destinations: true
destinations:
- name: "{platform-1}"
check_url: "{platform-1-stream-status-url}"
# Platform dashboard or API endpoint, not stream key URL
- name: "{platform-2}"
check_url: "{platform-2-stream-status-url}"
expected_bitrate: "{{ expected_bitrate_kbps }}"
tolerance_pct: "{{ bitrate_tolerance_pct }}"
probe_duration_s: 15
on_failure: warn
# Warn rather than abort — stream may be live and abort would disrupt it
- id: record-pipeline-start
name: "Log pipeline start for operational record"
capability: ops-audit-log
depends_on: [verify-relay-destinations]
inputs:
event: pipeline-started
pipeline: "{{ spec.description }}"
profile: "{{ profile }}"
destinations:
- "{platform-1}"
- "{platform-2}"
verified_bitrate: "{{ expected_bitrate_kbps }}"
teardown:
# Steps to execute when pipeline is stopped (graceful shutdown)
steps:
- id: stop-relay
name: "Stop relay — remove from platforms before stopping transcode"
capability: stream-stop
inputs:
unit: "{{ relay_unit }}"
drain_s: 5
# Allow 5s drain before hard stop to let platform buffers flush
- id: stop-transcode
name: "Stop transcoder after relay is confirmed stopped"
capability: stream-stop
depends_on: [stop-relay]
inputs:
unit: "{{ transcode_unit }}"
drain_s: 2
- id: record-pipeline-stop
name: "Log pipeline stop"
capability: ops-audit-log
depends_on: [stop-transcode]
inputs:
event: pipeline-stopped
pipeline: "{{ spec.description }}"
rollback:
# Manual rollback: restore previous transcode profile or relay config
# 1. Stop relay unit: systemctl stop {{ relay_unit }}
# 2. Stop transcode: systemctl stop {{ transcode_unit }}
# 3. Restore configs from backup (see stream-service profile change log)
# 4. Re-run playbook from start-transcode step
# See stream-pipeline-gates.md for rollback criteria