Skip to content

test(driver-turso): measure whether @libsql/client honours ?authToken= from an authored URL - #8929

Merged
hotlong merged 1 commit into
mainfrom
claude/issue-8860-turso-authtoken-url-measurement
Aug 15, 2026
Merged

test(driver-turso): measure whether @libsql/client honours ?authToken= from an authored URL#8929
hotlong merged 1 commit into
mainfrom
claude/issue-8860-turso-authtoken-url-measurement

Conversation

@hotlong

Copy link
Copy Markdown
Contributor

Fixes#8860

A measurement card: the deliverable is four recorded answers, plus a driver-local pin so they cannot rot silently under a caret range. No behaviour changes.

The question

The driver hands the authored url / syncUrl to @libsql/client untouched and passes the credential as its own authToken config key. Whether a token inside the URL's query string is a second, equivalent credential channel had never been observed — only called plausible.

The four answers, measured on the wire

Measured end-to-end through TursoDriver's own construction path against an endpoint that rejects anything but the exact expected bearer value, so every negative is paired with a positive control on the same harness.

#QuestionPredictionMeasuredAgree?
1url + ?authToken= honoured?yesYES — becomes a real Authorization: Bearer …, request authenticates
2Precedence on url when both presentURL winsURL QUERY STRING WINS over the explicit authToken option
3syncUrl + ?authToken= in replica modenot honouredNOT HONOURED — credential goes out empty, and the query string corrupts the request path; precedence is inverted (the option wins)✅ + two unpredicted extras
4Resolved @libsql/client0.17.40.17.4 (@libsql/core@0.17.4, native libsql@0.5.29)

Predictions were written down before the first probe ran and are reported beside the measurements, including where they diverged.

The headline: the two paths disagree

  • A token in url is a live credential — and it silently overrides the one the caller passed explicitly.
  • A token in syncUrl is not a credential at all. The replica path never parses the query string: the credential goes out as a bare Authorization: Bearer with no value, and because the native layer builds its endpoint by concatenation, the authored query string lands in the path of every sync request (/?authToken=…/info).

Observed vs read code

Everything in the table is observed behaviour (HTTP requests recorded at a real endpoint). Reading the client's source afterwards explains it, and is labelled as such: @libsql/core's config expansion seeds authToken from the passed config and then assigns over it while looping the URL's query pairs (last write wins); the native replica constructor takes authToken only from opts.authToken and passes opts.syncUrl through verbatim.

What lands here

One test file, packages/drivers/driver-turso/src/turso-authtoken-url-channel.test.ts — 8 legs: 4 answers plus 4 controls.

The pin can genuinely fail. Verified by ablation rather than asserted: three mutations (remove the URL token; supply the replica token via the option instead; expect a version that is not installed) were predicted to go red on exactly their own legs and did — 3 failed / 5 passed — then restored from the commit.

Harness note

The embedded-replica createClient opens synchronously in native code and blocks the calling event loop for the whole initial sync, so an in-process endpoint can never answer it and the run deadlocks — which looks exactly like "the client made no request". An early draft hit precisely that and would have reported a false zero. The replica legs therefore spawn the endpoint as a child process.

Scope

⛔ No refusal, redaction, or schema change — that is the spec lane's scope. This PR records behaviour and changes none of it. #8337 is not addressed here and remains open; the four answers are posted to that thread, since it is the consumer waiting on them.

Per triage, this settles "is #8337 one defect or two?" and does not gate its refusal — the severity-not-precondition reading stands.

Test-only: no user-visible change, so no changeset (skip-changeset).

Verification

Union re-run after the final commit, at HEAD = 55fbaf150.

Generated by Claude Code


Generated by Claude Code

…om an authored URL
Measurement card: the connect-half of the authored-credential question had
never been observed, only called plausible. Measured end-to-end through
TursoDriver's own construction path against a token-enforcing endpoint, with a
positive control on every negative:
- url + ?authToken= -> HONOURED as the credential (Authorization: Bearer)
- url precedence -> the query string WINS over the explicit authToken
- syncUrl + ?authToken= -> NOT honoured; credential goes out empty AND the
query string corrupts the sync request path
- syncUrl precedence -> inverted: the explicit option wins
Resolved @libsql/client 0.17.4 (core 0.17.4, native libsql 0.5.29); the caret
range means the answer can move without a file edit, so the version is pinned
alongside the behaviour.
Test-only: records behaviour, changes none. Refusal/redaction of a
credential-bearing URL is a packages/spec concern and is deliberately not done
here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XeQRiAa7vYRVX5Fog7Zby8
@vercel

vercelBot commented Aug 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 15, 2026 6:14pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

No hand-written docs reference the 0 changed package(s). ✅

@hotlonghotlong added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 15, 2026 — with Claude
@hotlong
hotlong marked this pull request as ready for review August 15, 2026 20:23
@hotlong
hotlong added this pull request to the merge queueAug 15, 2026
Merged via the queue into main with commit 3315d1dAug 15, 2026
30 checks passed
@hotlong
hotlong deleted the claude/issue-8860-turso-authtoken-url-measurement branch August 15, 2026 20:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gatetests

Projects

None yet

2 participants

@hotlong@claude