Skip to content

SDK: auth.broadcast is not deprecated, and a guard so it is not misread again - #1378

Open
feruzm wants to merge 1 commit into
developfrom
fix/sdk-custom-broadcast-doc
Open

SDK: auth.broadcast is not deprecated, and a guard so it is not misread again#1378
feruzm wants to merge 1 commit into
developfrom
fix/sdk-custom-broadcast-doc

Conversation

@feruzm

@feruzm feruzm commented Aug 6, 2026

Copy link
Copy Markdown
Member

You asked whether we could delete AuthContext.broadcast now that #1376 migrated the four mutations off it. We can't, and the reason is more useful than the answer.

What I checked

Mobile is already fully V2. useAuthContext() returns { adapter, enableFallback } and never sets broadcast. Nothing in vision-mobile reads it.

The web app has two live callers, and neither is legacy:

  • use-login-by-key.ts:34 grants posting permission during login, before the user exists to the adapter, so the key comes from a ref.
  • wallet-operations-sign.tsx:57 dispatches on a signing method the user picks mid-flow, which the adapter has no way to know about.

Both are the custom-broadcaster case, which useBroadcastMutation supports as case 'custom' — the last link of the default fallback chain ['key', 'hiveauth', 'hivesigner', 'keychain', 'custom']. It is a feature, not a leftover.

So the @deprecated tag was wrong, and it plausibly caused the bug

It read:

@deprecated Use platform adapter's broadcastWithKeychain/broadcastWithHiveAuth instead.

Which parses as "this is the old way to broadcast". Four mutations duly treated it as the keychain path: they checked auth?.broadcast and threw when it was absent. Every field on AuthContext is optional, so AuthContextV2 satisfies it structurally, the checks compiled everywhere, and each site failed only when a real user arrived. That is how four accumulated before one became a Sentry issue.

The doc now says what the field is actually for, names both legitimate callers so the next reader can tell their case apart, and says plainly never to use it to detect Keychain.

The guard

A syntax scan, because the type system cannot express "optional, but not the thing you want". It reads the AST, not the text, because auth.broadcast now appears in prose throughout these files and a text search reports the comments explaining why not to use it.

Sanctioned readers are listed with reasons:

  • use-broadcast-mutation.ts owns case 'custom'
  • broadcast-json.ts keeps it as the first branch of its own chain so V1 callers still work

Anything else fails, with the offending file named. Mutation-checked by reintroducing the exact bug in use-account-revoke-posting.ts, which the scan reports by name.

When it can actually be deleted

When those two web call sites have another way to supply signing. Until then, deleting the field would break login-by-key and the wallet signer. Worth revisiting if the adapter ever grows a "sign with this key I am handing you" method.

681 tests pass, 48 files. Typecheck clean.

Summary by CodeRabbit

  • Documentation

    • Clarified guidance for the optional authentication broadcaster used with custom signing.
    • Documented supported use cases and removed outdated recommendations.
  • Tests

    • Added safeguards to ensure authentication broadcaster access remains limited to approved SDK modules.
    • Added coverage for detecting valid usage while ignoring comments and test files.

…ad again

Checked whether AuthContext.broadcast could be deleted now that #1376 migrated
the four mutations off it. It cannot, and the reason matters more than the
answer.

Mobile is already fully V2: useAuthContext returns { adapter, enableFallback }
and never sets broadcast. But the web app has two live callers, and neither is
legacy:

- use-login-by-key grants posting permission DURING login, before the user
  exists to the adapter, so the key comes from a ref.
- wallet-operations-sign dispatches on a signing method the user picks mid-flow,
  which the adapter has no way to know about.

Both are the custom-broadcaster case, which useBroadcastMutation supports as
case 'custom', the last link of the default fallback chain. It is a feature.

So the @deprecated tag was wrong, and plausibly the cause. It said to use
broadcastWithKeychain instead, which reads as "this is the old way to
broadcast", and four mutations duly treated it as the keychain path: they
checked auth?.broadcast and threw when it was absent. Every field on AuthContext
is optional, so AuthContextV2 satisfies it structurally and the checks compiled
everywhere; each failed only when a real user arrived. The doc now says what the
field is for, names both legitimate callers, and says plainly never to use it to
detect Keychain.

The guard is a syntax scan, since the type system cannot express this. It reads
the AST rather than the text because auth.broadcast now appears in prose in
these files and a text search reports the comments explaining why not to use it.
Sanctioned readers are listed with reasons; anything else fails.

Confirmed by reintroducing the exact bug in use-account-revoke-posting: the scan
names the file.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The SDK documents AuthContext.broadcast as a supported custom-signing broadcaster. A Vitest AST guard scans SDK TypeScript modules and rejects unauthorized auth.broadcast reads while ignoring comments and spec files.

Changes

Custom broadcast controls

Layer / File(s) Summary
Broadcast contract documentation
packages/sdk/src/modules/core/types/auth.ts
Documents AuthContext.broadcast as an optional custom-signing path, its supported call sites, structural compatibility, and distinction from Keychain detection.
Broadcast usage guard
packages/sdk/src/modules/core/custom-broadcast-usage.spec.ts
Recursively discovers TypeScript modules, detects auth.broadcast property reads through the AST, applies sanctioned exceptions, and tests module coverage and comment handling.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Poem

I’m a rabbit guarding broadcast bytes,
With AST eyes and careful gates.
Comments may hop safely by,
Sanctioned readers pass the sky.
Clear docs guide each signing flight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the documentation correction and the AST guard added to prevent unauthorized reads of auth.broadcast.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sdk-custom-broadcast-doc

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/sdk/src/modules/core/custom-broadcast-usage.spec.ts (1)

74-77: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace the repository-size threshold with stable assertions.

files.length > 50 depends on the current module count. Normal cleanup can fail the test, and an incorrect directory with more than 50 files can still pass. Assert that both sanctioned files are discovered instead.

Proposed test change
-    expect(files.length).toBeGreaterThan(50);
+    expect(files).toContain(
+      join(MODULES, "core/mutations/use-broadcast-mutation.ts")
+    );
+    expect(files).toContain(
+      join(MODULES, "core/mutations/broadcast-json.ts")
+    );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sdk/src/modules/core/custom-broadcast-usage.spec.ts` around lines 74
- 77, Replace the files.length threshold assertion in the “finds the modules to
scan” test with stable assertions that verify both sanctioned files are present
in files, using their established filenames or symbols. Preserve the test’s
purpose of confirming the intended directory is scanned without relying on the
repository’s total module count.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/sdk/src/modules/core/custom-broadcast-usage.spec.ts`:
- Around line 57-65: Extend the AST detector in the visit function to flag every
read of AuthContext.broadcast, including string-literal element access such as
auth["broadcast"] and broadcast property bindings in object destructuring.
Normalize parenthesized and type-asserted expressions before checking the
receiver so (auth as AuthContext).broadcast and (<AuthContext>auth).broadcast
are covered, while preserving the existing direct and optional property-access
detection.

In `@packages/sdk/src/modules/core/types/auth.ts`:
- Around line 30-31: Update the contract documentation near the caller-supplied
broadcaster declaration to use the complete wording “A caller-supplied
broadcaster for signing operations that the platform adapter cannot perform.”

---

Nitpick comments:
In `@packages/sdk/src/modules/core/custom-broadcast-usage.spec.ts`:
- Around line 74-77: Replace the files.length threshold assertion in the “finds
the modules to scan” test with stable assertions that verify both sanctioned
files are present in files, using their established filenames or symbols.
Preserve the test’s purpose of confirming the intended directory is scanned
without relying on the repository’s total module count.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4d7d1b7c-b9e4-4a63-99a1-ed55dce8ae17

📥 Commits

Reviewing files that changed from the base of the PR and between 9863230 and 3ce8c5c.

📒 Files selected for processing (2)
  • packages/sdk/src/modules/core/custom-broadcast-usage.spec.ts
  • packages/sdk/src/modules/core/types/auth.ts

Comment on lines +57 to +65
const visit = (node: ts.Node): void => {
if (
ts.isPropertyAccessExpression(node) &&
node.name.text === "broadcast" &&
/^auth$/i.test(node.expression.getText(sf).replace(/[?!]/g, ""))
) {
found = true;
}
ts.forEachChild(node, visit);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -nP \
  -e "auth[[:space:]]*\\[[[:space:]]*['\"]broadcast" \
  -e "auth[[:space:]]+as[[:space:]].*\\.[[:space:]]*broadcast" \
  -e "\\([[:space:]]*auth[[:space:]]*\\)[[:space:]]*\\.[[:space:]]*broadcast" \
  -e "\\{[[:space:]]*broadcast[[:space:]]*\\}[[:space:]]*=[[:space:]]*auth\\b" \
  packages/sdk/src/modules --glob '*.ts' --glob '*.tsx' || true

Repository: ecency/vision-web

Length of output: 155


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

sed -n '1,180p' packages/sdk/src/modules/core/custom-broadcast-usage.spec.ts

printf '\n--- related files ---\n'
rg -n "custom-broadcast|custom.*broadcast|broadcast" packages/sdk/src -g '*.ts' -g '*.tsx' | head -80

Repository: ecency/vision-web

Length of output: 13574


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

node - <<'JS'
const ts = require("typescript");
const source = `
auth.broadcast
auth?.broadcast
auth["broadcast"]
auth['broadcast']
(auth as AuthContext).broadcast
(<AuthContext>auth).broadcast
const { broadcast } = auth
const [{ broadcast }] = [auth]
`;
const sf = ts.createSourceFile("<test.ts>", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
function match(node) {
  const matches = ts.isPropertyAccessExpression(node)
    && node.name.text === "broadcast"
    && /^auth$/i.test(node.expression.getText(sf).replace(/[?!]/g, ""));
  if (matches) {
    console.log(node.kind, ts.SyntaxKind[node.kind], node.name.text, JSON.stringify(node.expression.getText(sf)));
  }
  ts.forEachChild(node, visit);
}
function visit(node) {
  match(node);
}
visit(sf);
JS

Repository: ecency/vision-web

Length of output: 247


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

node - <<'JS'
const ts = require("typescript");

const cases = [
  ["dot", "auth.broadcast"],
  ["optional-dot", "auth?.broadcast"],
  ["string-index", 'auth["broadcast"]'],
  ["cast-dot", "(auth as AuthContext).broadcast"],
  ["angle-cast-dot", "(<AuthContext>auth).broadcast"],
  ["destructured", "const { broadcast } = auth"],
  ["array-destructuring", "const [{ broadcast }] = [auth]"],
  ["safe-destructuring", "const { broadcast = '' } = auth"],
];

for (const [label, source] of cases) {
  const sf = ts.createSourceFile(label + ".ts", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
  let matched = false;
  function visit(node) {
    if (ts.isPropertyAccessExpression(node) && node.name.text === "broadcast") {
      console.log(label + " PropertyAccessExpression:", node.expression.getText(sf));
      matched = true;
    }
    if (ts.isElementAccessExpression(node) && node.name?.text === "broadcast") {
      console.log(label + " ElementAccessExpression:", JSON.stringify(node.expression.getText(sf)), "argument:", JSON.stringify(node.argumentExpression?.getText(sf)));
      matched = true;
    }
    if (ts.isBindingPattern(node) || ts.isObjectBindingPattern(node) || ts.isArrayBindingPattern(node)) {
      node.elements.forEach((e) => {
        if (ts.isBindingElement(node) && e.name.getText(sf) === "broadcast") {
          console.log(label + " BindingElement:", JSON.stringify(source));
          matched = true;
        }
      });
    }
    ts.forEachChild(node, visit);
  }
  visit(sf);
  if (!matched) {
    console.log(label + " no local match");
  }
}
JS

Repository: ecency/vision-web

Length of output: 477


Cover all API forms that read AuthContext.broadcast.

The parser accepts auth["broadcast"], (auth as AuthContext).broadcast, (<AuthContext>auth).broadcast, and destructuring, but this detector only blocks auth.broadcast and auth?.broadcast. Add an explicit lint rule or extend the AST checks for string-literal element access and binding patterns so equivalent reads cannot bypass the guard.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sdk/src/modules/core/custom-broadcast-usage.spec.ts` around lines 57
- 65, Extend the AST detector in the visit function to flag every read of
AuthContext.broadcast, including string-literal element access such as
auth["broadcast"] and broadcast property bindings in object destructuring.
Normalize parenthesized and type-asserted expressions before checking the
receiver so (auth as AuthContext).broadcast and (<AuthContext>auth).broadcast
are covered, while preserving the existing direct and optional property-access
detection.

Comment on lines +30 to +31
* A caller-supplied broadcaster, for signing the platform adapter cannot do.
*

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the incomplete sentence in the contract documentation.

Use A caller-supplied broadcaster for signing operations that the platform adapter cannot perform. The current wording is grammatically incomplete and makes the callback contract harder to read.

Proposed wording
-   * A caller-supplied broadcaster, for signing the platform adapter cannot do.
+   * A caller-supplied broadcaster for signing operations that the platform adapter cannot perform.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* A caller-supplied broadcaster, for signing the platform adapter cannot do.
*
* A caller-supplied broadcaster for signing operations that the platform adapter cannot perform.
*
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sdk/src/modules/core/types/auth.ts` around lines 30 - 31, Update the
contract documentation near the caller-supplied broadcaster declaration to use
the complete wording “A caller-supplied broadcaster for signing operations that
the platform adapter cannot perform.”

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.

1 participant