Added plugin bitbang#1443
Conversation
|
I'd recommend including instructions for OctoPi installs (majority use case for OctoPrint) on how to disable the webcam stream services...I have similar instructions in my go2rtc gist for reference. |
|
One other thing I just remembered is that you might want to add your plugin_identifier to the list of plugins for remote access. https://github.com/OctoPrint/plugins.octoprint.org/blob/gh-pages/_topics/remote_access.md |
Just added -- thanks :) |
jneilliii
left a comment
There was a problem hiding this comment.
one small correction needed for this PR noted in remote_access.md.
relative to the plugin itself, just a quick scan with octoscanner brings up a couple of security concerns relative to the use of BlueprintPlugin.csrf_exempt.
+---------------------------------------------------- OctoScanner ----------------------------------------------------+
| |
| +- Plugin OctoPrint-BitBang --------------------------------------------------------------------------------------+ |
| | +---------------------------------------- Summary (2 total findings) -----------------------------------------+ | |
| | | 2 security | | |
| | +-------------------------------------------------------------------------------------------------------------+ | |
| | +------------------------------------------- Security (2 findings) -------------------------------------------+ | |
| | | SEC-0006 | | |
| | | Endpoint exempted from CSRF protection via BlueprintPlugin.csrf_exempt(). Ensure that the exemption is | | |
| | | necessary and that the endpoint uses an alternative anti-CSRF mechanism (e.g. token in header). | | |
| | | File: octoprint_bitbang\__init__.py:155 | | |
| | | 154 | @octoprint.plugin.BlueprintPlugin.route("/offer", methods=["POST"]) | | |
| | | > 155 | @octoprint.plugin.BlueprintPlugin.csrf_exempt() | | |
| | | 156 | def local_offer(self): | | |
| | | Suggestion: Remove the decorator if not strictly required. Otherwise, document why the endpoint cannot use | | |
| | | CSRF protection and ensure another anti-CSRF mechanism is in place. | | |
| | | | | |
| | | SEC-0006 | | |
| | | Endpoint exempted from CSRF protection via BlueprintPlugin.csrf_exempt(). Ensure that the exemption is | | |
| | | necessary and that the endpoint uses an alternative anti-CSRF mechanism (e.g. token in header). | | |
| | | File: octoprint_bitbang\__init__.py:250 | | |
| | | 249 | @octoprint.plugin.BlueprintPlugin.route("/camera/brightness", methods=["POST"]) | | |
| | | > 250 | @octoprint.plugin.BlueprintPlugin.csrf_exempt() | | |
| | | 251 | def set_brightness(self): | | |
| | | Suggestion: Remove the decorator if not strictly required. Otherwise, document why the endpoint cannot use | | |
| | | CSRF protection and ensure another anti-CSRF mechanism is in place. | | |
| | +-------------------------------------------------------------------------------------------------------------+ | |
| +-----------------------------------------------------------------------------------------------------------------+ |
| |
| +--------------------------- Rule Statistics (1 rules matched 2 times on 1/1 plugins) ----------------------------+ |
| | Matches Rule ID Message | |
| | 2 SEC-0006 Endpoint exempted from CSRF protection via BlueprintPlugin.csrf_exempt(). Ensure that the | |
| | exemption is necessary and that the endpoint uses an alternative anti-CSRF mechanism (e.g. | |
| | token in header). | |
| +-----------------------------------------------------------------------------------------------------------------+ |
| |
+---------------------------------------------------------------------------------------------------------------------+
|
I'm also not 100% sure why, but I've installed this plugin on 3 different instances and it only actually loads the settings/navbar in 1. I initially thought it was because of the pi zero 2w not being supported, but it also didn't load on my pi 4 with USB camera attached. The working install I don't have a camera attached to it but remote access did work fine. Would appreciate some more eyes on this plugin review due to the nature of it being remote access. |
Hi jneilliii, thanks! |
CSRF exemptions removed in v0.1.3; the JS now reads csrf_token from the cookie and sends it as X-CSRF-Token. Re-scan should be clean. It might require a hard reload. thanks! |
That was the weird part, there were no UI elements loading, not even settings, although the plugin was shown as being installed in plugin manager. I attempted reinstall and force refresh of the page. What I did notice was that the instances that did not work, the restart of OctoPrint was not triggered like it should have been. |
I think it would be better served to use the OctoPrintClient.getRequestHeaders function available in the OctoPrint js client rather than messing with the cookies directly. Alternatively, I think you could use the get/post methods of the OctoPrint js client instead of fetch and those headers will be added for you automatically (would need verification of that). |
|
Hi jneilliii, |
|
I just posted v0.1.5:
When you have a moment to re-test on a failing instance, the log line to look for is "BitBang plugin not loaded: " -- that should name the missing dependency directly. Happy to dig in from there. Thanks for the thoughtful review. |
jacopotediosi
left a comment
There was a problem hiding this comment.
@richlegrand Thanks for the contribution! I finally found some spare time to review the plugin; my findings are below.
- In
setup.py, you should passlicense="MIT"tosetuptools.setup()for compatibility with setuptools<77, as shown in the OctoPrint cookiecutter template. - In
pyproject.tomlthe following dependencies are never used and can be removed:numpy,Pillow,aiohttp. - I don't think it's necessary to override several plugin attributes in
__init__.y, e.g.__plugin_version__, since the plugin version is already declared inpyproject.toml. I'd suggest minimizing unnecessary overrides to reduce the risk of forgetting to update the value in both places. - In both templates you do some weird JavaScript things, like fetching
/api/settings. The correct approach is to use the viewmodels provided by OctoPrint, knockout bindings, and template vars.
In addition, I wanted to check how the PIN verification was done, but I couldn't because it seems to happen inside bitbangproxy, which however is included in the python library bitbang only as a precompiled binary. Is there a reason why the proxy's source code is not available?
|
Errata corrige: it appears that the PIN is checked by the two binaries contained in the |
|
Ah, sorry! I took a detour with the code and it's in transition. I was thinking that no one was looking, and I've just been checking into main. Please give me a a few days and I'll give an "all clear" :) I'll address your findings also-- many thanks for those. The previous code was all python and it had some speed issues running on the Pi 3, so I decided to do the proxy over the Go binary (bitbangproxy, which isn't public yet). This code will be fully open source as well. |
|
Gotcha, so just two quick suggestions about Python and Go interactions. I'd suggest avoiding the use of subprocessing and looking into whether they can interact through bindings (Foreign Function Interface) instead. For example, you can do this by compiling Go as a C shared library and calling it from Python with Regarding compilation, I'd suggest keeping only the Go source code in the repository instead of the compiled binaries, and adding a GitHub Action (or similar) that compiles them and bundles them into the Python library's build artifact. |
|
Hi @jacopotediosi and @jneilliii -- thanks for your patience while I finished the rework. I just tagged v0.2.1, which should close out the open items. Here's the "all clear" :) The Go proxy is now fully open source. What was the private bitbangproxy is public: https://github.com/richlegrand/bitbang-cli (MIT). The PIN check and all signaling/transport logic are in the source there. Binaries are no longer committed -- they're built from source in CI. Per @jacopotediosi's suggestion, the Some notes on FFI vs. subprocess -- I looked into ctypes/cffi and gopy. I ended up keeping the subprocess boundary on purpose: the proxy is a long-lived process running its own WebRTC/networking event loop, and I wanted hard crash-isolation from the OctoPrint process plus a clean lifecycle (it's the same binary users can run standalone as bare Remote-access security posture-- since you wanted more eyes given the nature of the plugin: traffic is peer-to-peer WebRTC/DTLS (end-to-end encrypted; the signaling server only brokers the handshake, and a TURN relay (if one is needed) carries ciphertext only), there's an optional PIN, no account, and the earlier CSRF exemptions are gone (now via I've QA'd on both 32-bit and 64-bit OctoPi. Please take look when you have a moment-- thanks! |
|
I'm testing out your new instructions on my failed instances, but then realized something about your plugin that should be modified. The DOM replacement of the default classic webcam is not the way to go for webcam providers. Instead, you should generate your own webcam jinja2 template. This way you aren't stepping on the toes of other plugins like the classic webcam and providing your own. An example of this can be found in the simple https://github.com/OctoPrint/OctoPrint-Testpicture |
I made this modification to v0.2.3. |
|
So after getting through the fixes for bookworm install of aiortc as described here the plugin is functional, but I wonder if there would be a better approach for initial install given the CI based binary changes. The install now requires a PyPi based install of OctoPrint-BitBang to be fully functional (after doing the other system level prerequisites) instead of a source distribution zip URL. The only other approach I can think of is bundling the compiled binaries into a source+binaries release asset zip on GitHub rather than pushing to PyPi. I know the WS281x plugin does this, albeit without the compiled binaries part. That being said, I think the plugin listing should include a prerequisites section documenting the necessary steps prior to attempting install of the plugin as you have on the PyPi description until either @jacopotediosi or @foosel are able to chime in on possible alternatives. |
|
I didn't realize the registry installs from a zip archive, not pip/PyPI. That reframes it-- thanks for the nudge and the WS281x pointer. Happy to make the change-- I'll have CI bundle the proxy binaries into a release.zip attached to the GitHub release, and point the listing's archive: at releases/latest/download/release.zip. I'll also add a prerequisites section covering the system-level steps. PyPI stays available for anyone who prefers pip install (but I could remove if it's confusing -- let me know) I'll try to get this set up today before travel. Let me know if there's anything else. |
|
If you go the release zip route make sure to update the software update hook as well. |
|
I'm currently quite busy with 2.0.0rc3 and some other stuff in the background of OctoPrint, so I cannot look closer at the discussion here, but just to clarify:
This basically just depends on how you register your plugin & your software update hook. In theory it should also work perfectly fine to set a PyPI package name as the archive URL. However, if you provide/stay with the default of the GitHub branch zip, it will go with that and not whatever you might or might not also have uploaded to PyPI. Check the docs on the plugin_id:
type: pypi_release
package: your_package
pip: "your_package=={target_version}"for the software update hook, and have just That should work. However, I have to admit that this would make your plugin the first plugin to ever test this, and also remove the option to support release channels further down the road (those require github releases/branches). If you need to support multiple architectures though with your combined bits, this is probably the best way to go, and at some point this really should also be tested. |
I thought that this might work that way, but wasn't completely sure since I don't remember any other plugins using this approach. |
|
Afaik no other plugins do it this way. But it should work 🤔 |
|
Thanks both, really helpful-- @jneilliii you're right about the hook: mine currently points pip at the /archive/{version}.zip source zip, the binary-less path that kicked this off, so it needs fixing regardless. pypi_release looks like the natural fit -- my wheel is py3-none-any with all three arch binaries baked in, so pip install OctoPrint-BitBang=={target_version} already works on every arch from one artifact. Before I commit to being the first to rely on it, I'd like to actually run an install + update through Plugin Manage -- and I'm traveling a few days, so I'll test it and report back shortly after. I've got a release.zip asset wired up in CI as a proven fallback either way. |
|
Software Update may be able to handle it but not Plugin Manager. I just tested by modifying config.yaml to pull from an offline copy of the plugins.json and plugin manager attempts to install by downloading the archive field first it seems. Looking at Plugin Manager code it seems there are only json, python file, and download of known extension types (zip, gzip, etc.). So tested again with
However, because this can't be patterned to use a "latest" version URL I think the publish to release asset on GitHub is still the better option. |
|
If you want to attempt your own installs with plugin manager you can update config.yaml to point to your own plugins.json file like I did for testing. Just modify the repository parameter to point to your own copy of the json file. |
jneilliii
left a comment
There was a problem hiding this comment.
There should be a pre-requisite section in the description describing any OS level application dependency installs.
jneilliii
left a comment
There was a problem hiding this comment.
I think all my concerns have been addressed. Will request re-review by @jacopotediosi.
|
quickie note -- I'm back from travel and I'm doing a refactor to address the code in bitbang_navbar.jinja2 that fetches /api/settings that @jacopotediosi brought up. I hope to have it up tonight :) |
|
@jacopotediosi @jneilliii -- all open items addressed, ready for another look:
Proxy source has been public since v0.2.1 at bitbang-cli (PIN check + transport there; CI builds the binaries from it). @jneilliii -- added the Prerequisites section and set the title to BitBang. Thanks! |

What is the name of your plugin?
OctoPrint-BitBang
What does your plugin do?
Provides full remote access to an OctoPrint instance -- including live H.264 video -- over a single secure HTTPS shareable link. It uses BitBang to broker a WebRTC peer-to-peer connection, so no account, subscription, port forwarding, tunnel or VPN is required. Video is hardware-encoded on Pi 4 (V4L2 M2M) and software-encoded on Pi 5 / other Linux hosts. Integrates with OctoPrint's
WebcamProviderPluginfor snapshots and timelapse.Where can we find the source code of your plugin?
https://github.com/richlegrand/OctoPrint-BitBang
Was any kind of genAI (ChatGPT, Copilot etc) involved in creating this plugin?
Yes -- some assistance for refactoring. Plugin is not vibe-coded; I designed it and can maintain it. BitBang is where much of the code is and it is fully written by me.
Is your plugin commercial in nature?
No
Does your plugin rely on some cloud services?
Yes -- the
bitba.ngsignaling service brokers the WebRTC peer connection. Once peers are connected, all media and HTTP traffic flows directly between the browser and OctoPrint over an encrypted WebRTC channel (DTLS-SRTP);bitba.ngonly sees the public key, a derived UID, and connection metadata. If a direct peer-to-peer path can't be established,bitba.ngmay relay the encrypted stream via TURN — even then, the relay sees ciphertext only. No account, no telemetry. Privacy policy: https://github.com/richlegrand/OctoPrint-BitBang/blob/main/PRIVACY.mdFurther notes
The
cloudattribute is declared in the registration YAML;__plugin_privacypolicy__is set in the plugin source and points at the same policy URL.