Skip to content

feat: add headless mode support to controller SDK#2315

Open
tarrencev wants to merge 38 commits intomainfrom
head2
Open

feat: add headless mode support to controller SDK#2315
tarrencev wants to merge 38 commits intomainfrom
head2

Conversation

@tarrencev
Copy link
Contributor

@tarrencev tarrencev commented Jan 8, 2026

Summary

  • Add headless connect support across controller + keychain, including signer-based login, unverified-policy approvals, and a dedicated headless approval route/event.
  • Simplify connect flows by separating headless params from standard connect, and move/remove e2e harness logic from production paths.
  • Update examples/docs and fix headless build typings. Example header now syncs starknet-react state after headless session approval.

Testing

  • pnpm lint:check
  • pnpm test:ci
  • pnpm build

@vercel
Copy link

vercel bot commented Jan 8, 2026

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

Project Deployment Actions Updated (UTC)
controller-example-next Ready Ready Preview Feb 6, 2026 10:33pm
keychain Ready Ready Preview Feb 6, 2026 10:33pm
keychain-storybook Ready Ready Preview Feb 6, 2026 10:33pm

Request Review

if (headless?.username && headless?.credentials) {
// Perform headless authentication without UI
return authenticateHeadless(headless.username, headless.credentials);
}
Copy link

Choose a reason for hiding this comment

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

Inconsistent headless detection causes hung promise on empty username

Medium Severity

Inconsistent headless mode detection between controller.ts and connect.ts can cause a hung promise. The controller checks !headlessOptions (object existence) to decide whether to open the modal, while the keychain's connect checks headless?.username && headless?.credentials (truthy property values) to decide whether to use headless authentication. When headless is provided with an empty string username, the modal isn't opened but the code falls through to the UI-based flow, which waits for user interaction callbacks that can never arrive since the modal is hidden.

Additional Locations (1)

Fix in Cursor Fix in Web

password,
},
},
});
Copy link

Choose a reason for hiding this comment

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

Repeated headless login attempts fail due to singleton iframe

High Severity

The HeadlessLogin component creates a new Controller on each login attempt, but the underlying IFrame class uses singleton-style DOM element management - it checks for an existing "controller" element and won't add a second one. After the first login attempt, subsequent Controller instances have their iframes created but never added to the DOM, so the penpal connection never establishes, this.keychain remains undefined, and connect() immediately returns undefined with a "Not ready to connect" error. Users clicking the login button multiple times will see the first attempt work (or fail for unimplemented reasons) but all subsequent attempts fail instantly.

Fix in Cursor Fix in Web

return authenticateHeadless(
headless.username,
headless.credentials,
).then((result) => {
Copy link

Choose a reason for hiding this comment

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

Headless mode ignores configured chain, always uses mainnet

High Severity

The authenticateHeadless function accepts an optional chainId parameter that defaults to mainnet, but the connect handler never passes a chainId when calling it. The HeadlessOptions type doesn't include chainId, and neither does ConnectOptions, so even though users configure defaultChainId: SN_SEPOLIA in the Controller SDK, headless authentication always uses mainnet. This causes headless authentication to operate on the wrong chain regardless of configuration.

Additional Locations (1)

Fix in Cursor Fix in Web

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