Skip to content

DEVX-314: Hotfix Plaud Cloudflare 403 + remove stale .mcp.json (0.1.1) - #16

Merged
allenahner merged 7 commits into
mainfrom
allenahner/DEVX-314-plaud-ua-hotfix-and-mcp-cleanup
May 17, 2026
Merged

DEVX-314: Hotfix Plaud Cloudflare 403 + remove stale .mcp.json (0.1.1)#16
allenahner merged 7 commits into
mainfrom
allenahner/DEVX-314-plaud-ua-hotfix-and-mcp-cleanup

Conversation

@allenahner

@allenahner allenahner commented May 16, 2026

Copy link
Copy Markdown
Member

Summary

First tagged release of RootScribe (v0.1.1). De facto 0.1.0 was the unreleased baseline that the broken UA was identifying as; this hotfix is the patch release on top.

  • Plaud poller hotfix: swap USER_AGENT in server/src/plaud/client.ts to a Chrome string. The previous self-identifying UA (rootscribe/0.1.0 (+https://github.com/...)) started getting Cloudflare-challenged with a 403 on 2026-05-15, breaking the sync loop. Token was valid; Cloudflare blocked the request at the edge.
  • Structural UA lock: the user-agent header is now applied AFTER the init.headers spread so callers cannot override it back to a bot-pattern UA (addresses Copilot review feedback on the first revision).
  • Regression tests: client.test.ts now forbids any UA matching ^name/ver (+http...) on both the default-headers path AND the caller-override path. Intentionally stricter than pinning the Chrome string so DEVX-314's follow-up env-var work doesn't break it.
  • .mcp.json removal: the workspace-root .mcp.json was stale — the inbox MCP is now provided via the seedkit plugin's plugin-level .mcp.json. Removal is intentional.
  • CHANGELOG.md: new file documenting the 0.1.0 baseline (135 commits, 6 DEVX tickets, fork from rsteckler/applaud@v0.5.6) and this 0.1.1 hotfix. Format follows Keep a Changelog 1.1 + SemVer.
  • Version bumps: all five package.json files bumped from 0.1.0 to 0.1.1.

Background

Direct curl diagnostic on 2026-05-15:

User-Agent Status
rootscribe/0.1.0 (+url) 403 (Cloudflare HTML)
Chrome browser UA 200 (JSON)
curl/8.x default 200 (JSON)

The proper fix — Cloudflare-aware 403 error class (PlaudEdgeBlockedError) + ROOTSCRIBE_PLAUD_USER_AGENT env var — is scoped under DEVX-314 for a follow-up PR. This one is the hotfix + structural lock + changelog + version bump needed to ship v0.1.1.

Test plan

  • pnpm lint — clean
  • pnpm typecheck — clean across all 4 workspaces
  • pnpm test:coverage — 776+ unit tests pass; coverage above pinned thresholds (95.36% statements, 86.59% branches)
  • pnpm -w test -- --run plaud — 79/79 plaud tests pass, including the new caller-override regression
  • Manual curl against api.plaud.ai/file/simple/web with the new Chrome UA returns 200 JSON
  • CI e2e (Playwright) — runs on this PR

Notes

  • Branch follows CLAUDE.md naming: allenahner/DEVX-314-....
  • Used gh pr create --repo Root-Functional-Medicine/rootscribe explicitly to avoid the known gh upstream-fork bug (see CLAUDE.md → "Critical PR rule").
  • Pre-push guard (.githooks/pre-push) verified origin push targets Root-Functional-Medicine/rootscribe, not rsteckler/applaud.

🤖 Generated with Claude Code

Plaud's API sits behind Cloudflare bot protection. The self-identifying
User-Agent `rootscribe/0.1.0 (+https://github.com/...)` started getting
served a Cloudflare challenge page (HTTP 403) on 2026-05-15, breaking
the sync poller. Token was valid; the request never reached Plaud.

Confirmed via direct curl: bot-style UA → 403 Cloudflare HTML, browser
UA → 200 JSON, curl-default UA → 200 JSON. The `name/version (+url)`
shape is the trigger.

This commit:

* Swaps `USER_AGENT` in server/src/plaud/client.ts to a Chrome string.
* Encodes the lesson as a regression test in client.test.ts that
  forbids any UA matching `^name/ver (+http...)`.
* Removes the unused .mcp.json from the repo root — the inbox MCP is
  now provided via the seedkit plugin's plugin-level .mcp.json, so the
  workspace-root copy was stale.

A proper Cloudflare-aware 403 branch + configurable UA env var is
scoped under DEVX-314 for a follow-up PR. This commit is partial
progress on that ticket: the hotfix unblocks the poller for the 0.1.0
release; the longer-lived fix lands later.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 16, 2026 15:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hotfixes Plaud API requests by replacing the self-identifying User-Agent with a browser-style User-Agent to avoid Cloudflare 403 challenges, and removes a stale MCP workspace config.

Changes:

  • Updated Plaud client User-Agent to a Chrome browser string.
  • Adjusted the Plaud client test to reject bot-style User-Agent patterns.
  • Removed the root .mcp.json file.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
server/src/plaud/client.ts Sets the Plaud fetch User-Agent to a browser-style value.
server/src/plaud/client.test.ts Updates header regression coverage for the new User-Agent behavior.
.mcp.json Removes stale MCP server configuration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread server/src/plaud/client.test.ts
First proper changelog for RootScribe, covering everything since the
fork from rsteckler/applaud@v0.5.6 (commit da7ae11). 168 commits across
7 DEVX tickets plus the foundational inbox-MCP work.

Format follows Keep a Changelog 1.1 + SemVer. Grouped as Added /
Changed / Fixed / Removed with sub-sections by DEVX ticket so each
section is traceable to its ticket and PR history.

The [0.1.0] reference link points at the GitHub release-tag URL, which
will become live when v0.1.0 is pushed post-merge.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@allenahner allenahner self-assigned this May 16, 2026
PR #16 review feedback from Copilot: the previous merge order in
plaudFetch spread `init.headers` AFTER the default user-agent, so a
caller passing their own `user-agent` (including a bot-pattern UA)
would override the safe default — and the regression test added in
44f6243 only exercised the no-headers default path, so it would not
have caught such a caller.

Move `"user-agent": USER_AGENT` to AFTER the `...init.headers` spread
so the lock applies structurally regardless of caller input. Keep the
other defaults (accept, authorization) overridable per existing
behavior — only the UA needs to be locked.

Add a second regression test that calls plaudFetch with a bot-pattern
`user-agent` in `init.headers` and asserts the outgoing UA does NOT
match the bot pattern and does NOT contain "rootscribe/".

No current caller passes a UA in init.headers (verified by grep across
server/src/plaud/* and server/src/routes/auth.ts), so this is a
forward-looking guard rather than a behavior change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 16, 2026 15:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment thread server/src/plaud/client.ts Outdated
@allenahner allenahner changed the title DEVX-314: Hotfix Plaud Cloudflare 403 + remove stale .mcp.json (0.1.0) DEVX-314: Hotfix Plaud Cloudflare 403 + remove stale .mcp.json (0.1.1) May 16, 2026
The package version `0.1.0` was already baked into the broken
self-identifying UA (`rootscribe/0.1.0 (+url)`), so 0.1.0 is the
unreleased pre-Cloudflare baseline. This hotfix is the patch release
on top, per SemVer 2.0.0:

- Bump version from 0.1.0 to 0.1.1 across all five package.json files
  (root, server, web, inbox-mcp, shared).
- Split CHANGELOG.md: [0.1.1] section gets the Cloudflare hotfix +
  structural UA lock + .mcp.json removal + CHANGELOG.md itself.
  [0.1.0] section becomes the unreleased baseline documenting the
  135 commits across 6 DEVX tickets (DEVX-96/99/100/101/102/103)
  since the fork from rsteckler/applaud@v0.5.6.
- Update CHANGELOG reference links to point [0.1.1] at the tag URL
  and [0.1.0] at a fork-point→tag compare URL.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Comment thread server/src/plaud/client.ts Outdated
Comment thread server/src/plaud/client.test.ts
Comment thread inbox-mcp/package.json
Comment thread server/package.json
Comment thread CHANGELOG.md Outdated
allenahner and others added 2 commits May 16, 2026 12:10
PR #16 round-2 review feedback from Copilot: HTTP header names are
case-insensitive (RFC 7230 §3.2) but JS object keys are case-sensitive
strings. The previous "spread first, then lock" merge order left both
the caller's casing variant (e.g. "User-Agent") AND the locked
"user-agent" in the merged headers object — and fetch's Headers init
concatenates same-named entries, so the outgoing request would carry
the bot-pattern UA combined with the locked value (e.g.
"rootscribe/0.1.0 (+url), Mozilla/...").

Fix: strip ALL case variants of "user-agent" from `init.headers`
BEFORE the spread, then add the locked lowercase key. The lock is
now structural across every possible caller casing.

Add a third regression test that passes both "User-Agent" and
"USER-AGENT" with bot-pattern values and asserts:
1. Only the locked lowercase "user-agent" key remains in the
   outgoing headers (no case-variant leaks).
2. No header value anywhere in the merged object contains the
   "rootscribe/" or `name/ver (+http...)` pattern.

The new test plus the existing two regressions now cover three
attack paths: default-headers, lowercase-override, and case-variant
override.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PR #16 round-3 review feedback from Copilot: bumping the five
package.json files to 0.1.1 was incomplete — three runtime constants
plus one README doc string still hard-coded "0.1.0":

- inbox-mcp/src/index.ts:30 — MCP server `version` metadata exposed
  to clients (now "0.1.1")
- server/src/webhook/post.ts:102 and :184 — outbound webhook
  User-Agent header on both prod and test paths (now "rootscribe/0.1.1")
- README.md:112 — documented webhook UA (now "rootscribe/0.1.1")

Without these bumps, 0.1.1 would have shipped advertising itself as
0.1.0 on every webhook, MCP handshake, and in the README. The
structurally correct fix is to derive these values from package.json
at runtime/build time — that's scoped under DEVX-314 follow-up. A
comment in inbox-mcp/src/index.ts marks the source-of-truth invariant.

Also fix the CHANGELOG.md `[0.1.0]` reference link Copilot flagged:
it previously pointed at `da7ae11...v0.1.1` which INCLUDED the
hotfix commits, contradicting the section text. Since 0.1.0 was
never tagged, the cleanest fix is to drop the link entirely and
leave HTML comments explaining why.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 16, 2026 16:13

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Comment thread CHANGELOG.md Outdated
PR #16 round-4 review feedback from Copilot: the CHANGELOG [0.1.1]
section said "Two regression tests ... default-headers path AND
caller-override path" but the PR actually added three tests — the
case-variant override test landed in e9abdbe and was not reflected
in the release notes I wrote earlier.

Update the bullet to list all three covered paths explicitly:
default-headers, lowercase caller-override, and case-variant
caller-override (User-Agent / USER-AGENT). Keeps the release notes
and test file in sync within the same PR that added the coverage.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

@allenahner
allenahner merged commit 0c3e08e into main May 17, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants