diff --git a/.forgejo/workflows/ci-gates.yml b/.forgejo/workflows/ci-gates.yml deleted file mode 100644 index ff6bff8..0000000 --- a/.forgejo/workflows/ci-gates.yml +++ /dev/null @@ -1,121 +0,0 @@ -name: ci-gates - -on: - pull_request: {} - push: - branches: [main] - workflow_dispatch: {} - -jobs: - checks: - if: ${{ github.server_url != 'https://codeberg.org' }} - runs-on: codeberg-medium-lazy - steps: - - name: Fetch source (no git required) - env: - GITHUB_TOKEN: ${{ github.token }} - shell: bash - run: | - set -euo pipefail - if [[ -z "${GITHUB_TOKEN:-}" ]]; then - echo "error: missing github.token" - exit 2 - fi - if ! command -v curl >/dev/null 2>&1; then - echo "error: curl is required" - exit 2 - fi - if ! command -v tar >/dev/null 2>&1; then - echo "error: tar is required" - exit 2 - fi - if [[ -z "${GITHUB_SHA:-}" ]]; then - echo "error: missing GITHUB_SHA" - exit 2 - fi - if [[ -z "${GITHUB_SERVER_URL:-}" ]]; then - echo "error: missing GITHUB_SERVER_URL" - exit 2 - fi - if [[ -z "${GITHUB_REPOSITORY:-}" ]]; then - echo "error: missing GITHUB_REPOSITORY" - exit 2 - fi - - rm -rf .repo - mkdir -p .repo - curl -fsSL -H "Authorization: token ${GITHUB_TOKEN}" \ - "${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz?rev=${GITHUB_SHA}" \ - -o .repo/src.tgz - tar -xzf .repo/src.tgz -C .repo --strip-components=1 - rm -f .repo/src.tgz - - - name: Bootstrap Rust + web build tools - shell: bash - run: | - set -euo pipefail - cd .repo - install -d -m 755 "$HOME/.local/bin" - echo "PATH=$HOME/.local/bin:$PATH" >> "$GITHUB_ENV" - export PATH="$HOME/.local/bin:$PATH" - - if ! command -v curl >/dev/null 2>&1; then - echo "error: curl is required" - exit 2 - fi - - if ! command -v cargo >/dev/null 2>&1; then - curl -fsSL https://sh.rustup.rs | sh -s -- -y --profile minimal - . "$HOME/.cargo/env" - elif [[ -f "$HOME/.cargo/env" ]]; then - . "$HOME/.cargo/env" - fi - - rustup target add wasm32-unknown-unknown - - if ! command -v trunk >/dev/null 2>&1; then - trunk_version="0.21.14" - arch="$(uname -m)" - case "${arch}" in - x86_64|amd64) trunk_target="x86_64-unknown-linux-gnu" ;; - aarch64|arm64) trunk_target="aarch64-unknown-linux-gnu" ;; - *) - echo "error: unsupported runner arch for trunk prebuilt binary: ${arch}" - exit 2 - ;; - esac - curl -fsSL "https://github.com/trunk-rs/trunk/releases/download/v${trunk_version}/trunk-${trunk_target}.tar.gz" \ - | tar -xz -C "$HOME/.local/bin" trunk - fi - - cargo --version - rustc --version - trunk --version - - - name: ECP lint - shell: bash - run: | - set -euo pipefail - cd .repo - bash ./scripts/ecp-lint.sh - - - name: Rust tests (core subset) - shell: bash - run: | - set -euo pipefail - cd .repo - if [[ -f "$HOME/.cargo/env" ]]; then - . "$HOME/.cargo/env" - fi - cargo test -p ec-core -p ec-crypto -p ec-moq -p ec-iroh -p ec-linux-iptv - - - name: Build web (apps/web) - shell: bash - run: | - set -euo pipefail - cd .repo - if [[ -f "$HOME/.cargo/env" ]]; then - . "$HOME/.cargo/env" - fi - cd apps/web - env -u NO_COLOR trunk build --release --public-url / diff --git a/.forgejo/workflows/deploy-cloudflare.yml b/.forgejo/workflows/deploy-cloudflare.yml index 2a5dc2e..dfe203b 100644 --- a/.forgejo/workflows/deploy-cloudflare.yml +++ b/.forgejo/workflows/deploy-cloudflare.yml @@ -10,118 +10,7 @@ concurrency: cancel-in-progress: true jobs: - checks: - if: ${{ github.server_url != 'https://codeberg.org' }} - runs-on: codeberg-medium-lazy - steps: - - name: Fetch Source (no git required) - env: - GITHUB_TOKEN: ${{ github.token }} - shell: bash - run: | - set -euo pipefail - if [[ -z "${GITHUB_TOKEN:-}" ]]; then - echo "error: missing github.token" - exit 2 - fi - if ! command -v curl >/dev/null 2>&1; then - echo "error: curl is required" - exit 2 - fi - if ! command -v tar >/dev/null 2>&1; then - echo "error: tar is required" - exit 2 - fi - if [[ -z "${GITHUB_SHA:-}" ]]; then - echo "error: missing GITHUB_SHA" - exit 2 - fi - if [[ -z "${GITHUB_SERVER_URL:-}" ]]; then - echo "error: missing GITHUB_SERVER_URL" - exit 2 - fi - if [[ -z "${GITHUB_REPOSITORY:-}" ]]; then - echo "error: missing GITHUB_REPOSITORY" - exit 2 - fi - - rm -rf .repo - mkdir -p .repo - - curl -fsSL -H "Authorization: token ${GITHUB_TOKEN}" \ - "${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz?rev=${GITHUB_SHA}" \ - -o .repo/src.tgz - tar -xzf .repo/src.tgz -C .repo --strip-components=1 - rm -f .repo/src.tgz - - - name: Bootstrap Rust + web build tools - shell: bash - run: | - set -euo pipefail - cd .repo - install -d -m 755 "$HOME/.local/bin" - echo "PATH=$HOME/.local/bin:$PATH" >> "$GITHUB_ENV" - export PATH="$HOME/.local/bin:$PATH" - - if ! command -v curl >/dev/null 2>&1; then - echo "error: curl is required" - exit 2 - fi - - if ! command -v cargo >/dev/null 2>&1; then - curl -fsSL https://sh.rustup.rs | sh -s -- -y --profile minimal - . "$HOME/.cargo/env" - elif [[ -f "$HOME/.cargo/env" ]]; then - . "$HOME/.cargo/env" - fi - rustup target add wasm32-unknown-unknown - - if ! command -v trunk >/dev/null 2>&1; then - trunk_version="0.21.14" - arch="$(uname -m)" - case "${arch}" in - x86_64|amd64) trunk_target="x86_64-unknown-linux-gnu" ;; - aarch64|arm64) trunk_target="aarch64-unknown-linux-gnu" ;; - *) - echo "error: unsupported runner arch for trunk prebuilt binary: ${arch}" - exit 2 - ;; - esac - curl -fsSL "https://github.com/trunk-rs/trunk/releases/download/v${trunk_version}/trunk-${trunk_target}.tar.gz" \ - | tar -xz -C "$HOME/.local/bin" trunk - fi - - - name: ECP lint - shell: bash - run: | - set -euo pipefail - cd .repo - bash ./scripts/ecp-lint.sh - - - name: Rust tests (core subset) - shell: bash - run: | - set -euo pipefail - cd .repo - if [[ -f "$HOME/.cargo/env" ]]; then - . "$HOME/.cargo/env" - fi - cargo test -p ec-core -p ec-crypto -p ec-moq -p ec-iroh -p ec-linux-iptv - - - name: Build site (web) - shell: bash - run: | - set -euo pipefail - cd .repo - if [[ -f "$HOME/.cargo/env" ]]; then - . "$HOME/.cargo/env" - fi - cd apps/web - env -u NO_COLOR trunk build --release --public-url / - deploy: - if: ${{ github.server_url != 'https://codeberg.org' }} - needs: checks runs-on: codeberg-medium-lazy steps: - name: Fetch Source (no git required) @@ -146,21 +35,13 @@ jobs: echo "error: missing GITHUB_SHA" exit 2 fi - if [[ -z "${GITHUB_SERVER_URL:-}" ]]; then - echo "error: missing GITHUB_SERVER_URL" - exit 2 - fi - if [[ -z "${GITHUB_REPOSITORY:-}" ]]; then - echo "error: missing GITHUB_REPOSITORY" - exit 2 - fi rm -rf .repo mkdir -p .repo # Use the authenticated API archive endpoint (works for private repos). curl -fsSL -H "Authorization: token ${GITHUB_TOKEN}" \ - "${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz?rev=${GITHUB_SHA}" \ + "https://codeberg.org/api/v1/repos/every-channel/every.channel/archive/${GITHUB_SHA}.tar.gz?rev=${GITHUB_SHA}" \ -o .repo/src.tgz tar -xzf .repo/src.tgz -C .repo --strip-components=1 rm -f .repo/src.tgz @@ -229,7 +110,7 @@ jobs: cd .repo curl -fsSL -X POST -H "Authorization: token ${GITHUB_TOKEN}" \ -H "content-type: application/json" \ - "${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA}" \ + "https://codeberg.org/api/v1/repos/every-channel/every.channel/statuses/${GITHUB_SHA}" \ -d '{"context":"deploy-cloudflare/breadcrumb","state":"pending","description":"bootstrap ok"}' >/dev/null - name: Configure CI Age identity @@ -254,7 +135,7 @@ jobs: curl -fsSL -X POST -H "Authorization: token ${GITHUB_TOKEN}" \ -H "content-type: application/json" \ - "${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA}" \ + "https://codeberg.org/api/v1/repos/every-channel/every.channel/statuses/${GITHUB_SHA}" \ -d '{"context":"deploy-cloudflare/breadcrumb","state":"pending","description":"age key ok"}' >/dev/null - name: Decrypt CI secrets from repo @@ -280,7 +161,7 @@ jobs: curl -fsSL -X POST -H "Authorization: token ${GITHUB_TOKEN}" \ -H "content-type: application/json" \ - "${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA}" \ + "https://codeberg.org/api/v1/repos/every-channel/every.channel/statuses/${GITHUB_SHA}" \ -d '{"context":"deploy-cloudflare/breadcrumb","state":"pending","description":"decrypt ok"}' >/dev/null - name: Build site (web) @@ -319,7 +200,7 @@ jobs: curl -fsSL -X POST -H "Authorization: token ${GITHUB_TOKEN}" \ -H "content-type: application/json" \ - "${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA}" \ + "https://codeberg.org/api/v1/repos/every-channel/every.channel/statuses/${GITHUB_SHA}" \ -d '{"context":"deploy-cloudflare/breadcrumb","state":"pending","description":"build ok"}' >/dev/null - name: Deploy worker @@ -335,5 +216,5 @@ jobs: curl -fsSL -X POST -H "Authorization: token ${GITHUB_TOKEN}" \ -H "content-type: application/json" \ - "${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA}" \ + "https://codeberg.org/api/v1/repos/every-channel/every.channel/statuses/${GITHUB_SHA}" \ -d '{"context":"deploy-cloudflare/breadcrumb","state":"success","description":"deploy ok"}' >/dev/null diff --git a/.forgejo/workflows/deploy-runner-images.yml b/.forgejo/workflows/deploy-runner-images.yml deleted file mode 100644 index d7463cc..0000000 --- a/.forgejo/workflows/deploy-runner-images.yml +++ /dev/null @@ -1,276 +0,0 @@ -name: deploy-runner-images - -on: - push: - tags: [boot-v*] - workflow_dispatch: - inputs: - release_tag: - description: "Release tag override (manual runs only)" - required: false - default: "" - publish_release: - description: "Publish artifacts to Forgejo release (true/false)" - required: false - default: "true" - build_x86_64_netboot: - description: "Build x86_64 netboot tarball (true/false)" - required: false - default: "true" - build_x86_64_iso: - description: "Build x86_64 installer ISO (true/false)" - required: false - default: "true" - -concurrency: - group: runner-image-deploy-${{ github.ref }} - cancel-in-progress: true - -jobs: - build-and-release: - if: ${{ github.server_url != 'https://codeberg.org' }} - runs-on: codeberg-medium-lazy - steps: - - name: Fetch source (no git required) - env: - GITHUB_TOKEN: ${{ github.token }} - shell: bash - run: | - set -euo pipefail - if [[ -z "${GITHUB_TOKEN:-}" ]]; then - echo "error: missing github.token" - exit 2 - fi - if ! command -v curl >/dev/null 2>&1; then - echo "error: curl is required" - exit 2 - fi - if ! command -v tar >/dev/null 2>&1; then - echo "error: tar is required" - exit 2 - fi - if [[ -z "${GITHUB_SHA:-}" ]]; then - echo "error: missing GITHUB_SHA" - exit 2 - fi - if [[ -z "${GITHUB_SERVER_URL:-}" ]]; then - echo "error: missing GITHUB_SERVER_URL" - exit 2 - fi - if [[ -z "${GITHUB_REPOSITORY:-}" ]]; then - echo "error: missing GITHUB_REPOSITORY" - exit 2 - fi - - rm -rf .repo - mkdir -p .repo - curl -fsSL -H "Authorization: token ${GITHUB_TOKEN}" \ - "${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz?rev=${GITHUB_SHA}" \ - -o .repo/src.tgz - tar -xzf .repo/src.tgz -C .repo --strip-components=1 - rm -f .repo/src.tgz - - - name: Bootstrap Nix - shell: bash - run: | - set -euo pipefail - if ! command -v nix >/dev/null 2>&1; then - curl -fsSL https://nixos.org/nix/install -o /tmp/install-nix.sh - sh /tmp/install-nix.sh --no-daemon --yes - fi - - if [[ -f "$HOME/.nix-profile/etc/profile.d/nix.sh" ]]; then - # shellcheck disable=SC1091 - . "$HOME/.nix-profile/etc/profile.d/nix.sh" - fi - if [[ -d "$HOME/.nix-profile/bin" ]]; then - echo "PATH=$HOME/.nix-profile/bin:$PATH" >> "$GITHUB_ENV" - export PATH="$HOME/.nix-profile/bin:$PATH" - fi - nix --version - - - name: Resolve build plan - id: plan - env: - INPUT_RELEASE_TAG: ${{ github.event.inputs.release_tag }} - INPUT_PUBLISH_RELEASE: ${{ github.event.inputs.publish_release }} - INPUT_BUILD_X86_64_NETBOOT: ${{ github.event.inputs.build_x86_64_netboot }} - INPUT_BUILD_X86_64_ISO: ${{ github.event.inputs.build_x86_64_iso }} - shell: bash - run: | - set -euo pipefail - - bool_norm() { - local raw - raw="$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" - case "${raw}" in - ''|true|1|yes|y|on) echo "true" ;; - false|0|no|n|off) echo "false" ;; - *) - echo "error: invalid boolean value '${1}'" >&2 - exit 2 - ;; - esac - } - - short_sha="${GITHUB_SHA:0:12}" - if [[ "${GITHUB_REF:-}" == refs/tags/* ]]; then - release_tag="${GITHUB_REF#refs/tags/}" - else - release_tag="${INPUT_RELEASE_TAG:-}" - if [[ -z "${release_tag}" ]]; then - release_tag="boot-${short_sha}" - fi - fi - - if [[ ! "${release_tag}" =~ ^[A-Za-z0-9._-]+$ ]]; then - echo "error: release tag contains unsupported characters: ${release_tag}" >&2 - exit 2 - fi - - publish_release="$(bool_norm "${INPUT_PUBLISH_RELEASE:-true}")" - build_x86_64_netboot="$(bool_norm "${INPUT_BUILD_X86_64_NETBOOT:-true}")" - build_x86_64_iso="$(bool_norm "${INPUT_BUILD_X86_64_ISO:-true}")" - - if [[ "${build_x86_64_netboot}" != "true" && "${build_x86_64_iso}" != "true" ]]; then - echo "error: at least one image build must be enabled" >&2 - exit 2 - fi - - artifact_suffix="${short_sha}-${GITHUB_RUN_NUMBER:-0}-${GITHUB_RUN_ATTEMPT:-1}" - - echo "release_tag=${release_tag}" >> "$GITHUB_OUTPUT" - echo "publish_release=${publish_release}" >> "$GITHUB_OUTPUT" - echo "build_x86_64_netboot=${build_x86_64_netboot}" >> "$GITHUB_OUTPUT" - echo "build_x86_64_iso=${build_x86_64_iso}" >> "$GITHUB_OUTPUT" - echo "artifact_suffix=${artifact_suffix}" >> "$GITHUB_OUTPUT" - - - name: Build runner boot images - env: - BUILD_X86_64_NETBOOT: ${{ steps.plan.outputs.build_x86_64_netboot }} - BUILD_X86_64_ISO: ${{ steps.plan.outputs.build_x86_64_iso }} - ARTIFACT_SUFFIX: ${{ steps.plan.outputs.artifact_suffix }} - shell: bash - run: | - set -euo pipefail - cd .repo - - if [[ -f "$HOME/.nix-profile/etc/profile.d/nix.sh" ]]; then - # shellcheck disable=SC1091 - . "$HOME/.nix-profile/etc/profile.d/nix.sh" - fi - export PATH="$HOME/.nix-profile/bin:$PATH" - - artifacts_dir="$PWD/.artifacts" - rm -rf "${artifacts_dir}" - mkdir -p "${artifacts_dir}" - - nix_args=(--accept-flake-config --extra-experimental-features "nix-command flakes") - - if [[ "${BUILD_X86_64_NETBOOT}" == "true" ]]; then - nix build "${nix_args[@]}" \ - .#nixosConfigurations.ec-runner-x86_64-netboot.config.system.build.netboot \ - -o result-netboot-x86_64 - tar -C result-netboot-x86_64 \ - -czf "${artifacts_dir}/ec-runner-x86_64-netboot-${ARTIFACT_SUFFIX}.tar.gz" \ - kernel initrd netboot.ipxe - fi - - if [[ "${BUILD_X86_64_ISO}" == "true" ]]; then - nix build "${nix_args[@]}" \ - .#nixosConfigurations.ec-runner-x86_64-iso.config.system.build.isoImage \ - -o result-iso-x86_64 - iso_source="" - if [[ -f result-iso-x86_64 ]]; then - iso_source="result-iso-x86_64" - else - iso_source="$(find -L result-iso-x86_64 -type f -name '*.iso' | head -n 1 || true)" - fi - if [[ -z "${iso_source}" ]]; then - echo "error: could not locate ISO output from result-iso-x86_64" >&2 - exit 2 - fi - cp -f "${iso_source}" "${artifacts_dir}/ec-runner-x86_64-iso-${ARTIFACT_SUFFIX}.iso" - fi - - if ! find "${artifacts_dir}" -maxdepth 1 -type f | grep -q .; then - echo "error: no image artifacts were produced" >&2 - exit 2 - fi - - ( - cd "${artifacts_dir}" - sha256sum -- * > SHA256SUMS.txt - ls -lh - ) - - - name: Publish artifacts to Forgejo release - if: ${{ steps.plan.outputs.publish_release == 'true' }} - env: - GITHUB_TOKEN: ${{ github.token }} - RELEASE_TAG: ${{ steps.plan.outputs.release_tag }} - shell: bash - run: | - set -euo pipefail - cd .repo - - if [[ -z "${GITHUB_TOKEN:-}" ]]; then - echo "error: missing github.token" - exit 2 - fi - if [[ -z "${GITHUB_SERVER_URL:-}" ]]; then - echo "error: missing GITHUB_SERVER_URL" - exit 2 - fi - if [[ -z "${GITHUB_REPOSITORY:-}" ]]; then - echo "error: missing GITHUB_REPOSITORY" - exit 2 - fi - - api_base="${GITHUB_SERVER_URL%/}/api/v1/repos/${GITHUB_REPOSITORY}" - release_json="$(curl -fsSL \ - -H "Authorization: token ${GITHUB_TOKEN}" \ - "${api_base}/releases/tags/${RELEASE_TAG}" 2>/dev/null || true)" - - if [[ -z "${release_json}" ]]; then - payload="$(cat </dev/null 2>&1; then - release_id="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' <<<"${release_json}" 2>/dev/null || true)" - fi - if [[ -z "${release_id}" ]]; then - release_id="$(printf '%s' "${release_json}" \ - | sed -nE 's/.*"id"[[:space:]]*:[[:space:]]*([0-9]+).*/\1/p' \ - | head -n 1)" - fi - if [[ -z "${release_id}" ]]; then - echo "error: failed to resolve release id for ${RELEASE_TAG}" >&2 - exit 2 - fi - - for asset_path in .artifacts/*; do - [[ -f "${asset_path}" ]] || continue - asset_name="$(basename "${asset_path}")" - curl -fsSL -X POST \ - -H "Authorization: token ${GITHUB_TOKEN}" \ - -H "content-type: application/octet-stream" \ - --data-binary @"${asset_path}" \ - "${api_base}/releases/${release_id}/assets?name=${asset_name}" >/dev/null - echo "uploaded: ${asset_name}" - done diff --git a/.forgejo/workflows/runner-smoke.yml b/.forgejo/workflows/runner-smoke.yml index 2d1f2e3..f3c470e 100644 --- a/.forgejo/workflows/runner-smoke.yml +++ b/.forgejo/workflows/runner-smoke.yml @@ -5,7 +5,6 @@ on: jobs: smoke: - if: ${{ github.server_url != 'https://codeberg.org' }} runs-on: codeberg-medium-lazy steps: - name: Basic runner + secret smoke test diff --git a/README.md b/README.md index 6124b6f..f4861f8 100644 --- a/README.md +++ b/README.md @@ -47,18 +47,6 @@ Runbook: cat docs/USAGE.md ``` -Git hosting topology: - -```sh -cat docs/GIT_HOSTING.md -``` - -NUC PXE rollout (Unifi + ProxyDHCP): - -```sh -cat docs/NUC_UNIFI_NETBOOT.md -``` - ## WebTransport Watch (MoQ) Publish (node -> Cloudflare relay): diff --git a/apps/web/app.js b/apps/web/app.js index 5842106..7c5dab5 100644 --- a/apps/web/app.js +++ b/apps/web/app.js @@ -162,7 +162,6 @@ function mountPlayer(relayUrl, name) { watch.setAttribute("name", name); watch.setAttribute("path", name); watch.setAttribute("volume", "1"); - watch.setAttribute("muted", ""); // Force WebTransport in-browser; websocket fallback has shown degraded // media behavior (especially audio) against public relay paths. @@ -170,16 +169,15 @@ function mountPlayer(relayUrl, name) { watch.connection.websocket = { enabled: false }; } - // Prefer a video element for native controls/audio routing. - // Start muted to satisfy autoplay policy, then unlock audio on user gesture. + // Use a media element for live playback so browser audio controls/policies apply naturally. const video = document.createElement("video"); video.className = "archiveVideo"; video.controls = true; video.autoplay = true; - video.muted = true; - video.volume = 1; + video.muted = false; video.playsInline = true; watch.appendChild(video); + mount.appendChild(watch); const forceAudioOn = () => { try { @@ -189,19 +187,9 @@ function mountPlayer(relayUrl, name) { // Best effort only. } }; - const unlockAudio = () => { - forceAudioOn(); - watch.backend?.paused?.set?.(true); - watch.backend?.paused?.set?.(false); - video.muted = false; - video.volume = 1; - void video.play().catch(() => {}); - setHint(`Live: subscribed to ${name} (audio unlocked)`, "ok"); - }; - document.addEventListener("pointerdown", unlockAudio, { once: true }); - video.addEventListener("pointerdown", unlockAudio, { once: true }); - setHint(`Live: subscribed to ${name} (tap video to unmute)`, "warn"); - void video.play().catch(() => {}); + forceAudioOn(); + window.setTimeout(forceAudioOn, 1000); + window.setTimeout(forceAudioOn, 4000); bindPlayerSignals(watch, name); } diff --git a/crates/ec-node/src/main.rs b/crates/ec-node/src/main.rs index 3b1be70..b4b68b3 100644 --- a/crates/ec-node/src/main.rs +++ b/crates/ec-node/src/main.rs @@ -6312,10 +6312,6 @@ async fn wt_publish(args: WtPublishArgs) -> Result<()> { if args.transcode { cmd.args([ - "-map", - "0:v:0", - "-map", - "0:a:0?", "-c:v", "libx264", "-preset", @@ -6336,10 +6332,8 @@ async fn wt_publish(args: WtPublishArgs) -> Result<()> { "1", "-c:a", "aac", - "-profile:a", - "aac_low", "-b:a", - "160k", + "128k", "-ac", "2", "-ar", diff --git a/docs/BRANCH_PROTECTION.md b/docs/BRANCH_PROTECTION.md deleted file mode 100644 index d0b3e02..0000000 --- a/docs/BRANCH_PROTECTION.md +++ /dev/null @@ -1,37 +0,0 @@ -# Branch Protection (Forgejo Primary) - -`main` should be protected to satisfy constitutional governance (`all changes merge through pull requests`) and to require CI before merge. - -## Required settings - -- Protected branch: `main` -- Direct pushes disabled -- Required approvals: `1` (or stricter) -- Required status checks: - - `ci-gates / checks` -- Require signed commits: enabled - -## Apply via script - -```sh -./scripts/fj-enforce-branch-protection.sh -``` - -Optional overrides: - -```sh -EVERY_CHANNEL_FORGE_HOST=https://forge.every.channel \ -EVERY_CHANNEL_FORGE_REPO=every-channel/every.channel \ -EVERY_CHANNEL_PROTECTED_BRANCH=main \ -EVERY_CHANNEL_REQUIRED_CHECKS="ci-gates / checks" \ -EVERY_CHANNEL_REQUIRED_APPROVALS=1 \ -./scripts/fj-enforce-branch-protection.sh -``` - -Token source order: - -1. `EVERY_CHANNEL_FORGE_TOKEN` / `FORGE_TOKEN` / `CODEBERG_TOKEN` env var -2. `secrets/forge-token.age` (preferred) via `agenix` or `age` -3. `secrets/codeberg-token.age` (compat) via `agenix` or `age` - -The token must have repository admin scope to edit branch protection. diff --git a/docs/DEPLOY_CLOUDFLARE.md b/docs/DEPLOY_CLOUDFLARE.md index 45dbef7..6993c60 100644 --- a/docs/DEPLOY_CLOUDFLARE.md +++ b/docs/DEPLOY_CLOUDFLARE.md @@ -1,25 +1,22 @@ # Cloudflare Deploy (Forgejo Actions) This repo deploys `https://every.channel` via Wrangler. -The deploy workflow is intended to run on the primary Forgejo host (not Codeberg/GitHub mirrors). ## Prereqs - Forgejo Actions enabled on the repo. -- Forgejo Actions secret `AGE_FORGE_SSH_KEY` set to the SSH private key used to decrypt repo-encrypted age secrets. -- `secrets/cloudflare-api-token.age` present in-repo and decryptable by `AGE_FORGE_SSH_KEY`. +- A Cloudflare API token stored as a Forgejo Actions secret: + - name: `CLOUDFLARE_API_TOKEN` -CI and deploy workflows: - -- PR/main checks: `.forgejo/workflows/ci-gates.yml` -- Deploy (main only, depends on checks): `.forgejo/workflows/deploy-cloudflare.yml` - -Mirror behavior: - -- Workflow jobs are guarded to skip execution on `https://codeberg.org`. +The workflow is defined in `.forgejo/workflows/deploy-cloudflare.yml`. ## Manual deploy (local) ```sh -./scripts/deploy-workers.sh +cd apps/tauri/ui +trunk build --release --public-url / + +cd deploy/cloudflare-worker +npm ci +npm run deploy ``` diff --git a/docs/GIT_HOSTING.md b/docs/GIT_HOSTING.md deleted file mode 100644 index 602abd6..0000000 --- a/docs/GIT_HOSTING.md +++ /dev/null @@ -1,45 +0,0 @@ -# Git Hosting Topology - -Primary host: - -- Forgejo (`origin`) - -Mirrors (push-only): - -- Codeberg (`mirror-codeberg`) -- GitHub (`mirror-github`) - -Codeberg and GitHub are distribution mirrors only. CI/actions should run on Forgejo primary. - -## Configure local remotes - -```sh -./scripts/git-configure-hosting.sh -``` - -Defaults: - -- `origin`: `git@forge.every.channel:every-channel/every.channel.git` -- `mirror-codeberg`: `git@codeberg.org:every-channel/every.channel.git` -- `mirror-github`: `git@github.com:every-channel/every.channel.git` - -You can override via env vars: - -- `EVERY_CHANNEL_PRIMARY_GIT_URL` -- `EVERY_CHANNEL_CODEBERG_GIT_URL` -- `EVERY_CHANNEL_GITHUB_GIT_URL` - -## Push mirrors - -```sh -./scripts/git-push-mirrors.sh -``` - -## Disable actions on Codeberg mirror - -```sh -EVERY_CHANNEL_FORGE_HOST=https://codeberg.org \ -EVERY_CHANNEL_FORGE_REPO=every-channel/every.channel \ -EVERY_CHANNEL_FORGE_ACTIONS_ENABLED=false \ -./scripts/forge-set-repo-actions.sh -``` diff --git a/docs/NUC_UNIFI_NETBOOT.md b/docs/NUC_UNIFI_NETBOOT.md deleted file mode 100644 index e3fbe55..0000000 --- a/docs/NUC_UNIFI_NETBOOT.md +++ /dev/null @@ -1,102 +0,0 @@ -# NUC Fleet Netboot (Unifi + ProxyDHCP) - -This runbook provisions x86_64 NUCs from runner netboot artifacts without USB image flashing. - -It uses: - -- Unifi DHCP for IP leases. -- Local `dnsmasq` ProxyDHCP for PXE/iPXE bootfile logic. -- Local HTTP + TFTP service for boot artifacts. - -## Why ProxyDHCP - -iPXE commonly needs two boot stages: - -1. firmware PXE -> `ipxe.efi` -2. iPXE -> `netboot.ipxe` - -If DHCP always returns `ipxe.efi`, clients can loop forever. ProxyDHCP handles stage-specific boot responses cleanly while leaving Unifi as the DHCP lease server. - -## Prerequisites - -- A Linux boot server on the same VLAN/L2 domain as the NUCs. -- Unifi network with normal DHCP enabled. -- Local DNS record on that VLAN: `boot.every.channel -> `. -- `curl`, `tar`, `python3`, `dnsmasq` installed on the boot server. -- Runner netboot artifact already published to Forgejo Releases (or available as a local tarball). - -## 1) Stage artifacts - -From repository root on the boot server: - -```sh -./scripts/netboot-stage.sh -``` - -Optional inputs: - -- `EVERY_CHANNEL_NETBOOT_RELEASE_TAG=boot-v2026.02.28` -- `EVERY_CHANNEL_NETBOOT_TARBALL=/path/to/ec-runner-x86_64-netboot-....tar.gz` -- `EVERY_CHANNEL_FORGE_TOKEN=` for private releases -- `EVERY_CHANNEL_NETBOOT_HOSTNAME=boot.every.channel` - -This stages: - -- `tmp/netboot/http/{kernel,initrd,netboot.ipxe}` -- `tmp/netboot/tftp/ipxe.efi` - -## 2) Serve HTTP + TFTP + ProxyDHCP - -Example (replace values for your VLAN): - -```sh -sudo \ - EVERY_CHANNEL_NETBOOT_LISTEN_IP=10.20.30.2 \ - EVERY_CHANNEL_NETBOOT_INTERFACE=eth0 \ - EVERY_CHANNEL_NETBOOT_PROXY_SUBNET=10.20.30.0/24 \ - EVERY_CHANNEL_NETBOOT_HOSTNAME=boot.every.channel \ - ./scripts/netboot-serve.sh -``` - -Notes: - -- Keep this process running during provisioning. -- Do not set Unifi DHCP bootfile options while this proxy mode is active. -- Ensure `boot.every.channel` resolves to the boot server IP from NUC clients. - -## 3) Unifi / NUC settings - -Unifi: - -- Keep DHCP enabled for the provisioning VLAN. -- Leave DHCP boot/TFTP overrides unset when using `netboot-serve.sh`. -- Create/verify local DNS host override: `boot.every.channel -> `. - -NUC BIOS: - -- Enable UEFI network boot (IPv4 PXE). -- Disable Legacy/CSM if possible. -- Put network boot before disk for first install cycle. - -## 4) Provision the fleet - -1. Boot each NUC on the provisioning VLAN. -2. PXE will chainload into iPXE and then runner `netboot.ipxe`. -3. Complete install/bootstrap flow on each node. -4. After successful install, switch boot order back to local disk. - -## Troubleshooting - -- Symptom: iPXE loop (`ipxe.efi` repeatedly) - - Cause: static DHCP bootfile without iPXE-aware logic. - - Fix: use ProxyDHCP flow (`netboot-serve.sh`) or set conditional DHCP rules. -- Symptom: NUC gets IP but never downloads boot artifacts - - Verify firewall allows UDP 67/68, UDP 69, and TCP 8080 between NUCs and boot server. -- Symptom: no `dnsmasq` offers seen - - Verify `EVERY_CHANNEL_NETBOOT_INTERFACE` and `EVERY_CHANNEL_NETBOOT_PROXY_SUBNET`. - -## Security / networking - -- Tailscale is not required for provisioning. -- Keep the provisioning VLAN isolated from regular clients. -- Stop `netboot-serve.sh` when rollout is complete. diff --git a/docs/RUNNER_IMAGES.md b/docs/RUNNER_IMAGES.md index 4d32893..ffe5c22 100644 --- a/docs/RUNNER_IMAGES.md +++ b/docs/RUNNER_IMAGES.md @@ -50,36 +50,6 @@ Build an aarch64 SD image: nix build .#nixosConfigurations.ec-runner-aarch64-sdimage.config.system.build.sdImage ``` -## CI Deploy (Forgejo Releases) - -Boot images can be built and published from CI via: - -- `.forgejo/workflows/deploy-runner-images.yml` - -Triggers: - -- Manual: `workflow_dispatch` -- Tags: `boot-v*` (for example `boot-v2026.02.28`) - -Manual inputs (all optional): - -- `release_tag` (defaults to `boot-`) -- `publish_release` (`true`/`false`, default `true`) -- `build_x86_64_netboot` (`true`/`false`, default `true`) -- `build_x86_64_iso` (`true`/`false`, default `true`) - -Published assets are attached to the resolved Forgejo release tag and include: - -- x86_64 netboot bundle (`kernel`, `initrd`, `netboot.ipxe`) as `.tar.gz` -- x86_64 installer `.iso` -- `SHA256SUMS.txt` - -Notes: - -- CI image publish is disabled on the Codeberg mirror host. -- Current CI scope is x86_64 targets; aarch64 image builds remain local/manual unless an aarch64-capable runner is added. -- For multi-NUC PXE rollout on Unifi networks, use `docs/NUC_UNIFI_NETBOOT.md`. - ## Outputs After building, artifacts will be in `./result` (a symlink into the Nix store). diff --git a/evolution/proposals/ECP-0063-cloudflare-moq-webtransport.md b/evolution/proposals/ECP-0063-cloudflare-moq-webtransport.md index 89ecf55..b75a2ad 100644 --- a/evolution/proposals/ECP-0063-cloudflare-moq-webtransport.md +++ b/evolution/proposals/ECP-0063-cloudflare-moq-webtransport.md @@ -1,6 +1,6 @@ # ECP-0063: Cloudflare MoQ Relay + WebTransport-Only Web Watch -Status: Implemented +Status: Draft ## Decision @@ -77,11 +77,6 @@ Implementation choice: Web share link: - `https://every.channel/watch?url=&name=` -## Alternatives considered - -- Keep the legacy WebRTC/WS path as primary. Rejected because it does not align with relay-native MoQ fanout goals. -- Wait for full draft parity across all relays before shipping. Rejected because live interop was already sufficient on the chosen relay path. - ## Rollout / Reversibility - Keep existing `/api/*` bootstrap endpoints during migration. diff --git a/evolution/proposals/ECP-0064-nixos-ec-node-publisher-module.md b/evolution/proposals/ECP-0064-nixos-ec-node-publisher-module.md index 916dc37..122d4ec 100644 --- a/evolution/proposals/ECP-0064-nixos-ec-node-publisher-module.md +++ b/evolution/proposals/ECP-0064-nixos-ec-node-publisher-module.md @@ -1,6 +1,6 @@ # ECP-0064: NixOS Module For `ec-node` WebTransport Publisher (Tower) -Status: Implemented +Status: Draft ## Decision @@ -41,12 +41,8 @@ Out of scope (defer): - Automatic lineup-based channel selection by callsign. - Secrets management (publisher doesn't require secrets for Cloudflare relay preview). -## Alternatives considered - -- Continue running publishers manually via shells/tmux. Rejected because it is not reproducible or restart-safe. -- Build a separate external deployment repo first. Rejected because this delays in-repo infrastructure ownership. - ## Rollout / Reversibility - Enabling the module is per-host. - Reversible by removing the module import and disabling the service(s); roll back with the existing deployment tooling. + diff --git a/evolution/proposals/ECP-0065-nixos-runner-images.md b/evolution/proposals/ECP-0065-nixos-runner-images.md index 43f223c..3899324 100644 --- a/evolution/proposals/ECP-0065-nixos-runner-images.md +++ b/evolution/proposals/ECP-0065-nixos-runner-images.md @@ -1,6 +1,6 @@ # ECP-0065: NixOS Runner Images + Netboot Artifacts -Status: Implemented +Status: Draft ## Decision @@ -40,11 +40,6 @@ Out of scope (defer): - Remote runtime provisioning (fetching per-node channel lists). - Hardware-accelerated transcode changes (keep current CPU x264 baseline). -## Alternatives considered - -- Keep runner images out-of-repo and publish ad hoc artifacts. Rejected because it weakens reproducibility and provenance. -- Restrict to one install path only (disk install only). Rejected because netboot/bootstrap is required for fleet recovery. - ## Rollout / Reversibility - Rollout begins with local builds and a single test machine. diff --git a/evolution/proposals/ECP-0066-iroh-control-protocol.md b/evolution/proposals/ECP-0066-iroh-control-protocol.md index f5edd22..359c0cd 100644 --- a/evolution/proposals/ECP-0066-iroh-control-protocol.md +++ b/evolution/proposals/ECP-0066-iroh-control-protocol.md @@ -1,6 +1,6 @@ # ECP-0066: iroh-Gossip Control Protocol For Hybrid MoQ Discovery -Status: Implemented +Status: Draft ## Decision @@ -39,11 +39,6 @@ Out of scope: - Security policy beyond existing iroh/gossip trust boundaries. - Replacing existing catalog gossip immediately (coexist first). -## Alternatives considered - -- Keep relay and direct discovery completely separate. Rejected because it forces duplicated consumer logic. -- Replace existing catalog gossip in one cutover. Rejected because additive coexistence is safer for rollout. - ## Rollout / Reversibility - Additive and reversible: removing control commands and topic does not affect existing media paths. diff --git a/evolution/proposals/ECP-0067-control-resolve-and-nixos-wiring.md b/evolution/proposals/ECP-0067-control-resolve-and-nixos-wiring.md index 21b9052..d15dfde 100644 --- a/evolution/proposals/ECP-0067-control-resolve-and-nixos-wiring.md +++ b/evolution/proposals/ECP-0067-control-resolve-and-nixos-wiring.md @@ -1,6 +1,6 @@ # ECP-0067: Control Transport Resolution And NixOS Control Wiring -Status: Implemented +Status: Draft ## Decision @@ -32,11 +32,6 @@ Out of scope: - End-to-end automatic failover execution (resolve + launch subscribe) in one command. - Cryptographic policy hardening beyond current control-topic trust model. -## Alternatives considered - -- Keep transport selection in ad hoc shell logic. Rejected because policy behavior becomes inconsistent across operators. -- Wire control flags per host manually. Rejected because it is error-prone and not declarative. - ## Rollout / Reversibility - Additive only: existing relay and direct publish/subscribe paths remain unchanged. diff --git a/evolution/proposals/ECP-0068-iroh-control-web-directory-bridge.md b/evolution/proposals/ECP-0068-iroh-control-web-directory-bridge.md index 87d0649..7157e92 100644 --- a/evolution/proposals/ECP-0068-iroh-control-web-directory-bridge.md +++ b/evolution/proposals/ECP-0068-iroh-control-web-directory-bridge.md @@ -1,6 +1,6 @@ # ECP-0068: Iroh Control To Web Directory Bridge -Status: Implemented +Status: Draft ## Decision @@ -34,11 +34,6 @@ Out of scope: - Signed/authenticated control announcements. - Replacing relay playback with direct iroh in browsers. -## Alternatives considered - -- Keep manual stream naming/link entry on the website. Rejected because it blocks one-click discovery. -- Bridge directly from browser clients instead of a node command. Rejected because browser trust/availability constraints are higher. - ## Rollout / Reversibility - Additive change; existing `/api/directory` and watch-by-link behavior remain intact. diff --git a/evolution/proposals/ECP-0069-nixos-control-bridge-autobootstrap.md b/evolution/proposals/ECP-0069-nixos-control-bridge-autobootstrap.md index bcec81d..0f4e220 100644 --- a/evolution/proposals/ECP-0069-nixos-control-bridge-autobootstrap.md +++ b/evolution/proposals/ECP-0069-nixos-control-bridge-autobootstrap.md @@ -1,6 +1,6 @@ # ECP-0069: NixOS Control Bridge Auto-Bootstrap -Status: Implemented +Status: Draft ## Decision @@ -31,11 +31,6 @@ Out of scope: - Signed control announcements. - Browser-native iroh direct transport playback. -## Alternatives considered - -- Continue manual gossip peer bootstrapping for the bridge. Rejected because restarts/reboots cause repeated operational toil. -- Use static peer lists only. Rejected because local publisher sets are dynamic and should be discovered from runtime endpoint files. - ## Rollout / Reversibility - Additive: existing publisher behavior is unchanged when `control.bridgeWeb.enable = false`. diff --git a/evolution/proposals/ECP-0070-relay-cas-archival.md b/evolution/proposals/ECP-0070-relay-cas-archival.md index 9487316..0a20108 100644 --- a/evolution/proposals/ECP-0070-relay-cas-archival.md +++ b/evolution/proposals/ECP-0070-relay-cas-archival.md @@ -1,7 +1,5 @@ # ECP-0070: Relay-Native CAS Archival + NixOS Auto-Archive Service -Status: Implemented - ## Summary Add a first-party archival path for MoQ relay streams: @@ -50,11 +48,6 @@ Tradeoffs: - Discovery source is the web public stream list (not full control-topic gossip ingestion). - Per-broadcast workers are process-based and best-effort supervised. -## Alternatives considered - -- Rely on browser-side replay caches only. Rejected because it does not provide durable archival storage. -- Archive only manifests without CAS payloads. Rejected because replay/integrity requires retained object bytes. - ## Rollout 1. Ship `wt-archive` command in `ec-node`. diff --git a/evolution/proposals/ECP-0071-archive-replay-dvr.md b/evolution/proposals/ECP-0071-archive-replay-dvr.md index aa902fc..872ad4b 100644 --- a/evolution/proposals/ECP-0071-archive-replay-dvr.md +++ b/evolution/proposals/ECP-0071-archive-replay-dvr.md @@ -1,7 +1,5 @@ # ECP-0071: Archive Replay DVR Endpoints -Status: Implemented - ## Context ECP-0070 added relay archival (`wt-archive`) into CAS objects plus JSONL indexes, but there is no read path for viewers to scrub historical content. @@ -28,16 +26,6 @@ Add an archive replay path with these pieces: - Preserves CAS as source of truth; playlists are derived views. - Uses standard HLS+DVR semantics so browser playback + scrubbing works without custom protocol work in the short term. -## Alternatives considered - -- Build a custom replay protocol/UI instead of HLS. Rejected because browser DVR support is stronger with standard HLS tooling. -- Serve archive from a separate domain only. Rejected because same-domain replay keeps watch links and CORS simpler. - -## Rollout / teardown - -- Enable archive serve mode on archive hosts and deploy worker proxy routing to `/api/archive/*`. -- Teardown by disabling `archive.serve.enable` and removing proxy routing. - ## Reversibility - Disable `archive.serve.enable` and remove worker proxy route to revert to archive-only mode. diff --git a/evolution/proposals/ECP-0072-cmaf-seedbox-invariant.md b/evolution/proposals/ECP-0072-cmaf-seedbox-invariant.md index 0a64487..d33a44d 100644 --- a/evolution/proposals/ECP-0072-cmaf-seedbox-invariant.md +++ b/evolution/proposals/ECP-0072-cmaf-seedbox-invariant.md @@ -1,7 +1,5 @@ # ECP-0072: CMAF Seedbox Invariant For Relay Archive -Status: Implemented - ## Context Archive replay currently stores and serves relay groups exactly as received, but many existing broadcasts were published in `legacy` container mode. Those bytes are not browser-HLS compatible, so archive playback fails despite a valid timeline and object store. @@ -22,16 +20,6 @@ Update the NixOS module default `services.every-channel.ec-node.passthrough = tr - Exact-byte retention avoids drift between live and replay. - Browsers can play CMAF fragments via standard HLS tooling; no custom legacy converter is required for new streams. -## Alternatives considered - -- Keep `passthrough=false` as default for all publishers. Rejected because archive replay needs byte-compatible CMAF fragments. -- Re-encode archived payloads during replay. Rejected because it adds complexity and breaks exact-byte history semantics. - -## Rollout / teardown - -- Flip default `passthrough` to true in CLI and Nix module, then verify new archives play via HLS. -- Teardown by explicitly setting `passthrough=false` on hosts needing legacy framing. - ## Reversibility - Operators can explicitly set `passthrough = false` per host to revert to legacy framing. diff --git a/evolution/proposals/ECP-0073-archive-relay-affinity-override.md b/evolution/proposals/ECP-0073-archive-relay-affinity-override.md index 50cbf86..d945432 100644 --- a/evolution/proposals/ECP-0073-archive-relay-affinity-override.md +++ b/evolution/proposals/ECP-0073-archive-relay-affinity-override.md @@ -1,7 +1,5 @@ # ECP-0073: Archive Relay Affinity Override -Status: Implemented - ## Context `wt-archive` workers discover streams from `/api/public-streams` and subscribe to the listed `relay_url`. In practice, `cdn.moq.dev` resolves to region-local relay IPs, and broadcasts published from one region are not consistently visible from another region endpoint. @@ -24,11 +22,6 @@ This allows operators to pin archive ingestion to the same relay endpoint used b - Keeps deployment-level control in Nix (no app-level migration needed). - Reversible with a single config change. -## Alternatives considered - -- Keep subscribing to directory-provided `relay_url` only. Rejected because cross-region visibility is inconsistent in practice. -- Rewrite directory entries per-region. Rejected because this mixes deployment affinity into public directory payloads. - ## Rollout 1. Set `archive.relayUrlOverride` on archive hosts that need relay affinity. diff --git a/evolution/proposals/ECP-0074-archive-hls-engine-selection.md b/evolution/proposals/ECP-0074-archive-hls-engine-selection.md index 382ddf8..0e334b7 100644 --- a/evolution/proposals/ECP-0074-archive-hls-engine-selection.md +++ b/evolution/proposals/ECP-0074-archive-hls-engine-selection.md @@ -1,7 +1,5 @@ # ECP-0074: Archive HLS Engine Selection For Chromium -Status: Implemented - ## Context Archive mode currently chooses native HLS whenever `video.canPlayType("application/vnd.apple.mpegurl")` is non-empty. @@ -18,16 +16,6 @@ Use native HLS only on Safari/iOS user agents. For all other browsers (including - Keeps Safari native path where it is reliable. - Preserves a single URL and UI flow (`/api/archive/.../master.m3u8`). -## Alternatives considered - -- Keep `canPlayType` as the only gate. Rejected because Chromium reports support but fails event-style playback. -- Force `hls.js` for all browsers including Safari. Rejected because Safari native playback is already reliable and simpler. - -## Rollout / teardown - -- Deploy UA-gated engine selection in web app and validate archive playback on Chromium and Safari. -- Teardown by reverting to the previous generic `canPlayType` gate. - ## Reversibility Revert the UA gate and return to the previous `canPlayType`-only check. diff --git a/evolution/proposals/ECP-0075-moq-watch-0.2.0-live-stability.md b/evolution/proposals/ECP-0075-moq-watch-0.2.0-live-stability.md index 8146efb..e2d4311 100644 --- a/evolution/proposals/ECP-0075-moq-watch-0.2.0-live-stability.md +++ b/evolution/proposals/ECP-0075-moq-watch-0.2.0-live-stability.md @@ -1,7 +1,5 @@ # ECP-0075: Bump Web Watcher To `@moq/watch@0.2.0` -Status: Implemented - ## Context Production web watchers currently load `@moq/watch@0.1.1`. Under live OTA relay streams, Chromium sessions frequently emit runtime failures (`VideoFrame clone` errors and repeated stream resets), leaving playback stalled even after successful subscribe. @@ -17,16 +15,6 @@ Set both `name` and `path` attributes on `` so minor-version attribut - Pulls in upstream runtime fixes without introducing new local playback logic. - Preserves multi-CDN fallback behavior already used for dependency resilience. -## Alternatives considered - -- Keep pin at `0.1.1` and add larger local workarounds. Rejected because upstream fixes already address core runtime failures. -- Switch to a different browser player stack immediately. Rejected because this is higher risk than a targeted minor-version bump. - -## Rollout / teardown - -- Roll out `@moq/watch@0.2.0` on all CDN import fallbacks and verify live subscribe/playback. -- Teardown by repinning imports to `0.1.1`. - ## Reversibility - Roll back by pinning imports back to `0.1.1` if regressions appear. diff --git a/evolution/proposals/ECP-0076-webtransport-only-web-watcher.md b/evolution/proposals/ECP-0076-webtransport-only-web-watcher.md index cb23aca..31e78bc 100644 --- a/evolution/proposals/ECP-0076-webtransport-only-web-watcher.md +++ b/evolution/proposals/ECP-0076-webtransport-only-web-watcher.md @@ -1,7 +1,5 @@ # ECP-0076: WebTransport-Only Browser Watcher Path -Status: Implemented - ## Context The browser watcher (`@moq/watch`) races WebTransport against WebSocket fallback by default. In production relay sessions this fallback path correlates with degraded playback behavior (frequent stream resets and unreliable audio despite active subscription). @@ -12,7 +10,7 @@ In `apps/web/app.js`, configure each `` instance to disable WebSocket - `watch.connection.websocket = { enabled: false }` -Also set default watcher volume to full (`volume="1"`). Keep canvas live rendering, and on mount force audio signals to `muted=false` and `volume=1`. +Also set default watcher volume to full (`volume="1"`) and mount live playback on a `