From c03f393c52c504591da74f7a66ff23b4b82097cf Mon Sep 17 00:00:00 2001 From: Guy Cochran Date: Wed, 7 Oct 2026 19:14:17 -0700 Subject: [PATCH] chore: relicense to Apache-2.0, adopt DCO, add TRADEMARKS + third-party notices - LICENSE: MIT -> Apache License 2.0 (verbatim text). New NOTICE carries the existing copyright line and notes that releases/tags published before the switch (v0.2.0, core-v1.0.0) remain available under MIT. - README license section, backend/package.json (+ lockfile root), companion module package.json + manifest.json -> Apache-2.0. - SPDX-License-Identifier: Apache-2.0 headers on py/js/sh/md sources (vendored code skipped; files under active edit in open PRs skipped). - DCO 1.1 (no CLA): CONTRIBUTING.md section + `git commit -s` guidance; scripts/check-dco.sh (dependency-free) + .github/workflows/dco.yml; tests. - CI: permissions: contents: read; actions pinned to commit SHAs. - TRADEMARKS.md: name/logo reserved, forks rebrand, nominative use allowed; recommendation that any protected brand be distinctive from "MXL". - THIRD-PARTY-NOTICES.md: ffmpeg.wasm wrapper/loader (MIT) and the fetched ffmpeg-core.wasm (GPL-2.0-or-later: --enable-gpl, x264/x265); fonts (OFL). fetch-core-wasm.sh now states the GPL status. Signed-off-by: Guy Cochran --- .github/workflows/ci.yml | 12 +- .github/workflows/dco.yml | 28 +++ CLAUDE.md | 1 + CONTRIBUTING.md | 72 ++++++ LICENSE | 223 ++++++++++++++++-- NOTICE | 10 + README.md | 12 +- REVIEW-HANDOFF.md | 1 + SECURITY.md | 1 + THIRD-PARTY-NOTICES.md | 83 +++++++ TRADEMARKS.md | 50 ++++ backend/facility.js | 1 + backend/ingest-info.js | 1 + backend/local-server.js | 1 + backend/mxl-auth.js | 1 + backend/mxl-routes-v1.js | 1 + backend/mxl-routes.js | 1 + backend/package-lock.json | 1 + backend/package.json | 1 + companion-module-mxl-switcher/README.md | 1 + .../TURNKEY-COMPANION.md | 1 + .../companion/manifest.json | 2 +- companion-module-mxl-switcher/main.js | 1 + companion-module-mxl-switcher/package.json | 2 +- contracts/v1/README.md | 1 + docs/ADOPTION-GAPS.md | 1 + docs/ARCHITECTURE-CORE-AND-SKINS.md | 1 + docs/AV-CONTRIBUTION-v0.3.md | 1 + docs/AWS-BUILD-PLAN.md | 1 + docs/CONTRIBUTION-SEAM.md | 1 + docs/CONTRIBUTION-SPLIT.md | 1 + docs/DESIRED-STATE-v0.md | 1 + docs/DOMAIN-STRATEGY.md | 1 + docs/FIELD-NOTES.md | 1 + docs/FINDINGS.md | 1 + docs/GCP-BUILD-PLAN.md | 1 + docs/GITHUB-ISSUES-DRAFTS.md | 1 + docs/GUEST-INGEST-HW-TEST.md | 1 + docs/IN-001-GAP-ANALYSIS.md | 1 + docs/IN-001-ISSUE-1-DRAFT.md | 1 + docs/IN-001-ISSUE-GAP-D-READY.md | 1 + docs/JONAS-FABRIC-HANDOFF.md | 1 + docs/LOCAL-CONTROL-PLANE.md | 1 + docs/ON-PREM-COTS.md | 1 + docs/QUICKSTART.md | 1 + docs/R1-STABILIZER-PLAN.md | 1 + docs/REDACTION-NOTE.md | 1 + docs/RESUME.md | 1 + docs/SITE-DEPLOYMENT.md | 1 + docs/TAMS.md | 1 + docs/TESTERS.md | 1 + docs/V1-CONTRACT.md | 1 + docs/ZOOMISO-BETA-RUNBOOK.md | 1 + docs/site.js | 1 + scripts/bring-up-mxl.sh | 1 + scripts/check-dco.sh | 46 ++++ scripts/doctor.sh | 1 + scripts/ensure-single-writers.sh | 1 + scripts/mxl-doctor | 1 + scripts/quickstart.sh | 1 + tests/README.md | 1 + tests/conftest.py | 1 + tests/js/mxl-auth.test.js | 1 + tests/js/mxl-ingest-transport.test.js | 1 + tests/js/mxl-routes-v1.test.js | 1 + tests/js/mxl-routes.test.js | 1 + tests/test_check_dco.py | 78 ++++++ tests/test_desired_state_report.py | 1 + tests/test_docs_links.py | 1 + tests/test_facility_from_discovery.py | 1 + tests/test_facility_manifest.py | 1 + tests/test_guest_slots.py | 1 + tests/test_js_backend.py | 1 + tests/test_launch_parity.py | 1 + tests/test_local_ui.py | 1 + tests/test_mxl_doctor.py | 1 + tests/test_no_hardcoded_hosts.py | 1 + tests/test_no_secrets.py | 1 + tests/test_participant.py | 1 + tests/test_quickstart_hardening.py | 1 + tests/test_selfheal.py | 1 + tools/audio_pgm.py | 1 + tools/backfill-mini.py | 1 + tools/cam2_ingest.py | 1 + tools/cam_ingest.py | 1 + tools/cam_relay.py | 1 + tools/desired_state_report.py | 1 + tools/facility.py | 1 + tools/facility_from_discovery.py | 1 + tools/flow_stabilizer.py | 1 + tools/grain_probe.py | 1 + tools/guest-leg-doctor.sh | 1 + tools/guest_audio.py | 1 + tools/guest_av_listen.sh | 1 + tools/guest_ingest.py | 1 + tools/guest_slot_watcher.py | 1 + tools/layout_pgm.py | 1 + tools/mv_encode.py | 1 + tools/mxl-egress.sh | 1 + tools/mxl-selfheal.sh | 1 + tools/mxl_multiview.py | 1 + tools/mxl_thumbs.py | 1 + tools/participant.py | 1 + tools/patch-target-ip.py | 1 + tools/pgm_lite.py | 1 + tools/program-fps-doctor.sh | 1 + tools/selector-doctor.sh | 1 + tools/start-jonas-leg.sh | 1 + tools/tams_shipper.py | 1 + tools/viewer_count.py | 1 + tools/zoomiso_dryrun.py | 1 + web/vendor/ffmpeg/fetch-core-wasm.sh | 4 + 112 files changed, 693 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/dco.yml create mode 100644 NOTICE create mode 100644 THIRD-PARTY-NOTICES.md create mode 100644 TRADEMARKS.md create mode 100755 scripts/check-dco.sh create mode 100644 tests/test_check_dco.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6222fd5..54ba604 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 name: CI # Cheap, fast, no-hardware checks that protect the hard-won media core from @@ -14,14 +15,17 @@ on: pull_request: workflow_dispatch: +permissions: + contents: read + jobs: lint-and-test: name: syntax • seam tests • docs links runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "3.12" @@ -46,7 +50,7 @@ jobs: sudo apt-get update -qq && sudo apt-get install -y -qq shellcheck find . -path ./.git -prune -o -name '*.sh' -print -exec shellcheck -S warning {} + || true - - uses: actions/setup-node@v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: "20" @@ -74,7 +78,7 @@ jobs: if: github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Smoke-test placeholder run: | echo "This job is a placeholder for the real quickstart smoke test." diff --git a/.github/workflows/dco.yml b/.github/workflows/dco.yml new file mode 100644 index 0000000..e3e9d29 --- /dev/null +++ b/.github/workflows/dco.yml @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: Apache-2.0 +name: DCO + +# Every commit in a PR must carry a Signed-off-by trailer matching its author +# (Developer Certificate of Origin 1.1 — see CONTRIBUTING.md). No CLA. +# Lightweight on purpose: no third-party action, just git + scripts/check-dco.sh. + +on: + pull_request: + +permissions: + contents: read + +jobs: + dco: + name: DCO sign-off + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Check Signed-off-by on every PR commit + env: + BASE_REF: ${{ github.base_ref }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: bash scripts/check-dco.sh "origin/$BASE_REF" "$HEAD_SHA" diff --git a/CLAUDE.md b/CLAUDE.md index 027f50f..97a8a94 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,3 +1,4 @@ + # CLAUDE.md — mxl-switcher Guidance for Claude Code working in this repo. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6962ea9..6a672af 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,4 @@ + # Contributing This is an **open DMF/MXL interoperability lab**. The most valuable thing you can do @@ -49,3 +50,74 @@ writers, multiview, and TAMS clipper are all here and buildable. Open a [Discussion](https://github.com/guycochran/mxl-switcher/discussions) for "how would I…" / "does MXL do…", and an Issue for a concrete bug or gap. + +## License of contributions + +This project is licensed under the [Apache License 2.0](LICENSE). By contributing, you +agree that your contribution is licensed under Apache-2.0 (Apache-2.0 §5). There is **no +CLA** — instead every commit carries a Developer Certificate of Origin sign-off. + +## Developer Certificate of Origin (DCO) + +Every commit in a pull request must be **signed off**, certifying the DCO 1.1 below. Add +the sign-off with `-s`: + +```bash +git commit -s -m "fix: describe the change" +``` + +That appends a trailer using your `git config user.name` / `user.email`: + +``` +Signed-off-by: Your Name +``` + +The sign-off email must match the commit's author email. CI runs a DCO check on every PR +(`scripts/check-dco.sh`, workflow `.github/workflows/dco.yml`). Forgot it? Fix the +branch and force-push it: + +```bash +git commit --amend -s --no-edit # last commit only +git rebase --signoff origin/master # every commit on the branch +git push --force-with-lease +``` + +Commits written with an AI assistant are fine; the human who submits them signs off and +takes responsibility for them under the DCO, the same as for any other commit. + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` diff --git a/LICENSE b/LICENSE index 99e98bc..d645695 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,202 @@ -MIT License - -Copyright (c) 2026 Guy Cochran / Office Hours Global - -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. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..89d7d99 --- /dev/null +++ b/NOTICE @@ -0,0 +1,10 @@ +mxl-switcher +Copyright 2026 Guy Cochran / Office Hours Global + +This product is licensed under the Apache License, Version 2.0 (see LICENSE). + +Releases and tags published before the change to Apache-2.0 (including v0.2.0 and +core-v1.0.0) were distributed under the MIT License and remain available under it. + +This repository includes third-party components under their own licenses; see +THIRD-PARTY-NOTICES.md. diff --git a/README.md b/README.md index 115be57..972ac10 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ + # MXL Switcher **An open DMF/MXL production lab — clone it, run one script, and put your own phone @@ -511,4 +512,13 @@ The interesting engineering is in **[docs/FINDINGS.md](docs/FINDINGS.md)** — i - [CLOUDflex-broadcast/easy-mxl](https://github.com/CLOUDflex-broadcast/easy-mxl) — the control panel that makes the domain approachable. -MIT licensed. Not affiliated with EBU or CBC; all findings offered upstream with love. +## License + +Licensed under the [Apache License, Version 2.0](LICENSE) — see also [NOTICE](NOTICE) and +[THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md) for bundled components. Releases and tags +published before the switch (including `v0.2.0` and `core-v1.0.0`) were released under the +MIT License and remain available under MIT. Contributions are accepted under the +[Developer Certificate of Origin](CONTRIBUTING.md#developer-certificate-of-origin-dco) +(`git commit -s`). Project name and logo: see [TRADEMARKS.md](TRADEMARKS.md). + +Not affiliated with EBU or CBC; all findings offered upstream with love. diff --git a/REVIEW-HANDOFF.md b/REVIEW-HANDOFF.md index 151b2fa..c5e6ddd 100644 --- a/REVIEW-HANDOFF.md +++ b/REVIEW-HANDOFF.md @@ -1,3 +1,4 @@ + # MXL Switcher — Review Handoff **For an independent reviewer.** This documents what was built, deployed, and claimed, diff --git a/SECURITY.md b/SECURITY.md index 17772b9..2f9eff6 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,3 +1,4 @@ + # Security Policy This is a **reference/lab project**, not a hardened production service. It is meant to diff --git a/THIRD-PARTY-NOTICES.md b/THIRD-PARTY-NOTICES.md new file mode 100644 index 0000000..a5228ab --- /dev/null +++ b/THIRD-PARTY-NOTICES.md @@ -0,0 +1,83 @@ + +# Third-party notices + +The project's own code is licensed under Apache-2.0 (see [LICENSE](LICENSE)). The +components below are bundled in, or fetched by, this repository and remain under their +own licenses. + +## ffmpeg.wasm — `web/vendor/ffmpeg/` + +Used by the in-browser clip/record pages (`web/mxl-clip.html`, `web/mxl-tams.html`). + +| File | Upstream package | Committed? | License | +|---|---|---|---| +| `ffmpeg.js`, `814.ffmpeg.js` | `@ffmpeg/ffmpeg` **0.12.10** (byte-identical to the npm `dist/umd` files) | yes | MIT | +| `ffmpeg-core.js` | `@ffmpeg/core` **0.12.6** Emscripten loader (byte-identical to npm `dist/umd/ffmpeg-core.js`) | yes | MIT (per the npm package metadata) | +| `ffmpeg-core.wasm` | `@ffmpeg/core` **0.12.6** compiled FFmpeg | **no** — fetched by `fetch-core-wasm.sh`, git-ignored | **GPL-2.0-or-later** (see below) | + +Upstream: . MIT License text for the +ffmpeg.wasm JavaScript: + +``` +MIT License + +Copyright (c) 2019 Jerome Wu + +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. +``` + +### The core WebAssembly binary is GPL + +Although the npm package metadata for `@ffmpeg/core` says "MIT", the compiled +`ffmpeg-core.wasm` it ships is FFmpeg **5.1** (libavcodec 59.37) configured with `--enable-gpl` and linked +with GPL libraries (libx264, libx265), plus libvpx, LAME, Theora, Vorbis, Opus, zlib, +libwebp, FreeType, FriBidi, libass and zimg. FFmpeg's own build configuration embedded in +the binary reads (excerpt): + +``` +--enable-gpl --enable-libx264 --enable-libx265 --enable-libvpx --enable-libmp3lame +--enable-libtheora --enable-libvorbis --enable-libopus --enable-zlib --enable-libwebp +--enable-libfreetype --enable-libfribidi --enable-libass --enable-libzimg +``` + +So the **combined binary is licensed GPL-2.0-or-later** (FFmpeg's `--enable-gpl` mode, +x264 and x265 are GPL-2.0-or-later). + +What that means here: + +- The `.wasm` is **not committed** to this repository; `fetch-core-wasm.sh` downloads the + exact upstream build on demand. No GPL object code is distributed in this Git tree. +- **If you deploy the clip/record pages, your web server distributes that GPL binary to + browsers.** You are then responsible for GPL compliance for it (for example, pointing + users to the corresponding source: the ffmpeg.wasm `v0.12.6` core build scripts at + , FFmpeg 5.1, x264 and x265 sources). +- If GPL obligations aren't acceptable for your deployment, build an LGPL-only core + (no `--enable-gpl`, no x264/x265) and serve that instead, or don't deploy those pages. + +## Web fonts — `web/fonts/` + +Barlow Condensed and IBM Plex Mono, under the SIL Open Font License 1.1. See +[`web/fonts/LICENSE`](web/fonts/LICENSE). + +## Runtime dependencies (not bundled) + +- `backend/` installs `express` and its dependencies from npm (`backend/package-lock.json`), + each under its own license (MIT for express). +- The quickstart pulls container images (e.g. `ghcr.io/cbcrc/*`, `bluenviron/mediamtx`) at + run time; they carry their own licenses and are not redistributed by this repository. diff --git a/TRADEMARKS.md b/TRADEMARKS.md new file mode 100644 index 0000000..1ba7322 --- /dev/null +++ b/TRADEMARKS.md @@ -0,0 +1,50 @@ + +# Trademarks + +The code in this repository is open source under the [Apache License 2.0](LICENSE). The +Apache license grants rights to the **code**; it does **not** grant rights to use the +project's name or logo (Apache-2.0 §6). This file explains what is and isn't fine. + +## What is reserved + +- The project name **"mxl-switcher"** / **"MXL Switcher"** as the name of a product, + service, distribution, or fork. +- The project's logo and visual identity, wherever they appear in this repository or on + the project's website. + +## What you can do without asking + +- **Use, modify, and redistribute the code** under Apache-2.0. That's the point. +- **Nominative use** — truthfully refer to the project to describe what your thing works + with or is built on, for example: + - "compatible with mxl-switcher" + - "a skin for mxl-switcher" / "built on the mxl-switcher core" + - "uses the mxl-switcher `/api/mxl/v1` API" + - "for MXL" (describing the media exchange layer your product interoperates with) +- Keep the existing copyright, license, and NOTICE text in redistributed copies (required + by the license anyway). + +## Forks and redistributions + +If you publish a fork, a modified build, or a hosted service based on this code, **give it +your own name and logo**. Don't present it as the official mxl-switcher, and don't use +the name or logo in a way that suggests endorsement or affiliation. A short line such as +"based on mxl-switcher" in your README or About page is welcome. + +## A note on the "MXL" name + +"MXL" appears to be the name of the open media exchange layer project developed in the +EBU / Linux Foundation community (the [MXL SDK](https://github.com/dmf-mxl/dmf-mxl)). We +have not confirmed who holds any rights in that name. This project is an independent +implementation and is **not affiliated with or endorsed by** the EBU, the Linux +Foundation, or the MXL project. + +**Recommendation:** because "MXL" is someone else's project name, any brand that is +meant to be protected (including this project's own product/brand name, if one is +adopted later) should be **distinctive** rather than built mainly on "MXL". Descriptive +use of "MXL" to say what something works with ("for MXL", "MXL-native") is fine. This is +a recommendation only; nothing is being renamed here. + +## Questions + +Unsure whether a use is okay? Open a GitHub issue and ask before you ship. diff --git a/backend/facility.js b/backend/facility.js index db8b265..7116d4e 100644 --- a/backend/facility.js +++ b/backend/facility.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 // Load the facility manifest (config/facility.json) — the single source of truth // for domain, network, control ports, and flow UUIDs. JS counterpart to // tools/facility.py so the backend and the Python tools read the SAME file. diff --git a/backend/ingest-info.js b/backend/ingest-info.js index 8f12b01..27efb6a 100644 --- a/backend/ingest-info.js +++ b/backend/ingest-info.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 // Pure, dependency-free builders for the "Add your camera" guest-ingest info: // the per-guest SRT URL, streamid, and Larix deep-link. Extracted from // local-server.js so it can be unit-tested WITHOUT express / npm deps (CI runs diff --git a/backend/local-server.js b/backend/local-server.js index 5dea2c4..2b033bc 100644 --- a/backend/local-server.js +++ b/backend/local-server.js @@ -1,4 +1,5 @@ #!/usr/bin/env node +// SPDX-License-Identifier: Apache-2.0 // local-server.js — the self-contained control plane for an MXL quickstart box. // // Mounts the portable mxl-routes.js (cut / key / pattern / status / repair) and diff --git a/backend/mxl-auth.js b/backend/mxl-auth.js index 381ba70..2793174 100644 --- a/backend/mxl-auth.js +++ b/backend/mxl-auth.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 // Optional shared-token auth + a tiny in-memory rate limiter for the MXL control // routes (backend/mxl-routes.js). Dependency-free; plain Express-style // (req, res, next) middleware, so it also works with connect/restify/etc. diff --git a/backend/mxl-routes-v1.js b/backend/mxl-routes-v1.js index f7e3471..b3523c8 100644 --- a/backend/mxl-routes-v1.js +++ b/backend/mxl-routes-v1.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 // /api/mxl/v1 — the versioned contract (contracts/v1/*, docs/V1-CONTRACT.md). // // Thin wrappers over the existing handlers in mxl-routes.js. The media behaviour is diff --git a/backend/mxl-routes.js b/backend/mxl-routes.js index 7e1af71..2f14d3c 100644 --- a/backend/mxl-routes.js +++ b/backend/mxl-routes.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 // MXL demo control routes — mount into any Express app that can reach the MXL VM. // The kiosk page (web/mxl.html) calls these; the browser can't reach the VM // directly (NSG-locked), so this proxies from a host whose IP is allowed. diff --git a/backend/package-lock.json b/backend/package-lock.json index 8f8d1ac..ca8c6f3 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "mxl-local-control", "version": "0.3.0", + "license": "Apache-2.0", "dependencies": { "express": "^4.19.2" }, diff --git a/backend/package.json b/backend/package.json index 0a5ea23..d017c74 100644 --- a/backend/package.json +++ b/backend/package.json @@ -2,6 +2,7 @@ "name": "mxl-local-control", "version": "0.3.0", "private": true, + "license": "Apache-2.0", "description": "Self-contained control plane for an MXL quickstart box: mounts the portable mxl-routes and serves the web/local.html switcher UI.", "main": "local-server.js", "scripts": { diff --git a/companion-module-mxl-switcher/README.md b/companion-module-mxl-switcher/README.md index 9acb1ee..9202a77 100644 --- a/companion-module-mxl-switcher/README.md +++ b/companion-module-mxl-switcher/README.md @@ -1,3 +1,4 @@ + # companion-module-mxl-switcher Bitfocus Companion module for the MXL cloud switcher in this repo: diff --git a/companion-module-mxl-switcher/TURNKEY-COMPANION.md b/companion-module-mxl-switcher/TURNKEY-COMPANION.md index d616476..a1d7272 100644 --- a/companion-module-mxl-switcher/TURNKEY-COMPANION.md +++ b/companion-module-mxl-switcher/TURNKEY-COMPANION.md @@ -1,3 +1,4 @@ + # Turnkey Companion setup (Windows PC + Stream Deck) Two ways to drive the MXL switcher from a Stream Deck. Pick by whether diff --git a/companion-module-mxl-switcher/companion/manifest.json b/companion-module-mxl-switcher/companion/manifest.json index 8fc053a..4495c27 100644 --- a/companion-module-mxl-switcher/companion/manifest.json +++ b/companion-module-mxl-switcher/companion/manifest.json @@ -4,7 +4,7 @@ "shortname": "mxl", "description": "MXL switcher (github.com/guycochran/mxl-switcher): cuts, SuperSource layouts, keyer, warm-up, PGM tally", "version": "0.1.0", - "license": "MIT", + "license": "Apache-2.0", "repository": "https://github.com/guycochran/mxl-switcher", "bugs": "https://github.com/guycochran/mxl-switcher/issues", "maintainers": [{ "name": "Guy Cochran" }], diff --git a/companion-module-mxl-switcher/main.js b/companion-module-mxl-switcher/main.js index d524994..fbe8bd0 100644 --- a/companion-module-mxl-switcher/main.js +++ b/companion-module-mxl-switcher/main.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 // companion-module-mxl-switcher — Bitfocus Companion module for the MXL // cloud switcher (this repo). Physical Stream Deck buttons with REAL // program tally: buttons glow red when their source is on air, dim when diff --git a/companion-module-mxl-switcher/package.json b/companion-module-mxl-switcher/package.json index a6044d3..2ad05e0 100644 --- a/companion-module-mxl-switcher/package.json +++ b/companion-module-mxl-switcher/package.json @@ -2,7 +2,7 @@ "name": "companion-module-mxl-switcher", "version": "0.1.0", "main": "main.js", - "license": "MIT", + "license": "Apache-2.0", "description": "Bitfocus Companion module for the MXL cloud production switcher — cuts, SuperSource, keyer, warm-up, with live PGM tally feedback", "dependencies": { "@companion-module/base": "~1.10.0" diff --git a/contracts/v1/README.md b/contracts/v1/README.md index 560bffc..dce1bbb 100644 --- a/contracts/v1/README.md +++ b/contracts/v1/README.md @@ -1,3 +1,4 @@ + # `contracts/v1/` — the MXL switcher v1 API contract (SKETCH) JSON Schemas are the **source of truth** for the `/api/mxl/v1` surface. The design diff --git a/docs/ADOPTION-GAPS.md b/docs/ADOPTION-GAPS.md index e60d323..1152f87 100644 --- a/docs/ADOPTION-GAPS.md +++ b/docs/ADOPTION-GAPS.md @@ -1,3 +1,4 @@ + # Adoption Gaps — cold-clone findings + roadmap **What this is:** the output of an *adoptability* test — can someone who isn't Guy clone this diff --git a/docs/ARCHITECTURE-CORE-AND-SKINS.md b/docs/ARCHITECTURE-CORE-AND-SKINS.md index c8b0b8a..dd4f32c 100644 --- a/docs/ARCHITECTURE-CORE-AND-SKINS.md +++ b/docs/ARCHITECTURE-CORE-AND-SKINS.md @@ -1,3 +1,4 @@ + # Architecture note: core ("bones") and skins **Status:** DRAFT proposal for discussion — nothing here is implemented yet. Written against `master` @ `ab44ddc` plus the open drafts it refers to (#17, #19, #20, #23, and the two UI mockups in #18 / #21). diff --git a/docs/AV-CONTRIBUTION-v0.3.md b/docs/AV-CONTRIBUTION-v0.3.md index b2dcd56..223dc6c 100644 --- a/docs/AV-CONTRIBUTION-v0.3.md +++ b/docs/AV-CONTRIBUTION-v0.3.md @@ -1,3 +1,4 @@ + # A/V Contribution — v0.3 **Status: IMPLEMENTED / hardware validation in progress.** Capability state: diff --git a/docs/AWS-BUILD-PLAN.md b/docs/AWS-BUILD-PLAN.md index 0e693de..b184ebf 100644 --- a/docs/AWS-BUILD-PLAN.md +++ b/docs/AWS-BUILD-PLAN.md @@ -1,3 +1,4 @@ + # Building the MXL Cloud Production Demo on AWS **Goal:** run the facility we proved on Azure for IBC 2026 — uncompressed diff --git a/docs/CONTRIBUTION-SEAM.md b/docs/CONTRIBUTION-SEAM.md index f3afa4b..8f78f5c 100644 --- a/docs/CONTRIBUTION-SEAM.md +++ b/docs/CONTRIBUTION-SEAM.md @@ -1,3 +1,4 @@ + # MXL Contribution Seam — spec (ZoomISO-ready, prove with SRT today) **Status:** IMPLEMENTED 2026-10-02 (commit `0e98a2c`, refined same day). The core + the diff --git a/docs/CONTRIBUTION-SPLIT.md b/docs/CONTRIBUTION-SPLIT.md index bf6b0b7..eaba2b9 100644 --- a/docs/CONTRIBUTION-SPLIT.md +++ b/docs/CONTRIBUTION-SPLIT.md @@ -1,3 +1,4 @@ + # Contribution ‖ Mixer: cameras, and isolating the attack surface **The question this answers:** how does a new user get *their* cameras in (SRT or MXL), diff --git a/docs/DESIRED-STATE-v0.md b/docs/DESIRED-STATE-v0.md index 5432581..7226e8a 100644 --- a/docs/DESIRED-STATE-v0.md +++ b/docs/DESIRED-STATE-v0.md @@ -1,3 +1,4 @@ + # Desired-State Controller v0.1 — design sketch (report-only) **Status:** DRAFT design / sketch. Not built against a live facility. diff --git a/docs/DOMAIN-STRATEGY.md b/docs/DOMAIN-STRATEGY.md index 014be59..605b193 100644 --- a/docs/DOMAIN-STRATEGY.md +++ b/docs/DOMAIN-STRATEGY.md @@ -1,3 +1,4 @@ + # Domain and identity strategy ## Recommendation diff --git a/docs/FIELD-NOTES.md b/docs/FIELD-NOTES.md index 96907b3..0ac1217 100644 --- a/docs/FIELD-NOTES.md +++ b/docs/FIELD-NOTES.md @@ -1,3 +1,4 @@ + # Field Notes: what an MXL v1.1.0 production deployment actually takes This facility ran a public, operator-driven cloud switcher on MXL for IBC diff --git a/docs/FINDINGS.md b/docs/FINDINGS.md index c13fe78..092c115 100644 --- a/docs/FINDINGS.md +++ b/docs/FINDINGS.md @@ -1,3 +1,4 @@ + # Field Findings — running MXL as a real remote production Notes from building this demo (September 2026, MXL SDK v1.1 era, stock diff --git a/docs/GCP-BUILD-PLAN.md b/docs/GCP-BUILD-PLAN.md index 5d4a415..6a5fcc9 100644 --- a/docs/GCP-BUILD-PLAN.md +++ b/docs/GCP-BUILD-PLAN.md @@ -1,3 +1,4 @@ + # Building the MXL Cloud Production Demo on GCP > **✅ VERIFIED 2026‑09‑15.** The `scripts/quickstart.sh` path ran clean end‑to‑end diff --git a/docs/GITHUB-ISSUES-DRAFTS.md b/docs/GITHUB-ISSUES-DRAFTS.md index 4adf3d9..352bb80 100644 --- a/docs/GITHUB-ISSUES-DRAFTS.md +++ b/docs/GITHUB-ISSUES-DRAFTS.md @@ -1,3 +1,4 @@ + # Starter GitHub issues — drafts Deliberately approachable entry points so a prospective contributor has somewhere to diff --git a/docs/GUEST-INGEST-HW-TEST.md b/docs/GUEST-INGEST-HW-TEST.md index a2474ab..708a0a9 100644 --- a/docs/GUEST-INGEST-HW-TEST.md +++ b/docs/GUEST-INGEST-HW-TEST.md @@ -1,3 +1,4 @@ + # Guest-ingest: what to test when the VMs come back up > **HW RUN — Oct 6 2026 (cold-clone on mxl-lab):** diff --git a/docs/IN-001-GAP-ANALYSIS.md b/docs/IN-001-GAP-ANALYSIS.md index 030e0a2..c44c10e 100644 --- a/docs/IN-001-GAP-ANALYSIS.md +++ b/docs/IN-001-GAP-ANALYSIS.md @@ -1,3 +1,4 @@ + # AMWA IN-001 Gap Analysis — field data from a live MXL switcher **Source spec:** [AMWA-TV/in-001](https://github.com/AMWA-TV/in-001) — *API Requirements – Control of the Media eXchange Layer (MXL) v1.0* (Draft Increment, public review open). diff --git a/docs/IN-001-ISSUE-1-DRAFT.md b/docs/IN-001-ISSUE-1-DRAFT.md index b7f235e..ac6a979 100644 --- a/docs/IN-001-ISSUE-1-DRAFT.md +++ b/docs/IN-001-ISSUE-1-DRAFT.md @@ -1,3 +1,4 @@ + # Draft GitHub Issue for AMWA-TV/in-001 — review before posting **Where to post:** https://github.com/AMWA-TV/in-001/issues → "New issue" diff --git a/docs/IN-001-ISSUE-GAP-D-READY.md b/docs/IN-001-ISSUE-GAP-D-READY.md index 24dcadb..1d81235 100644 --- a/docs/IN-001-ISSUE-GAP-D-READY.md +++ b/docs/IN-001-ISSUE-GAP-D-READY.md @@ -1,3 +1,4 @@ + # IN-001 §6.2 issue — READY TO POST (Gap D: liveness) > ✅ **POSTED Oct 1 2026 — AMWA-TV/in-001 issue #6: https://github.com/AMWA-TV/in-001/issues/6** diff --git a/docs/JONAS-FABRIC-HANDOFF.md b/docs/JONAS-FABRIC-HANDOFF.md index 526b28c..f189c72 100644 --- a/docs/JONAS-FABRIC-HANDOFF.md +++ b/docs/JONAS-FABRIC-HANDOFF.md @@ -1,3 +1,4 @@ + # Receive our live program as raw MXL grains — fabric handoff **The offer:** your easy-mxl is already orchestrating our whole production diff --git a/docs/LOCAL-CONTROL-PLANE.md b/docs/LOCAL-CONTROL-PLANE.md index 9ba0ee3..a6419f4 100644 --- a/docs/LOCAL-CONTROL-PLANE.md +++ b/docs/LOCAL-CONTROL-PLANE.md @@ -1,3 +1,4 @@ + # Local control plane — scope **Status: BUILT (v1 — news-grade switcher).** A self-contained, broadcast-grade diff --git a/docs/ON-PREM-COTS.md b/docs/ON-PREM-COTS.md index e3f0a4d..207a706 100644 --- a/docs/ON-PREM-COTS.md +++ b/docs/ON-PREM-COTS.md @@ -1,3 +1,4 @@ + # On-prem / owned hardware: what does this cost to *buy*? **The demo runs in the cloud, but nothing about MXL requires the cloud.** The diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index f659fe5..18f8d72 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -1,3 +1,4 @@ + # Quickstart: your own MXL switcher in minutes *Cold-clone verified Oct 2026 on fresh VMs across three clouds — git clone → diff --git a/docs/R1-STABILIZER-PLAN.md b/docs/R1-STABILIZER-PLAN.md index 3fdf958..8c76509 100644 --- a/docs/R1-STABILIZER-PLAN.md +++ b/docs/R1-STABILIZER-PLAN.md @@ -1,3 +1,4 @@ + # R1 — eliminate the cold-reader wedge with stabilizers (design + PoC) **Status: mechanism PROVEN on hardware (Oct 4 2026); full selector integration is diff --git a/docs/REDACTION-NOTE.md b/docs/REDACTION-NOTE.md index f7203d4..a3082a4 100644 --- a/docs/REDACTION-NOTE.md +++ b/docs/REDACTION-NOTE.md @@ -1,3 +1,4 @@ + # Facility-identifier redaction — note to the reviewer **Re:** review items **R6d** and **R6e** in `docs/REVIEW-RESPONSE.md`. diff --git a/docs/RESUME.md b/docs/RESUME.md index 73a260c..dc121ec 100644 --- a/docs/RESUME.md +++ b/docs/RESUME.md @@ -1,3 +1,4 @@ + # Resume / rebuild after teardown The IBC-week VMs were deallocated. Everything needed to stand the facility diff --git a/docs/SITE-DEPLOYMENT.md b/docs/SITE-DEPLOYMENT.md index 71e1663..c0b0857 100644 --- a/docs/SITE-DEPLOYMENT.md +++ b/docs/SITE-DEPLOYMENT.md @@ -1,3 +1,4 @@ + # Informational site deployment The informational site is deliberately static and lives in `docs/`: diff --git a/docs/TAMS.md b/docs/TAMS.md index fa1474b..e7c134c 100644 --- a/docs/TAMS.md +++ b/docs/TAMS.md @@ -1,3 +1,4 @@ + # MXL → TAMS: the full recipe The DMF white paper notes *"MXL Grains can be grouped as TAMS Flow Segments"* diff --git a/docs/TESTERS.md b/docs/TESTERS.md index d5a6711..7e2dc1a 100644 --- a/docs/TESTERS.md +++ b/docs/TESTERS.md @@ -1,3 +1,4 @@ + # For testers — fire it up in ~3 minutes Hand-this-to-a-tester guide. Full detail is in [QUICKSTART.md](QUICKSTART.md); diff --git a/docs/V1-CONTRACT.md b/docs/V1-CONTRACT.md index cfdd27d..f0f585d 100644 --- a/docs/V1-CONTRACT.md +++ b/docs/V1-CONTRACT.md @@ -1,3 +1,4 @@ + # The `/api/mxl/v1` contract (minimal first slice — SKETCH) **Status:** DRAFT sketch for discussion. Schemas live in `contracts/v1/`. No handlers diff --git a/docs/ZOOMISO-BETA-RUNBOOK.md b/docs/ZOOMISO-BETA-RUNBOOK.md index 33ac4aa..42b33c0 100644 --- a/docs/ZOOMISO-BETA-RUNBOOK.md +++ b/docs/ZOOMISO-BETA-RUNBOOK.md @@ -1,3 +1,4 @@ + # ZoomISO Cloud — first 30 minutes with the beta (measure → wire → cut) **Goal:** when the ZoomISO Cloud beta lands, go from "we have access" to "Zoom participants diff --git a/docs/site.js b/docs/site.js index 9f5a95e..b626541 100644 --- a/docs/site.js +++ b/docs/site.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 (() => { const descriptions = { live: "A seven-input selector, live layout compositor, program audio mixer and HTML5 keyer operate against flows in a single shared-memory domain.", diff --git a/scripts/bring-up-mxl.sh b/scripts/bring-up-mxl.sh index ced9cb3..0fdea14 100755 --- a/scripts/bring-up-mxl.sh +++ b/scripts/bring-up-mxl.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# SPDX-License-Identifier: Apache-2.0 # bring-up-mxl.sh — one-command restore of the full MXL IBC demo # Restores: Azure VM containers → pipelines (exact live config captured 2026-09-09) # → cam push → cloudflared feed tunnel → mxl.html iframe URL → verify. diff --git a/scripts/check-dco.sh b/scripts/check-dco.sh new file mode 100755 index 0000000..b3706bf --- /dev/null +++ b/scripts/check-dco.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: Apache-2.0 +# DCO check: every non-merge commit in BASE..HEAD must carry a +# Signed-off-by: Name +# trailer whose email matches the commit's author email (case-insensitive). +# See CONTRIBUTING.md ("Developer Certificate of Origin"). +# +# Usage: scripts/check-dco.sh [] (head defaults to HEAD) +# Exit 0 = all signed off; 1 = at least one commit is missing a matching sign-off; +# 2 = usage / git error. Dependency-free (bash + git). +set -euo pipefail + +BASE="${1:-}"; HEAD_REF="${2:-HEAD}" +if [ -z "$BASE" ]; then + echo "usage: $0 []" >&2; exit 2 +fi +git rev-parse --verify --quiet "$BASE^{commit}" >/dev/null || { echo "unknown base ref: $BASE" >&2; exit 2; } +git rev-parse --verify --quiet "$HEAD_REF^{commit}" >/dev/null || { echo "unknown head ref: $HEAD_REF" >&2; exit 2; } + +fail=0; checked=0 +for c in $(git rev-list --no-merges "$BASE..$HEAD_REF"); do + checked=$((checked + 1)) + author_email=$(git log -1 --format='%ae' "$c" | tr '[:upper:]' '[:lower:]') + subject=$(git log -1 --format='%s' "$c") + # all Signed-off-by trailers, emails only, lower-cased + signoffs=$(git log -1 --format='%(trailers:key=Signed-off-by,valueonly)' "$c" \ + | sed -n 's/.*<\([^>]*\)>.*/\1/p' | tr '[:upper:]' '[:lower:]') + if [ -z "$signoffs" ]; then + echo "✗ ${c:0:10} missing Signed-off-by — \"$subject\""; fail=1 + elif ! printf '%s\n' "$signoffs" | grep -qxF "$author_email"; then + echo "✗ ${c:0:10} Signed-off-by does not match author <$author_email> — \"$subject\""; fail=1 + else + echo "✓ ${c:0:10} $subject" + fi +done + +if [ "$fail" -ne 0 ]; then + cat <<'MSG' + +DCO check failed. Sign off your commits (see CONTRIBUTING.md): + git rebase --signoff # adds Signed-off-by to every commit on the branch + git push --force-with-lease +MSG + exit 1 +fi +echo "DCO: $checked commit(s) checked, all signed off." diff --git a/scripts/doctor.sh b/scripts/doctor.sh index 1e25fcf..d94510b 100755 --- a/scripts/doctor.sh +++ b/scripts/doctor.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# SPDX-License-Identifier: Apache-2.0 # doctor.sh — one-glance health check for an MXL switcher lab. # # Answers the question every MXL experimenter eventually asks: "is it actually diff --git a/scripts/ensure-single-writers.sh b/scripts/ensure-single-writers.sh index 8446ebf..10c2e41 100755 --- a/scripts/ensure-single-writers.sh +++ b/scripts/ensure-single-writers.sh @@ -1,4 +1,5 @@ #!/bin/bash +# SPDX-License-Identifier: Apache-2.0 # ensure-single-writers.sh — make each single-instance hls2mxl pipeline have # EXACTLY ONE supervisor+child. Fixes the duplicate-writer hazard that the # pipelines section's self-matching `pkill -f run-camN.sh` can create when diff --git a/scripts/mxl-doctor b/scripts/mxl-doctor index 343dcc4..13f57b8 100755 --- a/scripts/mxl-doctor +++ b/scripts/mxl-doctor @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# SPDX-License-Identifier: Apache-2.0 # mxl-doctor — one front door for all MXL lab health tooling. # # Consolidates what used to be four separate scripts into one command with a diff --git a/scripts/quickstart.sh b/scripts/quickstart.sh index 378536f..8ac662b 100755 --- a/scripts/quickstart.sh +++ b/scripts/quickstart.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# SPDX-License-Identifier: Apache-2.0 # quickstart.sh — fresh Ubuntu VM → cuttable MXL switcher in one command. # # git clone https://github.com/guycochran/mxl-switcher diff --git a/tests/README.md b/tests/README.md index a1df845..ea1499f 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,3 +1,4 @@ + # Tests Fast, hardware-free guards for the hard-won media core. They run in CI (`.github/workflows/ci.yml`) and locally with: diff --git a/tests/conftest.py b/tests/conftest.py index b68c766..4948a43 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 """Stub GStreamer (`gi`) so the contribution seam imports on a CI runner with no GStreamer installed. diff --git a/tests/js/mxl-auth.test.js b/tests/js/mxl-auth.test.js index a322972..ddd27d0 100644 --- a/tests/js/mxl-auth.test.js +++ b/tests/js/mxl-auth.test.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 // Run with: node --test tests/js/*.test.js (Node >= 18, no npm deps) const test = require('node:test'); const assert = require('node:assert'); diff --git a/tests/js/mxl-ingest-transport.test.js b/tests/js/mxl-ingest-transport.test.js index 2078f88..ae2ea1d 100644 --- a/tests/js/mxl-ingest-transport.test.js +++ b/tests/js/mxl-ingest-transport.test.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 // Verifies the "Add your camera" ingest builders produce the right SRT URL / streamid // / Larix deep-link for each guest transport: srt-direct (one shared port + streamid) // vs srt-listen (per-guest port, no streamid). Tests the pure backend/ingest-info.js diff --git a/tests/js/mxl-routes-v1.test.js b/tests/js/mxl-routes-v1.test.js index 24dd52c..a0c7c11 100644 --- a/tests/js/mxl-routes-v1.test.js +++ b/tests/js/mxl-routes-v1.test.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 // Unit tests for the /v1 shim's pure logic (backend/mxl-routes-v1.js). The route wiring // is integration-tested live on HW; here we lock the id derivation, kind inference, // health mapping, and error-code translation — the parts a refactor could silently break. diff --git a/tests/js/mxl-routes.test.js b/tests/js/mxl-routes.test.js index 7fd22ce..26e4523 100644 --- a/tests/js/mxl-routes.test.js +++ b/tests/js/mxl-routes.test.js @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 // Integration-ish test of backend/mxl-routes.js wiring using a fake Express app and a // mocked global fetch — no express, no network, no MXL VM. const test = require('node:test'); diff --git a/tests/test_check_dco.py b/tests/test_check_dco.py new file mode 100644 index 0000000..366ab7e --- /dev/null +++ b/tests/test_check_dco.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: Apache-2.0 +"""scripts/check-dco.sh: passes signed-off commits, fails missing/mismatched sign-offs, +ignores merge commits. Runs against a throwaway git repo (no network).""" +import shutil +import subprocess +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parent.parent +SCRIPT = ROOT / "scripts" / "check-dco.sh" + +pytestmark = pytest.mark.skipif(shutil.which("git") is None or shutil.which("bash") is None, + reason="needs git + bash") + + +def _git(repo, *args, email="dev@example.com", name="Dev"): + env = {"GIT_AUTHOR_NAME": name, "GIT_AUTHOR_EMAIL": email, + "GIT_COMMITTER_NAME": name, "GIT_COMMITTER_EMAIL": email, + "HOME": str(repo), "PATH": "/usr/bin:/bin:/usr/local/bin:/opt/homebrew/bin", + "GIT_CONFIG_NOSYSTEM": "1"} + return subprocess.run(["git", "-C", str(repo), *args], capture_output=True, text=True, + env=env, check=True).stdout.strip() + + +def _commit(repo, fname, msg, **kw): + (repo / fname).write_text(fname) + _git(repo, "add", fname, **kw) + _git(repo, "commit", "-q", "-m", msg, **kw) + + +def _check(repo, base, head="HEAD"): + return subprocess.run(["bash", str(SCRIPT), base, head], cwd=repo, capture_output=True, text=True) + + +@pytest.fixture +def repo(tmp_path): + _git(tmp_path, "init", "-q", "-b", "main") + _commit(tmp_path, "base.txt", "base") + _git(tmp_path, "branch", "base") + return tmp_path + + +def test_signed_off_commits_pass(repo): + _commit(repo, "a.txt", "feat: a\n\nSigned-off-by: Dev ") + _commit(repo, "b.txt", "fix: b\n\nSigned-off-by: Dev ") # case-insensitive + r = _check(repo, "base") + assert r.returncode == 0, r.stdout + r.stderr + assert "2 commit(s) checked" in r.stdout + + +def test_missing_signoff_fails(repo): + _commit(repo, "a.txt", "feat: a\n\nSigned-off-by: Dev ") + _commit(repo, "b.txt", "fix: no trailer") + r = _check(repo, "base") + assert r.returncode == 1 + assert "missing Signed-off-by" in r.stdout and "fix: no trailer" in r.stdout + + +def test_mismatched_signoff_fails(repo): + _commit(repo, "a.txt", "feat: a\n\nSigned-off-by: Someone Else ") + r = _check(repo, "base") + assert r.returncode == 1 + assert "does not match author" in r.stdout + + +def test_merge_commits_are_ignored(repo): + _git(repo, "checkout", "-q", "-b", "side", "base") + _commit(repo, "s.txt", "side\n\nSigned-off-by: Dev ") + _git(repo, "checkout", "-q", "main") + _git(repo, "merge", "-q", "--no-ff", "-m", "Merge side (no sign-off on the merge itself)", "side") + r = _check(repo, "base") + assert r.returncode == 0, r.stdout + + +def test_usage_error_without_base(repo): + r = subprocess.run(["bash", str(SCRIPT)], cwd=repo, capture_output=True, text=True) + assert r.returncode == 2 diff --git a/tests/test_desired_state_report.py b/tests/test_desired_state_report.py index 86bcc8b..eb980a1 100644 --- a/tests/test_desired_state_report.py +++ b/tests/test_desired_state_report.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 """Unit tests for the report-only multi-plane desired-state drift stub. No docker, no heal, no live facility — fixture JSON only. diff --git a/tests/test_docs_links.py b/tests/test_docs_links.py index eb06c20..7d5cbac 100644 --- a/tests/test_docs_links.py +++ b/tests/test_docs_links.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 """Docs-link sanity: every relative Markdown link to a repo file must resolve. An adoptable reference implementation's docs are a promise; a 404 relative link is a diff --git a/tests/test_facility_from_discovery.py b/tests/test_facility_from_discovery.py index 5e3baaa..03dc2a7 100644 --- a/tests/test_facility_from_discovery.py +++ b/tests/test_facility_from_discovery.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 """R2 fixture test: the quickstart's discovered flow UUIDs must flow into a manifest the UI/backend can use, so cuts resolve to REAL flows on a fresh box — not the lab's fixed UUIDs that don't exist there. diff --git a/tests/test_facility_manifest.py b/tests/test_facility_manifest.py index b144f84..db12c5d 100644 --- a/tests/test_facility_manifest.py +++ b/tests/test_facility_manifest.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 """The facility manifest (config/facility.json) is the single source of truth for flow UUIDs and control ports. Tools still carry a baked-in fallback map so they run if the manifest goes missing — but those fallbacks MUST agree with the diff --git a/tests/test_guest_slots.py b/tests/test_guest_slots.py index 0a2e6e8..365b589 100644 --- a/tests/test_guest_slots.py +++ b/tests/test_guest_slots.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 """Stable guest-slot semantics for guest_slot_watcher. The bug this guards: when guests were appended only if present, an absent Guest 1 made diff --git a/tests/test_js_backend.py b/tests/test_js_backend.py index 1833d93..1985ac9 100644 --- a/tests/test_js_backend.py +++ b/tests/test_js_backend.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 """Runs the dependency-free Node tests under tests/js/ (backend auth + rate limit). Skipped automatically when `node` isn't installed, so the Python suite still runs diff --git a/tests/test_launch_parity.py b/tests/test_launch_parity.py index 79bda88..07a272c 100644 --- a/tests/test_launch_parity.py +++ b/tests/test_launch_parity.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 """Golden-string parity test for the guest SRT launch pipeline. During the seam refactor we verified (via a stubbed-gi dry run) that the guest launch diff --git a/tests/test_local_ui.py b/tests/test_local_ui.py index c674164..f35eb15 100644 --- a/tests/test_local_ui.py +++ b/tests/test_local_ui.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 """The local control plane (backend/local-server.js + web/local.html) is the self-contained switcher UI an adopter gets from the open repo — no prodbots, no external services. These checks keep that promise without needing Node: they scan diff --git a/tests/test_mxl_doctor.py b/tests/test_mxl_doctor.py index ca92c65..4ad3835 100644 --- a/tests/test_mxl_doctor.py +++ b/tests/test_mxl_doctor.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 """mxl-doctor is the single front door that consolidates the four health scripts. These check the dispatcher's SAFE, environment-free paths — help text, argument handling, and that every script it dispatches to actually exists. The report and diff --git a/tests/test_no_hardcoded_hosts.py b/tests/test_no_hardcoded_hosts.py index c054f51..cd934ae 100644 --- a/tests/test_no_hardcoded_hosts.py +++ b/tests/test_no_hardcoded_hosts.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 """Tripwire: no NEW hardcoded IPs / site hostnames in code. Site-specific endpoints (VM IPs, the TAMS/MinIO host, the facility backend URL, the diff --git a/tests/test_no_secrets.py b/tests/test_no_secrets.py index d52621f..d344018 100644 --- a/tests/test_no_secrets.py +++ b/tests/test_no_secrets.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 """Guard against committing secrets to this public repo. Not a replacement for a real scanner (CI also runs one), but a fast, dependency-free diff --git a/tests/test_participant.py b/tests/test_participant.py index 4654959..adcf59f 100644 --- a/tests/test_participant.py +++ b/tests/test_participant.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 """Participant pairs a guest's video+audio legs (v0.3 Phase 3). The whole point of Participant is that it composes the TWO per-essence legs from the diff --git a/tests/test_quickstart_hardening.py b/tests/test_quickstart_hardening.py index 41f2f01..3ea5499 100644 --- a/tests/test_quickstart_hardening.py +++ b/tests/test_quickstart_hardening.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 """Hardening guards for scripts/quickstart.sh and the browser pages. Hardware-free: extracts the small, self-contained pieces (graphics server, guest SRT diff --git a/tests/test_selfheal.py b/tests/test_selfheal.py index 5087b9a..4293e22 100644 --- a/tests/test_selfheal.py +++ b/tests/test_selfheal.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 """R7 self-healer tests. The healer (tools/mxl-selfheal.sh) is a bash script that polls local control ports and recovers the two drift failures we hit all session (selector down, relay waiting-for-audio). We test its DECISION logic without a real diff --git a/tools/audio_pgm.py b/tools/audio_pgm.py index 9482ee3..bd7caf3 100644 --- a/tools/audio_pgm.py +++ b/tools/audio_pgm.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """Program AUDIO MIXER for the MXL demo (v2 — replaces audio-follow-video). Writes the "PGM Audio" flow the encoder already consumes, but as a live MIX: diff --git a/tools/backfill-mini.py b/tools/backfill-mini.py index f5f69ee..2d4969c 100644 --- a/tools/backfill-mini.py +++ b/tools/backfill-mini.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 # One-time: build ~/tams-mini/.jpg from the last-12h sprite sheets already in # MinIO, so overview_pass produces a full 12h strip immediately (instead of # filling 1 tile/min over 12h). Additive; reads sprites/, writes local minis. diff --git a/tools/cam2_ingest.py b/tools/cam2_ingest.py index 838d779..762dc0f 100644 --- a/tools/cam2_ingest.py +++ b/tools/cam2_ingest.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """Camera 2 (static SDI shot via Haivision Makito X4) ingest — thin adapter. Same canonical back half as cam1/guest (contribution_core.py); this is the front diff --git a/tools/cam_ingest.py b/tools/cam_ingest.py index 349dbac..919aa9a 100644 --- a/tools/cam_ingest.py +++ b/tools/cam_ingest.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """Camera 1 (studio PTZ, H.264 over RTSP) ingest — thin adapter. Low-latency cam -> canonical v210 -> "CAM Live" (selector slot 0), with the diff --git a/tools/cam_relay.py b/tools/cam_relay.py index 5f89b34..f744980 100644 --- a/tools/cam_relay.py +++ b/tools/cam_relay.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """MXL latency normalizer: re-publish the PTZ cam flow with PTS shifted forward so its grain index aligns with locally-generated flows (makes it selector-cuttable). Usage: cam_relay.py [offset_ns]""" diff --git a/tools/desired_state_report.py b/tools/desired_state_report.py index abff51b..2d4935b 100755 --- a/tools/desired_state_report.py +++ b/tools/desired_state_report.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """desired_state_report — report-only desired vs actual drift table (v0.1 sketch). Compares config/facility.json (desired) to a mocked actual-state JSON and prints diff --git a/tools/facility.py b/tools/facility.py index 861d4bc..e511aa9 100644 --- a/tools/facility.py +++ b/tools/facility.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """Load the facility manifest (config/facility.json) — the single source of truth for domain, network, control ports, and flow UUIDs. diff --git a/tools/facility_from_discovery.py b/tools/facility_from_discovery.py index 480569c..3f38ed9 100644 --- a/tools/facility_from_discovery.py +++ b/tools/facility_from_discovery.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """Generate a facility.json from the flow UUIDs the quickstart actually discovered. The problem (review R2): the UI / backend read flow UUIDs from config/facility.json diff --git a/tools/flow_stabilizer.py b/tools/flow_stabilizer.py index 6ee689c..44cd913 100644 --- a/tools/flow_stabilizer.py +++ b/tools/flow_stabilizer.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """Flow stabilizer — the first-principles fix for the reader-wedge class. A real switcher's Super Source never has a dead pane because its crossbar diff --git a/tools/grain_probe.py b/tools/grain_probe.py index 257fa99..18ca54c 100644 --- a/tools/grain_probe.py +++ b/tools/grain_probe.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """Grain-rate + uniqueness probe for the MXL demo health board (v2). Why uniqueness and not just rate: every wedge species we've hit passes the diff --git a/tools/guest-leg-doctor.sh b/tools/guest-leg-doctor.sh index dbdf341..73db388 100644 --- a/tools/guest-leg-doctor.sh +++ b/tools/guest-leg-doctor.sh @@ -1,4 +1,5 @@ #!/bin/bash +# SPDX-License-Identifier: Apache-2.0 # guest-leg-doctor — fast (15s) healer for the guest fabric legs. # Every contributor reconnect (cellular EOS, Larix stop/start) recreates the # VM2 source flow, which wedges the leg on BOTH ends: the initiator's reader diff --git a/tools/guest_audio.py b/tools/guest_audio.py index fbe23a3..7ddefcf 100644 --- a/tools/guest_audio.py +++ b/tools/guest_audio.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """Guest AUDIO ingest — thin adapter (v0.3). Pulls a contributor's audio and writes it as an MXL audio flow for the program diff --git a/tools/guest_av_listen.sh b/tools/guest_av_listen.sh index c4e7c5c..a050a0e 100755 --- a/tools/guest_av_listen.sh +++ b/tools/guest_av_listen.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# SPDX-License-Identifier: Apache-2.0 # guest_av_listen — A/V SRT fan-out for srt-listen mode. # # In srt-listen mode the contributor dials ONE SRT stream straight into the switcher diff --git a/tools/guest_ingest.py b/tools/guest_ingest.py index 400585d..055948a 100644 --- a/tools/guest_ingest.py +++ b/tools/guest_ingest.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """Contributor ("guest") video ingest for the MXL switcher — thin adapter. Anyone can push SRT to mediamtx (streamid publish:guest1 / guest2); this conforms diff --git a/tools/guest_slot_watcher.py b/tools/guest_slot_watcher.py index 6f32de0..c69e936 100644 --- a/tools/guest_slot_watcher.py +++ b/tools/guest_slot_watcher.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """guest_slot_watcher — backend-free selector re-attach for the quickstart tier. The live demo re-attaches the selector via the Express backend's /api/mxl/repair diff --git a/tools/layout_pgm.py b/tools/layout_pgm.py index 7bee51d..358c84d 100644 --- a/tools/layout_pgm.py +++ b/tools/layout_pgm.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """Program layout compositor for the MXL demo switcher (Tier 3: 2-up / PiP / 4-up). Writes a "Layout PGM" flow that composites up to FOUR of the six switcher diff --git a/tools/mv_encode.py b/tools/mv_encode.py index 13d7d9c..e806246 100644 --- a/tools/mv_encode.py +++ b/tools/mv_encode.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """Multiview encoder leg: the ONE browser encoder for the whole wall. Reads the "Multiview PGM" flow (written by mxl_multiview.py in the hls2mxl diff --git a/tools/mxl-egress.sh b/tools/mxl-egress.sh index a520086..503b953 100644 --- a/tools/mxl-egress.sh +++ b/tools/mxl-egress.sh @@ -1,4 +1,5 @@ #!/bin/bash +# SPDX-License-Identifier: Apache-2.0 # mxl-egress.sh — OUTPUT NODE for the MXL demo (the "distribute" pillar, akin to # a Grass Valley AMPP output node). Pulls the finished PROGRAM that mediamtx # already carries (path mxl2webrtc) and pushes it to an RTMP/SRT destination. diff --git a/tools/mxl-selfheal.sh b/tools/mxl-selfheal.sh index 3fbb156..0f75707 100755 --- a/tools/mxl-selfheal.sh +++ b/tools/mxl-selfheal.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# SPDX-License-Identifier: Apache-2.0 # mxl-selfheal.sh — a portable self-healer for the quickstart MXL switcher. # # Opt-in. quickstart.sh does NOT start this by default. Set MXL_SELFHEAL=1 when diff --git a/tools/mxl_multiview.py b/tools/mxl_multiview.py index eda3e57..c826d5f 100644 --- a/tools/mxl_multiview.py +++ b/tools/mxl_multiview.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """MXL-native multiview compositor (Tier 1.5): 8 domain flows -> one 3x3 wall. All seven switcher inputs plus the Keyer PGM are read straight out of the diff --git a/tools/mxl_thumbs.py b/tools/mxl_thumbs.py index 0df801a..4375657 100644 --- a/tools/mxl_thumbs.py +++ b/tools/mxl_thumbs.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """Multiview thumbnails for the MXL demo switcher (Tier 1). One persistent low-rate pipeline per selector input: mxlsrc -> 1 frame every diff --git a/tools/participant.py b/tools/participant.py index 29ce45b..02af7ce 100644 --- a/tools/participant.py +++ b/tools/participant.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """Participant — one contributor, both essences (v0.3 Phase 3). A guest contributes VIDEO and AUDIO together, but each essence runs in its OWN diff --git a/tools/patch-target-ip.py b/tools/patch-target-ip.py index 33ebf17..5b031ee 100644 --- a/tools/patch-target-ip.py +++ b/tools/patch-target-ip.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """Patch an mxl-fabrics-demo target.json for NAT/non-routed networks. TargetInfo embeds the target's LOCAL bind sockaddr inside the base64 diff --git a/tools/pgm_lite.py b/tools/pgm_lite.py index 0265b9c..a6af55c 100644 --- a/tools/pgm_lite.py +++ b/tools/pgm_lite.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """PGM Lite: 960x540 copy of the Keyer PGM for bandwidth-limited fabric receivers. Full 1080p30 v210 is ~1.32 Gbps — MORE than 1GbE; this quarter- resolution flow is ~0.33 Gbps and fits comfortably on a GigE laptop at a diff --git a/tools/program-fps-doctor.sh b/tools/program-fps-doctor.sh index 252eea2..01f878b 100644 --- a/tools/program-fps-doctor.sh +++ b/tools/program-fps-doctor.sh @@ -1,4 +1,5 @@ #!/bin/bash +# SPDX-License-Identifier: Apache-2.0 # program-fps-doctor v2 — detects the repeat-wedged ENCODER reader. # v1 used mpdecimate (similarity) and would FALSE-TRIGGER on static night # scenes. v2 counts BYTE-IDENTICAL decoded frames (YDIF==0): x264 emits diff --git a/tools/selector-doctor.sh b/tools/selector-doctor.sh index 06fa001..da12855 100755 --- a/tools/selector-doctor.sh +++ b/tools/selector-doctor.sh @@ -1,4 +1,5 @@ #!/bin/bash +# SPDX-License-Identifier: Apache-2.0 # selector-doctor.sh — auto-heal the wedge where the input-selector container's # CONTROL API goes dead (times out) while the container spins ~100% CPU. # This is the 2026-09-11 14:02 incident class: the selector API is what every diff --git a/tools/start-jonas-leg.sh b/tools/start-jonas-leg.sh index 1e8d370..ae2e043 100644 --- a/tools/start-jonas-leg.sh +++ b/tools/start-jonas-leg.sh @@ -1,4 +1,5 @@ #!/bin/bash +# SPDX-License-Identifier: Apache-2.0 # start-jonas-leg.sh — send our live Keyer PGM (or PGM Lite) to a remote # MXL target as raw grains over the fabric. Run on VM1 as guy. # diff --git a/tools/tams_shipper.py b/tools/tams_shipper.py index 6d8f1e9..497533c 100644 --- a/tools/tams_shipper.py +++ b/tools/tams_shipper.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """MXL → TAMS bridge (shipper half). Runs on VM2. Watches the spool of 1s MPEG-TS segments cut from the fabric-delivered MXL diff --git a/tools/viewer_count.py b/tools/viewer_count.py index 286557d..38e6646 100644 --- a/tools/viewer_count.py +++ b/tools/viewer_count.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """Live WebRTC viewer counter + 24h traffic record for the MXL health board. mediamtx's API is disabled (enabling = container recreate = viewer blip), but diff --git a/tools/zoomiso_dryrun.py b/tools/zoomiso_dryrun.py index 69427d6..5fd6073 100644 --- a/tools/zoomiso_dryrun.py +++ b/tools/zoomiso_dryrun.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 """zoomiso_dryrun — prove the ZoomISO Cloud pipeline WITHOUT the real product. ZoomISO Cloud (when we get the beta) emits native MXL v210 grains. We don't have it diff --git a/web/vendor/ffmpeg/fetch-core-wasm.sh b/web/vendor/ffmpeg/fetch-core-wasm.sh index 7c5456f..512c0a0 100755 --- a/web/vendor/ffmpeg/fetch-core-wasm.sh +++ b/web/vendor/ffmpeg/fetch-core-wasm.sh @@ -1,9 +1,13 @@ #!/bin/bash # The ffmpeg.wasm core binary is 32MB — not committed to keep the repo lean. # Run this once from web/vendor/ffmpeg/ to pull the exact matching build. +# LICENSE: this binary is FFmpeg built with --enable-gpl + libx264/libx265, i.e. +# GPL-2.0-or-later. Serving it to browsers distributes GPL object code; see +# THIRD-PARTY-NOTICES.md at the repo root for what that means. set -e cd "$(dirname "$0")" URL="https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.6/dist/umd/ffmpeg-core.wasm" echo "Fetching ffmpeg-core.wasm (32MB) from $URL ..." curl -fL -o ffmpeg-core.wasm "$URL" echo "Done. Size: $(du -h ffmpeg-core.wasm | cut -f1)" +echo "Note: ffmpeg-core.wasm is GPL-2.0-or-later (FFmpeg --enable-gpl, x264/x265). See THIRD-PARTY-NOTICES.md."