Upload to Roam via the backend API instead of a headless browser - #146
Merged
Conversation
Roam's web app now sits behind a Vercel bot-verification challenge that the headless Firefox on the Pi fails: sign-in succeeds, but the graph never finishes loading (or renders "Failed to verify your browser"), so the browser uploader crashes on every batch. On devices still running the pre-#114 code it also silently committed notes it never inserted (block uid None). This adds roam_backend_api.py, a small client for Roam's backend API (q and write endpoints, peer redirect handling, retries), and roam_api_uploader.py, which builds the same Daily Notes > [[Go Note Go Notes]]: > session > notes structure as helper.js without a browser. The runner uses it whenever ROAM_API_TOKEN is configured and otherwise falls back to the browser uploader. Also: - settings.get() accepts a default for optional settings. - ROAM_API_TOKEN added to the settings template, masked in the settings server, and exposed in the settings UI. - requests is now a declared dependency. - conftest.py stubs the per-device secure_settings module so tests that import settings can run in CI. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Lets ':set ROAM_API_TOKEN <token>' on a running device take effect without a restart, and covers is_configured with a test. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
dbieber
force-pushed
the
claude/roam-backend-api
branch
from
September 5, 2026 12:22
3f03025 to
b2f5aa4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Roam uploads from the Pi 400s have been failing. Sign-in works, but the graph never loads in the headless Firefox: the loading astrolabe stays for 90+ seconds and the uploader crashes with
RuntimeError: Astrolabe still there after timeoutevery few minutes. An earlier screenshot from the same device shows why: Roam's hosting now serves a Vercel Security Checkpoint ("Failed to verify your browser, Code 99") to the old headless browser. The subscription is fine; the browser is being blocked.Worse, devices still on pre-#114 code ignored the failed graph load, "inserted" notes at block uid
None, and then committed them out of the queue. On one device 42 notes since Sep 1 were dropped that way (recovered from the process log).What
roam_backend_api.py: minimal client for Roam's backend API (qandwrite), following the official SDK: Bearer token headers, one-time 308 redirect to the graph's peer host, retries on 503 / connection errors, client-generated 9-char block uids,order: "last".roam_api_uploader.py: same output structure ashelper.js(Daily Notes page →[[Go Note Go Notes]]:→HH:MM AMsession → notes, with indent/unindent stack and audio embeds), but via HTTP. ReturnsFalseon API errors so the runner keeps notes queued.ROAM_API_TOKENis configured, otherwise falls back to the browser uploader with a hint.settings.get(key, default)for optional settings;ROAM_API_TOKENadded to the template, masked in the settings server, and shown in the settings UI.requestsdeclared as a dependency; rootconftest.pystubs the untrackedsecure_settingsmodule so settings-importing tests run in CI.Small intentional differences from the browser path: the Daily Notes page is chosen from the note's effective timestamp (same time already used for the session title since #145) rather than upload time, and an audio note with no blob storage configured no longer gets an empty
{{audio: }}child.Setup
In Roam: Settings → Graph → API tokens → new token with edit access. Then on the device:
:set ROAM_API_TOKEN <token>(or via the settings UI).Testing
pytest: 63 passed.api.roamresearch.comwith a bogus token: redirect topeer-24…:3001handled, then a clean "Token cannot be verified" error.🤖 Generated with Claude Code