commit b8b2ba8da121b47312e5e642b701c4bee383401e Author: Peter Edley Date: Fri Sep 18 18:36:19 2026 +0100 chore: initial project scaffolding Add project configuration, documentation and development tooling: - flake.nix dev shell with Go, golangci-lint, turbojpeg - .envrc sourcing sops-encrypted .secrets.env - golangci-lint v2 config - opencode.json MCP + LSP wiring - AGENTS.md development guidelines diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..691c730 --- /dev/null +++ b/.envrc @@ -0,0 +1,10 @@ +use flake + +# Load project-local secrets (sops-encrypted, gitignored). Decrypt on the fly +# with this machine's age key so plaintext never touches disk. Requires the +# age key at ~/.config/sops/age/keys.txt. +if [[ -f .secrets.env ]]; then + set -a + eval "$(sops -d --output-type dotenv .secrets.env)" + set +a +fi \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cee048a --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# direnv +.direnv/ +# sops-encrypted project secrets; keep out of git (encrypted via .sops.yaml) +.secrets.env + +# nix +result +result-* diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..fed25c6 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,49 @@ +version: "2" + +run: + timeout: 5m + tests: true + +linters: + default: none + enable: + - errcheck + - govet + - staticcheck + - ineffassign + - unused + - misspell + - revive + - gosec + - nakedret + - unconvert + - gocritic + exclusions: + # Cgo-heavy capture/protocol code may need exceptions for specific patterns. + rules: + - path: _test\.go + linters: + - gosec + # Wire-format int conversions: the Teleport protocol defines 32-bit fields + # on the wire (uint32) while the reference uses int32 struct fields. These + # casts are intentional and mirror obs-teleport's types.go. + - path: "internal/protocol/" + linters: + - gosec + text: "G115" + # gocritic mis-parses cgo calls with multiple pointer args (&jpegSize, + # &dstPtr) as suspicious == comparisons. + - path: "internal/protocol/jpeg\\.go" + linters: + - gocritic + text: "dupSubExpr" + # Dialogue box pixel math is clamped by n<=4 in the avg loop. + - path: "cmd/teleportfling/" + linters: + - gosec + text: "G115" + +formatters: + enable: + - gofmt + - goimports \ No newline at end of file diff --git a/.sops.yaml b/.sops.yaml new file mode 100644 index 0000000..2e4493f --- /dev/null +++ b/.sops.yaml @@ -0,0 +1,8 @@ +# sops creation rules for TeleportFling. +# Encrypts .secrets.env with this machine's age key (same key used in Nix-Vibe). +# The age private key lives at ~/.config/sops/age/keys.txt — sops finds it +# automatically, so no SOPS_AGE_KEY_FILE override is needed here. +creation_rules: + - path_regex: \.secrets\.env$ + age: >- + age17z3fuzlfmerpnsrum9g4sfmkmgghtlltfq07lp79uzugm5are3cs64dnqy \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..174c8c3 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,45 @@ +# AGENTS.md + +Development guidelines for all agents and contributors working in this repository. + +## Core Development Principles + +1. **Do not repeat yourself (DRY)** - All development must avoid code duplication. Extract shared logic into reusable functions, modules, or components. +2. **Language best practices** - All development must follow the idiomatic style and best practices for the chosen language. +3. **Well-commented code** - All development must be clearly commented so that someone new to the project can understand the intent and flow of the code. + +## Git Workflow + +1. **Test before commit** - Changes must be tested locally before they are committed. Do not commit broken or untested code. +2. **No remote push without confirmation** - Changes are committed locally only. Nothing is pushed to the remote repository until explicitly confirmed by the user. +3. **Small atomic commits** - Keep commits focused. One logical change per commit. Do not bundle unrelated changes. +4. **Run the linter before committing** - Always run the project's linting and formatting checks before creating a commit. Fix any warnings or errors. +5. **Failing builds never merge** - No change that breaks the build, tests, or linting may be merged or pushed. + +### Commit Messages + +Use [Conventional Commits](https://www.conventionalcommits.org/) format: + +``` +feat: add user authentication +fix: resolve race condition in queue +refactor: extract validation logic +docs: update setup instructions +test: add unit tests for teleport fling +``` + +Types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`. Optionally include a scope: `feat(auth): ...`. + +## Testing + +1. **Test-first development** - Write tests alongside new features. Test coverage should reflect the functionality being added or changed. +2. **Full suite before commit** - Run the complete test suite (not just incremental tests) before committing to ensure nothing is broken. + +## Security + +1. **Never commit secrets** - No API keys, passwords, tokens, connection strings, or credentials of any kind in code, config files, or git history. Use environment variables or secret management tools. +2. **Review `.gitignore`** - Ensure files containing potential secrets are excluded from version control. + +## Documentation + +1. **Keep docs in sync** - Update relevant documentation (README, comments, examples) alongside any behavioral change. Documentation that describes outdated behavior is a bug. \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9deb179 --- /dev/null +++ b/LICENSE @@ -0,0 +1,340 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable form. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the remainder of the section is intended +to apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claim; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Your decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is a free library, we recommend using the GNU Lesser +General Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..9a65615 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1789684949, + "narHash": "sha256-ZKhUe/2IJUq1JhKxKMu8rbkgSGmPP2ZCqlIPn40aGCM=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e554fab72f81915600f3f449b786fd9af40439a5", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..9c84bef --- /dev/null +++ b/flake.nix @@ -0,0 +1,44 @@ +{ + description = "TeleportFling Development Environment"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + devShells.default = pkgs.mkShell { + inputsFrom = []; + nativeBuildInputs = with pkgs; [ + go + golangci-lint + pkg-config + gopls + nixd + nodejs + gitea-mcp-server + ]; + buildInputs = with pkgs; [ + git + direnv + nix-direnv + libjpeg_turbo + pipewire + xdg-desktop-portal + ]; + + # Make turbojpeg resolve via pkg-config for cgo builds. + PKG_CONFIG_PATH = "${pkgs.libjpeg_turbo.dev}/lib/pkgconfig"; + LD_LIBRARY_PATH = "${pkgs.libjpeg_turbo.out}/lib"; + + shellHook = '' + echo "TeleportFling dev shell ready!" + ''; + }; + }); +} diff --git a/opencode.json b/opencode.json new file mode 100644 index 0000000..67ea445 --- /dev/null +++ b/opencode.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://opencode.ai/config.json", + "references": { + "obs-teleport": { + "repository": "fzwoch/obs-teleport", + "description": "Reference implementation of the Teleport protocol used by the OBS receiver. Consult for packet layouts (JPEG/WAVE headers), multicast discovery (AnnouncePayload), and sender behavior when implementing teleportfling." + } + }, + "mcp": { + "gitea": { + "type": "local", + "command": ["gitea-mcp", "-t", "stdio"], + "environment": { + "GITEA_HOST": "http://homeserver:3050", + "GITEA_ACCESS_TOKEN": "{env:GITEA_ACCESS_TOKEN}" + } + }, + "obs": { + "type": "local", + "command": ["npx", "-y", "obs-mcp"], + "environment": { + "OBS_WEBSOCKET_URL": "ws://localhost:4455", + "OBS_WEBSOCKET_PASSWORD": "{env:OBS_WEBSOCKET_PASSWORD}" + } + } + }, + "lsp": { + "gopls": { + "command": ["gopls"], + "extensions": [".go"] + }, + "nixd": { + "command": ["nixd"], + "extensions": [".nix"] + } + } +} \ No newline at end of file diff --git a/project.md b/project.md new file mode 100644 index 0000000..5768ddb --- /dev/null +++ b/project.md @@ -0,0 +1,151 @@ +# TeleportFling - Project Overview + +## Summary + +TeleportFling is a standalone Linux application that captures a single screen along with +audio from the host machine and transmits the combined stream over a LAN using the +[Teleport protocol](https://github.com/fzwoch/obs-teleport). The teleport stream is received +by another machine running [OBS Studio](https://obsproject.com) with the `obs-teleport` +plugin installed, where it appears as a regular Teleport source. + +The project is an OBS/NDI-like replacement for a minimal multi-machine streaming setup: +one machine (the "flinger") produces a screen + audio feed, a second machine aggregates +and streams it. No NDI compatibility of any form. + +## Goals + +1. Capture a single monitor (screen) + configurable audio source on Linux. +2. Push the stream over the LAN using the Teleport protocol so an OBS + `obs-teleport` + receiver can consume it with zero plugin-side configuration on the source machine. +3. Ship a standalone, installable application, not an OBS plugin: + - Full configuration GUI (device, audio source, quality, discovery settings). + - System tray icon for quick start/stop and status. + - Works headless-ish: capture/transmit logic is decoupled from the GUI so it can + later run as a daemon or be scripted. + +## Non-Goals + +- NDI or DistroAV compatibility. +- Software transcoding beyond the JPEG frame encoding the protocol requires. +- Synchronous multi-screen capture initially (single screen only, per the name). +- Windows/macOS support initially (Linux only). + +## Target Environment + +| Concern | Decision | +|--------------------|----------------------------------------------------------------| +| Language | Go (same language as `obs-teleport`) | +| Display server | Wayland primary; X11 support if it falls out cheaply | +| Audio | Configurable: system/desktop audio (default) or mic/input device | +| Screen capture | Wayland: PipeWire / xdg-desktop-portal; X11: X11 extension or PipeWire fallback | +| GUI | Fyne (config window + system tray) | +| Discovery | UDP multicast peer discovery (identical to `obs-teleport`) | +| Target quality | Initial release: 1920x1080 @ 30 fps, balanced JPEG quality | +| Protocol target | Latest `obs-teleport` release (protocol contract below) | +| Remote git | Self-hosted Gitea at `http://homeserver:3050/` | + +## Teleport Protocol Contract + +Reference implementation: https://github.com/fzwoch/obs-teleport (GPL-2.0). + +1. **Peer discovery** - UDP multicast broadcast of a JSON `AnnouncePayload`: + `{"Name", "Port", "AudioAndVideo", "Version", "Address"}`. The OBS receiver uses + these announcements to populate its source list. `obs-teleport` uses + `github.com/schollz/peerdiscovery`. +2. **Transport** - TCP. The sender binds a listener on a configurable port and accepts + connections from receivers (multiple concurrent receivers supported). +3. **Packet header** (all little-endian): + - `Header`: `Type [4]byte`, `Timestamp uint64`, `Size int32`. + - `ImageHeader` (video only): `ColorMatrix [16]float32`, `ColorRangeMin [3]float32`, + `ColorRangeMax [3]float32` (from OBS video format color parameters). + - `WaveHeader` (audio only): `Format int32`, `SampleRate int32`, `Speakers int32`, + `Frames int32`. +4. **Video frames** - `Type = "JPEG"`. Frames are compressed to JPEG (turbojpeg) with + configurable quality. Payload = header + image header + JPEG bytes. +5. **Audio** - `Type = "WAVE"`. Raw interleaved PCM. Payload = header + wave header + + PCM samples (16-bit stereo @ 48 kHz typical). +6. **Ordering** - Video frames are queued, encoded in order, and sent in the same order; + dropped frames are counted as lagged frames. Audio is sent immediately as it arrives. +7. **Compatibility contract** - We must remain byte-compatible with the latest + `obs-teleport` release so that stock OBS Teleport receivers can discover and decode + our stream without modification. + +## Architecture (proposed) + +``` +┌────────────────────────────┐ ┌───────────────────────────────┐ +│ Fling app │ │ OBS receiver │ +│ ┌──────────┐ ┌─────────┐ │ │ │ +│ │ Screen │ │ Audio │ │ │ ┌─────────────────────────┐ │ +│ │ capture │ │ capture │ │ │ │ obs-teleport source │ │ +│ └────┬─────┘ └────┬────┘ │ │ └───────────┬─────────────┘ │ +│ │ │ │ │ │ │ +│ ▼ ▼ │ │ ▼ │ +│ ┌────────────────────────┐ │ │ ┌─────────────────────────┐ │ +│ │ Flinger core │ │ │ │ (decode + render) │ │ +│ │ · JPEG encode (video) │ ├─────┼─▶│ OBS Studio pipeline │ │ +│ │ · interleave (audio) │ │ │ └─────────────────────────┘ │ +│ │ · packetize │ │ │ │ +│ │ · TCP sender │ │ │ │ +│ │ · UDP announcer │ │ │ │ +│ └───────────┬────────────┘ │ │ │ +└──────────────┼──────────────┘ └───────────────────────────────┘ + │ LAN (TCP + UDP multicast) +``` + +Components: + +1. **Core library (`internal/flinger`)** - protocol + capture agnostic: + - `protocol`: packet types, JPEG encode, WAVE packetize, header (de)serialization. + - `output`: TCP listener, connection management (reuse `obs-teleport`'s per-connection + buffered channel approach with drop-when-overflow), framing/ordering queue. + - `discovery`: UDP multicast announcer (`AnnouncePayload`). + - `capture`: interface `CaptureSource` + implementations for screen and audio. +2. **GUI (`cmd/teleportfling` or `internal/gui`)** - Fyne: + - Settings window: monitor picker, audio source picker, port, JPEG quality, + resolution/fps targets, discovery on/off, enable/disable. + - System tray: start/stop toggle, status indicator, open settings, quit. + - Config persistence (JSON config file, e.g. `~/.config/teleportfling/config.json`). +3. **Capture backend**: + - Wayland: PipeWire via `pipewire` Go bindings or `xdg-desktop-portal` screen capture. + - Audio: PipeWire/PulseAudio monitor source (system audio) or input device; configurable. + - X11 (optional): X11 API screen grab or PipeWire fallback where available. + +## Milestones + +1. **M1 - Protocol proof of life**: static JPEG frames + synthesized audio sent over TCP, + multicast announcements read successfully by OBS Teleport receiver. CLI only. +2. **M2 - Real capture**: PipeWire screen capture + system audio capture feeding the + core; confirm 1080p30 on Wayland. +3. **M3 - GUI + tray**: Fyne settings window and system tray start/stop. +4. **M4 - Hardening**: config persistence, frame dropping/backpressure, discovery options, + X11 fallback if feasible, packaging (Nix package / AppImage / release binaries). + +## Development Environment + +- Nix flake (`flake.nix`) with `devShell` workspace. +- `direnv` manages activating the Nix environment. +- Build: `go build ./...`; Lint: `golangci-lint`; Tests: `go test ./...`. +- All development follows the rules in `AGENTS.md`. + +## Open Questions + +- Bitrate/bandwidth targets beyond the balanced 1080p30 default. +- Whether to support scaling/downsampling of the captured monitor in M1/M2 or only the + monitor's native resolution. +- PipeWire bindings strategy (pure-Go bindings vs. cgo wrappers vs. `xdg-desktop-portal`). + +## Decisions Made (2026-09-18) + +1. **Name**: project (and binary) is `teleportfling`. +2. **JPEG encoding**: match `obs-teleport` exactly - use turbojpeg via cgo (`libturbojpeg`). + Benchmark against a pure-Go encoder during M1 anyway; use turbojpeg unless it is a + blocker. +3. **System tray**: Fyne's native tray support is limited on Linux; evaluate + `github.com/nicedoc/systray` (or similar) when we reach M3. Do not block M1/M2 on it. +4. **PipeWire bindings**: unresolved - evaluate pure-Go bindings vs. cgo to libpipewire + during M1. This drives capture backend build requirements. +5. **End-to-end testing**: OBS + `obs-teleport` are available locally on the dev machine + for now (loopback/LAN testing). A remote receiver machine can be added later for + further testing. +6. **License**: GPL-2.0, matching the `obs-teleport` project whose protocol we implement. \ No newline at end of file