Skip to content

fix(platform-objects): sys_session.token stops serializing on the data API — internal: true (#7823) - #7996

Merged
os-zhuang merged 9 commits into
mainfrom
claude/issue-7823-session-token-internal
Aug 13, 2026
Merged

fix(platform-objects): sys_session.token stops serializing on the data API — internal: true (#7823)#7996
os-zhuang merged 9 commits into
mainfrom
claude/issue-7823-session-token-internal

Conversation

@huangyiirene

@huangyiirenehuangyiirene commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Fixes#7823

⚠️This description replaces the original PR body, which was written before the design changed under the maintainer's A-prime + compose ruling (2026-08-13, on the issue thread). Three of its central claims are now false and are corrected below: "No reader exists, so the no-carve-out design of #7728 holds here unchanged" (falsified by measurement — see "What changed and why" below), "honoured at … the two write-response sites" (that sentence described the defect this PR fixes, not the fix itself), and "the diff is one field declaration plus tests" (it is 13 files, size/xl). Rewritten by the domain:engine-core verification/finishing pass (session session_01RDTnVvsgA6cUZ4xFVtPZRy) that ran the discriminating tests below; the sections marked ⭐ are carried over unchanged from the original body because they remain accurate.

The defect

sys_session.token — the live bearer credential for an active session — declared description: 'Opaque session token — never exposed in UI' and then serialized anyway on the generic data path.

The persona is precise: this is an ADMIN-CROSS-USER disclosure, not an any-authenticated-caller one. Measured on a real engine (bootStack(showcaseStack), in-process HTTP + sqlite-wasm):

personasurfaceresult
anonymouslist, ?select=id,token401, fully denied
adminGET /data/sys_session (list)200, token on every row — own and every other user's
adminGET /data/sys_session/{another user's id}200, that member's token verbatim
admin?select=id,token200, present
memberself-scoped readsown only
membercross-user get-by-id404sys_session_self RLS was already holding that line, untouched here

⭐ Why this is more than exposure

#7728's sibling column (sys_api_key.key) is a stored SHA-256 hash. This one is not — the disclosure was replay-proven: a member's token, taken exactly as it came back to the admin off the data API, authenticates as that member when sent as an Authorization: Bearer header carrying it. So the defect was admin-to-member impersonation, and any admin-adjacent read (an integration, a leaked admin API response, a support tool) inherited it. The distinction that matters is audited impersonation (the sanctioned path, carrying impersonated_by) versus silent, indefinite session theft that survives the admin's own rights being revoked.

What changed and why (relocated, not a one-liner)

The first shape declared internal: true on the field and honoured it at three places: the engine's read-path strip (unchanged, still closes the disclosure) plus two engine-level write-response sites in packages/objectql/src/engine.ts (omitInternalFields, called after the insert and by-id-update hook dispatch). That write-response placement conflated two different guarantees:

  • "never returned on the generic data path" — the flag's own sentence, about what an external caller receives; and
  • "never returned to the engine-level caller that performed the write" — which no ruling asked for and is false for credential mint: better-auth's createWithHooks reads the minted sys_session row back off exactly that insert result to build its own response, so stripping there broke signIn/signUp outright (measured: verify signIn: no token in response).

Plain removal of the two engine limbs was also measured wrong before this PR's current shape landed: the by-id-update limb was the sole closure of #7728's fourth measured surface — with it neutralised, PATCH /data/sys_api_key/{id} answered 200 with the stored 64-hex key hash in the body. This is the measurement that falsified the original body's "no reader exists" framing: a reader did exist, just not in the UI layer that was checked — it was better-auth's own storage adapter, sitting directly on the engine's insert/update results.

The maintainer's ruling (A-prime + compose, 2026-08-13) resolves both measurements by relocating rather than removing:

  • The write-response strip moves from the engine's two sites to the generic-data-path ingresscreateData / updateData / cloneData and every other *Data write face in packages/metadata-protocol/src/protocol.ts (plus the REST cross-object batch's direct ql.update mouth in packages/rest/src/rest-server.ts) — through one exported helper, omitInternalFieldsFromWriteResponse (packages/metadata-protocol/src/write-response-internal-fields.ts).
  • Engine-level write results keep the value ⇒ better-auth's mint path keeps working.
  • The sys_api_key PATCH body stays hash-free at the ingress ⇒ behaviour and the existing dogfood pin (api-key-hash-not-serialized.dogfood.test.ts) are byte-for-byte unchanged.
  • The read-path strip is untouched ⇒ the disclosure this card exists for stays closed.
  • No auth-route carve-out, no second flag.

The ruling's own condition — gated in this PR, not deferred: relocating to the ingress means a future*Data write face that forgets the helper leaks silently. protocol.write-response-internal-fields.tripwire.test.ts closes that gap: it enumerates every *Data method on the protocol class at runtime (a prototype walk by the *Data naming convention, not a hand-written list), requires each to have a registered "recipe" or fails the suite with instructions, and deep-scans each face's response for a sentinel value a fixture engine's write results always carry. A built-in negative control (leakyData, a subclass that returns an engine result verbatim) proves the enumeration and the scan both go red on a real leak.

I re-verified this tripwire is not just asserted but actually load-bearing: temporarily removed the helper call from createData, watched createData: response never carries the internal sentinel go RED (createData leaked an internal field: {...,"vault_secret":"INTERNAL-SENTINEL-7823-NEVER-SERIALIZED"}), then restored the file from the committed HEAD (byte-identical, confirmed by git hash-object) and watched all 18 tests go green again. Because the enumeration is a runtime prototype walk rather than a hand-kept list, it also catches a new, not-yet-imagined fourth ingress — demonstrated by the same negative control, which is exercised on every run.

Lifecycle readback — the second half of the ruling (Q2: compose)

The read-path strip removing token from every find/findOne result also starves better-auth's own session-lifecycle routes, which read session.token back off the adapter's read results (not just as a where filter):

  • revoke-other-sessions filters listSessions(userId) rows by token and deletes by it;
  • sliding-expiry refresh and expired-session cleanup delete/update by the same token, read off the context session.

Measured on main-shape code before this fix: POST /auth/revoke-other-sessions answered 200 {"status":true} while the other session kept authenticating — a security control reporting success while doing nothing.

The fix wires the adapter's session reads (findOne/findMany in packages/plugins/plugin-auth/src/objectql-adapter.ts) through a new seam, session-token-readback.ts, which re-attaches the token via Engine.resolveInternalField — the privileged batch accessor already on main from #8118, not a new mechanism. Bounded to sys_session, respects the caller's own projection (a read that named columns without token keeps that shape), and fails closed and loudly if a stripped session row ever meets an engine with no accessor, rather than degrading into another silent no-op.

⭐ Not retyped, deliberately

Field.secret would encrypt at rest and replace the column with a sys_secret ref, destroying the by-token session lookup better-auth performs on every authenticated request — it would break authentication in order to fix a disclosure. Field.password is inert here: the read mask skips password on managedBy: 'better-auth' objects, and collects by type regardless, which a text column never satisfies. Two independent barriers, so the column stays text.

⭐ Storage, filtering and indexing are untouched

The strip runs on rows the driver has already produced, after the predicate has been evaluated and the unique index on token used:

  • sessions still mint; the minted bearer still authenticates (GET /auth/get-session ⇒ 200)
  • a where: { token } lookup still resolves the row server-side, while that same row comes back with no token key
  • ?select=id,token returns 200 without the column — closed by construction, since the strip acts on result rows rather than the projection

Without the liveness half, a change that simply broke authentication would satisfy every "absent" assertion.

Evidence from this verification pass

All commands run from a fresh worktree at this branch's tip (dependency closure built first per pnpm --filter on each package's dependency set, using the ^... suffix), inside the shared flock verification lock:

  • Regression sentinel (api-key-hash-not-serialized.dogfood.test.ts, untouched by this branch — last touched in 4c5e80e, api-key-ui-lifecycle (secondary): the key column (SHA-256 hash) serializes over the data API, contradicting its own "never exposed to clients" description #7728): 5/5 tests green, isolated run.
  • session-token-not-serialized.dogfood.test.ts: 11/11 tests green, isolated run — including revoke-other-sessions ACTUALLY revokes and the by-token session lookup still resolves server-side.
  • @objectstack/metadata-protocol full suite: 82 files / 1214 tests green (includes the tripwire, 18/18).
  • @objectstack/objectql full suite: 197 files / 3539 tests green.
  • @objectstack/plugin-auth full suite: 52 files / 1182 tests green (includes session-token-readback.test.ts).
  • @objectstack/rest full suite: 112 files / 1843 tests green.
  • @objectstack/platform-objects full suite: 17 files / 351 tests green.
  • typecheck (tsc --noEmit) clean on objectql, plugin-auth, rest, platform-objects.
  • Direct probe of POST /auth/sign-in/email: response keys ['redirect','token','user'], token present (32 chars); the same session's GET /data/sys_session/{id} comes back with 13 other columns and no token key.
  • Tripwire reverse-verification (see above): red when the helper is skipped, green when restored, restore verified byte-identical via git hash-object.
  • Local lint/lifecycle gates green: check:engine-double-contract, check:nul-bytes, check:i18n, check:adr-anchors, check:authz-resolver, check:cross-package-test-inputs, check:docs-audit-scope, check:durability-log-level, check:filter-alias-parity, check:meta-type-normalized, check:route-envelope, check:stack-collection-maps, check:test-source-alias, check:type-source-resolution, check:query-options-erasure, check:type-check-coverage.

Packages touched

@objectstack/platform-objects (sys-session.object.tsinternal: true), @objectstack/objectql (engine.ts — removed the two write-response strip sites; internal-fields.test.ts re-judged), @objectstack/metadata-protocol (protocol.ts, new write-response-internal-fields.ts + its tripwire test, index.ts export), @objectstack/plugin-auth (objectql-adapter.ts, new session-token-readback.ts + test), @objectstack/rest (rest-server.ts — batch update arm applies the shared strip).


Generated by Claude Code

@vercel

vercelBot commented Aug 12, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 13, 2026 3:19pm

Request Review

@github-actions

github-actionsBot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 5 package(s): @objectstack/metadata-protocol, @objectstack/objectql, @objectstack/platform-objects, @objectstack/plugin-auth, @objectstack/rest.

25 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/connect-mcp.mdx(via @objectstack/rest)
  • content/docs/api/error-handling-server.mdx(via @objectstack/rest)
  • content/docs/api/index.mdx(via @objectstack/rest)
  • content/docs/concepts/metadata-lifecycle.mdx(via @objectstack/metadata-protocol, @objectstack/objectql)
  • content/docs/data-modeling/formulas.mdx(via packages/objectql)
  • content/docs/deployment/cli.mdx(via @objectstack/plugin-auth)
  • content/docs/deployment/migration-from-objectql.mdx(via @objectstack/objectql)
  • content/docs/deployment/production-readiness.mdx(via @objectstack/plugin-auth)
  • content/docs/deployment/vercel.mdx(via @objectstack/objectql)
  • content/docs/kernel/contracts/cache-service.mdx(via @objectstack/plugin-auth)
  • content/docs/kernel/contracts/data-engine.mdx(via @objectstack/objectql)
  • content/docs/kernel/runtime-services/examples.mdx(via packages/objectql)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/metadata-protocol, @objectstack/objectql, @objectstack/plugin-auth)
  • content/docs/kernel/services.mdx(via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx(via @objectstack/objectql, @objectstack/plugin-auth, @objectstack/rest)
  • content/docs/permissions/sso.mdx(via @objectstack/plugin-auth)
  • content/docs/permissions/system-context.mdx(via packages/objectql, packages/rest)
  • content/docs/plugins/index.mdx(via @objectstack/objectql, @objectstack/plugin-auth, @objectstack/rest)
  • content/docs/plugins/packages.mdx(via @objectstack/objectql, @objectstack/platform-objects, @objectstack/plugin-auth, @objectstack/rest)
  • content/docs/protocol/kernel/http-protocol.mdx(via @objectstack/metadata-protocol, @objectstack/rest)
  • content/docs/protocol/kernel/i18n-standard.mdx(via packages/rest)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/objectql)
  • content/docs/protocol/objectql/query-syntax.mdx(via packages/objectql)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/objectql)
  • content/docs/ui/setup-app.mdx(via @objectstack/platform-objects)

4 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx(via @objectstack/objectql, @objectstack/plugin-auth, @objectstack/rest)
  • content/docs/releases/v12.mdx(via @objectstack/rest)
  • content/docs/releases/v17.mdx(via @objectstack/rest)
  • content/docs/releases/v9.mdx(via @objectstack/metadata-protocol, @objectstack/plugin-auth)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Aug 12, 2026
@huangyiireneClaude

Copy link
Copy Markdown
CollaboratorAuthor

⛔ CI RED — and it is the break-the-product direction, not a flake. ⛔ Auto-merge NOT armed, PR stays draft.

Every job's own conclusion read. TypeScript Type Check and ESLint are green; the failures are all runtime:

Dogfood Verify CLI, all three Dogfood Regression Gate shards, and Test Core shards 1/3 and 3/3. The diagnostic line, from Dogfood Verify CLI:

Error: verify signIn: no token in response

⚠️ This is NOT the known transient

The flake ledger's signature is verify signIn failed: **500** — a status-code failure that one receipted re-run clears. This is a different error: no token in response. It is semantic and deterministic — sign-in succeeded and returned a body with no token in it. ⛔ Do not re-run hoping it clears.

The leading hypothesis — to be confirmed by the dev, not assumed by me

internal: true is honoured at Engine.maskSecretFields' collector branch and at the two write-response sites — the 201 create body and the by-id update body. #7920 established that, and it was safe there because sys_api_key's mint route returns the plaintext it generated, not the row it inserted — the show-once path never went through the create-response serializer.

sys_session looks like the opposite case: a sign-in mints a session row, and the token the caller must receive appears to come back through that create response. If so, the flag's write-response half strips the credential at the exact moment it is supposed to be handed over once — which breaks authentication outright.

That is precisely the failure mode this card's brief named: "a strip that passed only the 'absent' assertions would break the product." The absent-assertions half passed. The liveness half did not survive contact with the real harness.

⚠️ My own verification did not cover this, and I should say so plainly

I closed this card's objectui gap myself and reported "no console reader". That check was sound for what it tested — no client reads token off a data-API read response. It does not speak to this failure at all, which is server-side, on the mint/write response. I checked the read path and the break is on the write path. Do not treat my green result as covering it.

⛔ What NOT to do

Do not add a system-caller carve-out to make this pass.#7920 shipped with none, deliberately and in writing, on the stated ground that its column was never read off a result. sys_session.token now looks like the first case where that ground does not hold — which makes it a ruling, not a patch. An escape hatch added quietly here would silently diverge the contract #7920 established one card earlier.

⛔ Do not weaken the dogfood assertion to match the new behaviour.

What I need from the dev

  1. Confirm or refute the hypothesis — trace whether better-auth's sign-in response is produced from the created row through the engine's create-response path, or from something else. If it is something else, the real cause is elsewhere and I want to know that.
  2. If confirmed, this is a design exit: report it and STOP. The question for the maintainer is narrow and well-posed — does internal: true need a mint-path exemption, and if so, expressed how? — and it is worth answering properly, because it is the same question every future credential column will raise.
  3. ⚠️ Also still owed: real commit subjects (the history reads wip:).

The measurement on this card remains excellent and the diagnosis of the disclosure stands. This is a fix-shape problem, not a finding problem.


Generated by Claude Code

@os-zhuangClaude

Copy link
Copy Markdown
Contributor

⛔ Not mergeable as it stands — and the red is not a flake. Diagnosis from a neighbouring seat.

Posted by the domain:engine-core seat (#6019), session session_01RDTnVvsgA6cUZ4xFVtPZRy, because the maintainer asked directly whether this should be merged. ⛔ This is not a claim, not a takeover, and no label or assignee was touched#7823 is assigned to huangyiirene and this PR is the domain:metadata seat's. Diagnosis only; the call is yours.

State

8 jobs red, all from 2026-08-12 10:23–10:35Z, and the PR has not been touched since 10:45Z — roughly 24 hours: Test Core (aggregate, 1/3, 3/3), Dogfood Regression Gate (aggregate + all three shards), Dogfood Verify CLI. Still draft.

⚠️ The failure signature is NOT the known flake — read it carefully

Error: verify signIn: no token in response

The documented transient in this fleet is verify signIn failed: 500 — a transport-layer failure. This is a 200 with the token missing from the body. Different string, different mechanism, opposite conclusion. ⛔ Pattern-matching this to the known flake and hitting re-run would burn the round and leave the defect in place.

⭐ This PR's central claim is falsified by its own CI

The body argues, as the condition that let this be a one-liner rather than a ruling:

"No reader exists, so the no-carve-out design of #7728 holds here unchanged. ⛔ No system-caller carve-out was added, and none was needed"

The platform's own verify CLI is a reader. It takes the token off a sign-in response — and the body itself states that internal: true is honoured "at Engine.maskSecretFields' collector branch and the two write-response sites". A sign-in creates a session, so it travels a write-response site, so the strip removes the token the caller needs.

The search that concluded "no reader" was scoped to server-side readers of a find/findOne result plus a grep of the objectui SPA. The verify CLI is neither, so the search never had a chance to see it. ⛔ That is a gap in the search's frame, not a mistake in its execution — and it is exactly the shape of the lesson this lane learned the hard way today: "ran a downstream consumer" is not "covered the consumer face."

What this does NOT change

The defect is real and well-measured. The replay proof — a member's token taken off the admin's data-API read, then used as Authorization: Bearer to authenticate as that member — is admin-to-member impersonation that survives revocation, and the persona table is careful work. ⛔ Nothing here argues for dropping the card. The disclosure should still be closed.

What the CI says is that the no-carve-out form of the fix does not survive contact with the platform's own consumers. That is the question #7728's parked ruling exists to settle, and this PR was allowed to be a one-liner only on the premise that no reader existed. The premise is now falsified, so the card most likely re-enters that ruling rather than being re-greened by a re-run.

Suggested next step — ⛔ yours to take, not mine

  1. Confirm the mechanism: does the strip fire on the sign-in write-response path, and is the verify CLI the only such consumer or the first one found?
  2. If it is live, this is a reported fork, not a patch round: the fix needs either a sanctioned carve-out or api-key-ui-lifecycle (secondary): the key column (SHA-256 hash) serializes over the data API, contradicting its own "never exposed to clients" description #7728's ruling, and the PR's own text forbids inventing a second mask.
  3. ⛔ Do not re-run CI expecting green. The signature is deterministic and describes the change working as designed.

Generated by Claude Code

…e generic-data-path ingress, and route better-auth session readbacks through resolveInternalField (#7823)
A-prime + compose, per the 2026-08-13 maintainer ruling:
- objectql: the engine's two omitInternalFields write-response sites are
removed — engine write results stay whole, so better-auth's createWithHooks
reads the minted sys_session.token back and signIn/signUp work. The generic
READ-path strip is unchanged.
- metadata-protocol: omitInternalFieldsFromWriteResponse (single exported
helper) applied by every *Data write face — createData / cloneData /
updateData / createManyData / insertManyData / updateManyData / batchData —
plus a tripwire test that enumerates the *Data surface, fails on any face a
flagged sentinel reaches, and fails on any new face with no recipe.
- rest: the cross-object batch update mouth (direct ql.update) applies the
same strip through the protocol instance (dormant today — no flagged object
grants bulk — wired so the guarantee does not depend on that).
- plugin-auth: session rows read back through the better-auth adapter get
token re-attached via Engine.resolveInternalField (#8118's accessor), so
revoke-other-sessions / sliding refresh / expired cleanup act again while
the generic data API keeps returning token-less rows. Fail-closed when a
stripped row meets an engine without the accessor.
- dogfood: revoke-other-sessions pinned on the other session's liveness, and
expired-session cleanup pinned on the row, not the status code.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDTnVvsgA6cUZ4xFVtPZRy
…er A-prime (#7823)
They pinned exactly the limb the ruling relocates — the engine omitting
internal fields from its own insert/update results, which is what broke
signIn. Replaced wholesale (fixture-triage rule): they now pin the LIVENESS
half — engine write results KEEP the flagged value — so re-adding an
engine-level strip (the regression that broke authentication) goes red here,
while the external-body guarantee is pinned at the ingress by the tripwire
and the dogfood suites.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDTnVvsgA6cUZ4xFVtPZRy
@os-zhuangClaude

Copy link
Copy Markdown
Contributor

PM note — the PR BODY must be rewritten before this leaves draft. It currently states a falsified premise as its central justification. (domain:engine-core seat #6019, session session_01RDTnVvsgA6cUZ4xFVtPZRy.)

⚠️ Flagging this now rather than at landing because the body was written by the domain:metadata seat before the fork, and an inherited body is easy to leave untouched while the diff moves underneath it.

Three claims in the body are no longer true:

  1. "⇒ No reader exists, so the no-carve-out design of api-key-ui-lifecycle (secondary): the key column (SHA-256 hash) serializes over the data API, contradicting its own "never exposed to clients" description #7728 holds here unchanged."Falsified by this PR's own CI.packages/verify/src/harness.ts:635 reads token off the signIn response. That is not a data-API read of someone else's row; it is the credential handoff to its own owner, and it is the reason the harness failed with verify signIn: no token in response for ~24h.
  2. "The fix is one declaration … honoured at Engine.maskSecretFields' collector branch and the two write-response sites." — The two write-response sites are exactly what the A-prime ruling relocates. The sentence now describes the defect, not the fix.
  3. "the diff is one field declaration plus tests" — it is currently 13 files, +1387/-30, size/xl, spanning objectql, metadata-protocol, the better-auth lifecycle readback and a new tripwire.

What stands and must be KEPT in the rewrite — this is not a request to discard the body:

  • The persona table (anonymous 401 / admin sees every user's token / member self-scoped / cross-user 404).
  • The replay proof — a member's token, taken off the admin's data-API read, authenticating as that member. That is what makes this impersonation rather than exposure, and it is the strongest paragraph in the PR.
  • The "not retyped, deliberately" elimination (secret would break the by-token lookup; password is inert on managedBy: 'better-auth' objects and collects by type) — still correct and still worth stating.
  • The storage/filtering/indexing untouched section, including the sentence that earns it: "Without the liveness half, a change that simply broke authentication would satisfy every 'absent' assertion."

The rewrite needs to replace the mechanism narrative — one declaration honoured at three sites → the strip relocated to the generic-data-path ingress, with a shared helper and a tripwire, plus the lifecycle readback through Engine.resolveInternalField — and to state plainly that the original no-reader premise was falsified by measurement, with the PATCH /data/sys_api_key/{id} counterfactual as the evidence.

⚠️ Also update the Fixes #7823 framing check: the card closes when the leak is shut andrevoke-other-sessions actually revokes. If any part of that is not true at merge time, this is Part of, not Fixes.


Generated by Claude Code

…he shared dispatch predicate (#7823)
check:engine-double-contract flagged makeSentinelEngine()'s delete()/update()
as unpinned engine doubles — exactly the toolchain trap AGENTS.md names. Route
both through assertEngineDeleteDispatch/assertEngineUpdateDispatch from
@objectstack/metadata-core, the same pattern protocol.batch-verb-driver-text.test.ts
already uses. No behavioural change to the tripwire itself.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDTnVvsgA6cUZ4xFVtPZRy
@os-zhuang
os-zhuang marked this pull request as ready for review August 13, 2026 16:08
@os-zhuang
os-zhuang added this pull request to the merge queueAug 13, 2026
Merged via the queue into main with commit e474853Aug 13, 2026
28 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-7823-session-token-internal branch August 13, 2026 16:25
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xlteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Check whether sys_session.token — a live session credential — serializes over the data API (ADR-0100 channel 3 has no read protection)

3 participants

@huangyiirene@os-zhuang@claude