Skip to content

Preview/auth payment test#1

Open
Moeabdelaziz007 wants to merge 14 commits into
mainfrom
preview/auth-payment-test
Open

Preview/auth payment test#1
Moeabdelaziz007 wants to merge 14 commits into
mainfrom
preview/auth-payment-test

Conversation

@Moeabdelaziz007
Copy link
Copy Markdown
Contributor

@Moeabdelaziz007 Moeabdelaziz007 commented May 25, 2026

Summary by CodeRabbit

  • Documentation

    • Added a comprehensive AxiomID Agentic design system: theme, typography, spacing, components, animations, accessibility, and tone guidelines.
  • New Features

    • Pi payment flows and webhook endpoints for cancel/error/incomplete handling.
    • New Pi purchase/connect UI: a Buy-with-Pi button and client hooks for Pi connection/purchases.
    • Injected Pi SDK to load before page interactivity.
  • Improvements

    • Redesigned Privacy and Terms into bento-style layouts with updated copy.
    • Simplified Pi wallet/auth flow and auto-auth behavior.
    • Standardized agent status values to uppercase and added an extra status.
  • Tests

    • Removed one auth-related test case.

Review Change Stack

**Disclaimer This is AxiomID Review Agent.

Greptile Summary

This PR introduces Pi Network payment flows (cancel/error/incomplete webhook routes), a PiButton component, pi-sdk-hooks, SandboxProvider, and a wallet debug/test tooling layer. It also refactors pi-sdk.ts into a simpler connectPi/ensurePiSdk model, uppercases agent status values, and redesigns the Privacy and Terms pages.

  • Pi SDK refactor replaces the old retry-and-cache approach with a streamlined connectPi / ensurePiSdk flow; however Pi.init() is now called without a sandbox flag, the PiButton auto-triggers authentication on mount, and access token fragments are piped into the visible dashboard terminal.
  • Payment webhooks add cancel and error acknowledgement endpoints (appropriate), but the incomplete-payment handler only fetches status rather than calling approve/complete on the Pi Network backend, leaving those payments permanently stuck.
  • Agent status values are correctly uppercased in the API routes, but the dashboard comparisons still use lowercase "active", so the badge and toggle button display incorrectly for all users.

Confidence Score: 3/5

Not safe to merge in current form — payment transactions silently target the wrong network, token data is surfaced in the UI, and the PiButton initiates authentication without any user action.

The payment layer has multiple concurrently broken paths: Pi.init is always called without the sandbox flag so sandbox payments hit the live network; the incomplete-payment handler fetches status but never approves or completes; usePiPurchase has no lifecycle callbacks; the PiButton fires authentication on component mount. On top of that, access token fragments are written to the dashboard terminal, and window.postMessage is patched unconditionally for all visitors in iframes. The agent status case mismatch means the badge and toggle button display incorrectly for every user.

apps/axiomid/src/lib/pi-sdk.ts, apps/axiomid/src/lib/pi-sdk-hooks.ts, apps/axiomid/src/components/PiButton.tsx, apps/axiomid/src/app/context/wallet-context.tsx, apps/axiomid/src/app/context/sandbox-provider.tsx, apps/axiomid/src/app/api/pi/payment/incomplete/route.ts, apps/axiomid/src/app/dashboard/page.tsx

Security Review

  • Token fragment surfaced in UI (pi-sdk.ts lines 98, 185): connectPi and runWalletTest both log the first 20–30 characters of the Pi access token through onLog. These messages are piped into walletLogs and rendered in the dashboard terminal, exposing partial token data to anyone viewing the screen.
  • Unconditional postMessage patching with targetOrigin="*" (pi-sandbox.ts / sandbox-provider.tsx): SandboxProvider wraps the entire app and always calls initSandboxCompatibility(), which replaces window.postMessage to forward all messages to the parent frame using "*" as the target origin. This runs for all visitors in any iframe context, broadcasting outgoing messages (including any that carry auth tokens or app state) to an arbitrary parent without origin validation.

Important Files Changed

Filename Overview
apps/axiomid/src/lib/pi-sdk.ts Heavily refactored: removed retry/cache logic, replaced with simpler ensurePiSdk/connectPi/runWalletTest. Pi.init() never passes sandbox flag; access token fragment logged via onLog to the dashboard terminal.
apps/axiomid/src/lib/pi-sdk-hooks.ts New file. usePiPurchase calls pi.createPayment without lifecycle callbacks (previously flagged). usePiConnection syncs from PiSdkBase.user with uid falling back to display name (previously flagged).
apps/axiomid/src/components/PiButton.tsx New component. Auto-triggers Pi authentication via useEffect on SDK ready, without requiring any user gesture, which will surprise users who navigate to pages containing this component.
apps/axiomid/src/app/context/wallet-context.tsx checkPiBrowser returns true for any iframe and once Pi SDK is loaded (previously flagged); payments scope restored in connectPi; demo wallet session not re-authenticated on page load (previously flagged).
apps/axiomid/src/app/context/sandbox-provider.tsx New component. Calls initSandboxCompatibility() unconditionally for all visitors, patching window.postMessage to forward all messages to parent with targetOrigin=* (previously flagged security concern).
apps/axiomid/src/app/api/pi/payment/incomplete/route.ts New route. Fetches payment status from Pi Network but never calls approve/complete — incomplete payments remain permanently stuck (previously flagged).
apps/axiomid/src/app/dashboard/page.tsx Adds wallet-test terminal commands; CSS tokens migrated to Tailwind classes. Agent status badge and toggle button still compare against lowercase 'active' instead of 'ACTIVE' (previously flagged); handleToggleAgentState always activates (previously flagged).
apps/axiomid/src/app/layout.tsx Adds SandboxProvider and Pi SDK script tag with strategy='beforeInteractive', making the Pi CDN a blocking critical-path dependency for all visitors (previously flagged).
apps/axiomid/src/lib/pi-sandbox.ts New file. patchPostMessageForSandbox replaces window.postMessage to forward all messages to parent with targetOrigin='*' whenever in an iframe — runs unconditionally for all visitors via SandboxProvider.

Sequence Diagram

sequenceDiagram
    participant U as User
    participant PB as PiButton
    participant Hook as usePiConnection
    participant SDK as pi-sdk.ts (connectPi)
    participant Pi as window.Pi (CDN)
    participant API as /api/auth/pi

    PB->>Hook: mount → useEffect polls isPiSdkLoaded()
    Note over Hook: ready=true when Pi SDK loads
    Hook-->>PB: "ready=true"
    PB->>Hook: auto-calls connect() (no user gesture)
    Hook->>SDK: connectPi()
    SDK->>Pi: "ensurePiSdk() then Pi.init({ version:"2.0" })"
    Note over SDK,Pi: sandbox flag never passed
    SDK->>Pi: Pi.authenticate(["payments","username"])
    Pi-->>SDK: "authResult { accessToken, user }"
    Note over SDK: token fragment logged via onLog
    SDK-->>Hook: PiAuthResult
    Hook->>API: POST /api/auth/pi
    API-->>Hook: "{ user }"
    Hook-->>PB: "connected=true"
    U->>PB: click Buy with Pi
    PB->>Hook: usePiPurchase → pi.createPayment(data)
    Note over Hook: no lifecycle callbacks — payment hangs
Loading

Comments Outside Diff (2)

  1. apps/axiomid/src/app/dashboard/page.tsx, line 248-264 (link)

    P1 "Toggle" handler always activates — pause is unreachable

    handleToggleAgentState unconditionally calls activateAgent(), which always sets the status to ACTIVE. When the agent is already active and a user clicks "■ PAUSE RUNTIME", the handler activates it again instead of deactivating or pausing it. There is no corresponding deactivate/pause call here, so the PAUSE button is permanently broken.

    Fix in Conductor

  2. apps/axiomid/src/app/context/wallet-context.tsx, line 474-506 (link)

    P1 Demo-wallet session lost on every page reload

    The old else branch that re-authenticated stored demo wallets via /api/auth/connect on page load has been removed. Now the auto-auth effect only calls connectWallet() when usePi is true; for all regular (non-Pi-browser, non-sandbox) users nothing happens on mount — setIsLoading(false) fires immediately and user remains null forever until the user manually clicks "Connect". Any state tied to the demo wallet (XP, tier, agent) is invisible until reconnect.

    Fix in Conductor

Fix All in Conductor

Reviews (7): Last reviewed commit: "fix(auth): unify pi-sdk and fix sandbox ..." | Re-trigger Greptile

Greptile also left 3 inline comments on this PR.

- agent/activate/route.ts: 'active' -> 'ACTIVE'
- agent/main/route.ts: 'active' -> 'ACTIVE'
- agent/route.ts: 'inactive' -> 'INACTIVE'
- lib/agent-flow.ts: AgentStatus type updated to match Prisma enum
- pi-sdk.ts: rewritten ensurePiSdk to await Pi.init() before checking Pi.authenticate
- wallet-context.tsx: Pi.authenticate(scopes, callback) not object syntax
- pi-wallet.ts: same fix
- layout.tsx: added <Script> for SDK CDN load with beforeInteractive
@vercel
Copy link
Copy Markdown

vercel Bot commented May 25, 2026

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

Project Deployment Actions Updated (UTC)
axiomid-project Ready Ready Preview, Comment May 25, 2026 12:48pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a DESIGN.md design system, normalizes AgentStatus to uppercase (adds SLEEPING) and updates agent APIs, refactors the Pi SDK and wallet authentication flow, injects the Minepi script in layout, updates auth-connect tests, and restyles Terms, Privacy, and dashboard UI with themed tokens.

Changes

Design System & Agent Status Normalization

Layer / File(s) Summary
Design System Documentation
DESIGN.md
Complete AxiomID Agentic design system: tokens, palette, Geist typography, spacing/rounded scales, bento/card/button/grid/glow/scrollbar specs, animations, accessibility, and tone Do/Don't rules.
Agent Status Type Normalization
apps/axiomid/src/lib/agent-flow.ts
AgentStatus union changed to uppercase literals and extended with 'SLEEPING'.
Agent API Status Updates
apps/axiomid/src/app/api/agent/*.ts
Three agent endpoints updated to persist uppercase status strings ('INACTIVE', 'ACTIVE') when creating/updating userAgent records.

Pi SDK Refactoring & Wallet Integration

Layer / File(s) Summary
Pi SDK Core Refactor
apps/axiomid/src/lib/pi-sdk.ts
Replaces the previous loader/ensure logic with a minimal wrapper exposing getPiSdk() and connectPi() plus helpers isPiSdkLoaded()/isPiSdkConnected().
Client hooks & PiButton
apps/axiomid/src/lib/pi-sdk-hooks.ts, apps/axiomid/src/components/PiButton.tsx
Adds usePiConnection and usePiPurchase hooks and a PiButton component wired to them for purchases.
Pi Wallet Authenticate Signature
apps/axiomid/src/lib/pi-wallet.ts
connectPiWallet now calls Pi.authenticate(scopes, onIncompletePaymentCallback) instead of an options object.
Wallet Context: Pi Detection & Connect
apps/axiomid/src/app/context/wallet-context.tsx
Simplifies Pi detection to isPiSdkLoaded()/UA, branches demo vs Pi auth, uses connectPi() for Pi flows, posts to /api/auth/pi, removes prior non-Pi auto-auth fallback, and drops accessToken from agent API payloads.
Layout: Minepi SDK Injection & Dependency
apps/axiomid/src/app/layout.tsx, apps/axiomid/package.json
Adds next/script and injects https://sdk.minepi.com/pi-sdk.js with strategy="beforeInteractive" and adds @pinetwork/pi-sdk-js dependency.
Pi Payment API Endpoints
apps/axiomid/src/app/api/pi/payment/*.ts
New POST endpoints for payment cancel, error, and incomplete payment checking with validation, upstream fetch, and error handling.
Auth Connect Tests Update
apps/axiomid/src/app/api/__tests__/auth-connect.test.ts
Removes Jest mock for @/lib/tiers and deletes the existing-user tier-recalculation test case.

Terms, Privacy & Dashboard UI

Layer / File(s) Summary
Privacy Page Bento Layout
apps/axiomid/src/app/privacy/page.tsx
Adds BentoCard, new header with AXIOMID logo and BACK link, restructures privacy content into labeled sections, and adds footer.
Terms Page Bento Layout
apps/axiomid/src/app/terms/page.tsx
Adds BentoCard and replaces Terms page with Bento-style legal card, labeled sections, updated Last-updated text, and footer.
Dashboard & Marketplace Themed Tokens
apps/axiomid/src/app/dashboard/page.tsx, apps/axiomid/src/app/page.tsx
Replaces many hardcoded hex color classes with themed utilities like neon-green, electric-blue, and text-axiom-gold across dashboard, terminal, MCP hub, KYC cards, agent badges, runtime toggle, and Marketplace cards.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 In neon glass I hop and hum tonight,
Uppercase statuses glow in brighter light,
Pi speaks simpler calls and clears the road,
Bento cards hold legal lines in code,
A rabbit cheers the stack — small, bright, and bold.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'Preview/auth payment test' is vague and does not accurately summarize the substantial changes across multiple systems including auth refactoring, agent status standardization, UI redesign, and payment integration. Replace with a clear, specific title that reflects the main changes, such as 'Refactor Pi auth to use official SDK and standardize agent status to uppercase' or 'Migrate to @pinetwork/pi-sdk-js with uppercase agent statuses and bento UI redesign'.
Docstring Coverage ⚠️ Warning Docstring coverage is 4.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 preview/auth-payment-test

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

Comment thread apps/axiomid/src/app/context/wallet-context.tsx Outdated
Comment thread apps/axiomid/src/lib/pi-sdk.ts Outdated
Comment thread apps/axiomid/src/lib/pi-sdk.ts Outdated
Comment thread apps/axiomid/src/app/context/wallet-context.tsx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 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 `@apps/axiomid/src/lib/agent-flow.ts`:
- Line 1: Update the AgentStatus handling to tolerate legacy lowercase persisted
values by adding a read-time normalizer and a one-time DB backfill: add a
function (e.g., normalizeAgentStatus) that maps any lowercase or mixed-case
status strings to the uppercase union (referencing the AgentStatus type) and
call it wherever agent records are deserialized or serialized for responses
(e.g., in any getAgent / serializeAgent / toApiPayload code paths) so returned
payloads always emit uppercase statuses; additionally create and run a migration
script that updates existing DB rows to uppercase the status column to
permanently fix persisted data.

In `@apps/axiomid/src/lib/pi-sdk.ts`:
- Around line 46-74: The cached Promise for loading the Pi SDK currently
resolves on script.onerror and can remain pending if the internal wait loop
never reaches a terminal state; modify the loader so that both the onerror
handler and the internal wait timeout path use a shared failure routine that
clears the top-level cached variable (so subsequent calls will recreate the
loader) and resolves/rejects the Promise consistently. Concretely, add a
deadline/timeout inside the wait() loop, implement a fail() helper that does
cached = undefined and then resolves (or rejects) the Promise, and call that
fail() from script.onerror, from p.init.catch, and when the wait loop exceeds
the deadline; keep references to the existing identifiers cached,
script.onerror, wait, p.init, and p.authenticate to locate where to change the
code.
- Around line 40-43: ensurePiSdk currently chains .then/.catch off pi.init(...)
but Pi.init is synchronous and returns void; replace those promise chains by
calling pi.init(...) inside a try/catch and set the shared cached Promise
appropriately. Specifically, in the fast path (where you reference cached and
call pi.init) remove the .then/.catch, call pi.init({ version: "2.0", sandbox:
!!sandbox }) inside try { ... } and set cached = Promise.resolve() on success
and cached = Promise.resolve() (or a resolved void promise) on error after
catching it (or optionally set to Promise.reject(err) if you want callers to see
init errors); do the same change in the script-load path where pi.init is
invoked so both branches do not chain off a void return and both set cached to a
proper Promise<void>.

In `@apps/axiomid/src/lib/pi-wallet.ts`:
- Around line 42-47: Update the TypeScript declaration for Pi.authenticate in
the global.d.ts to match the SDK's positional-argument signature: change the
current object-parameter overload to a function signature
Pi.authenticate(scopes: string[] | readonly string[], onIncompletePaymentFound?:
(payment: any) => void): Promise<any> (or the appropriate return type), ensuring
the parameter names and types match the usage in Pi.authenticate(scopes,
onIncompletePaymentFound) and existing calls in wallet-context.tsx and
pi-wallet.ts; keep any additional overloads if needed but ensure the positional
form is present so the compiler enforces the two-argument call shape.
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5ea0335d-d840-4601-a0e6-b8d321ad34fc

📥 Commits

Reviewing files that changed from the base of the PR and between 404980d and 3ba6a82.

📒 Files selected for processing (8)
  • DESIGN.md
  • apps/axiomid/src/app/api/agent/activate/route.ts
  • apps/axiomid/src/app/api/agent/main/route.ts
  • apps/axiomid/src/app/api/agent/route.ts
  • apps/axiomid/src/app/context/wallet-context.tsx
  • apps/axiomid/src/lib/agent-flow.ts
  • apps/axiomid/src/lib/pi-sdk.ts
  • apps/axiomid/src/lib/pi-wallet.ts

@@ -1,4 +1,4 @@
export type AgentStatus = 'inactive' | 'active' | 'paused';
export type AgentStatus = 'INACTIVE' | 'ACTIVE' | 'PAUSED' | 'SLEEPING';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Add a compatibility path for legacy lowercase status values.

The type contract is now uppercase-only, but read paths still pass through raw persisted status values. Existing lowercase rows from earlier writes can break this contract at runtime. Add a read-time normalizer and a DB backfill/migration so responses always emit uppercase statuses.

Suggested direction
+export function normalizeAgentStatus(status: string): AgentStatus {
+  switch (status?.toUpperCase()) {
+    case 'ACTIVE':
+    case 'INACTIVE':
+    case 'PAUSED':
+    case 'SLEEPING':
+      return status.toUpperCase() as AgentStatus;
+    default:
+      return 'INACTIVE';
+  }
+}

Then apply this when returning agent payloads (or in API serializers), and run a one-time data migration to uppercase existing stored values.

📝 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
export type AgentStatus = 'INACTIVE' | 'ACTIVE' | 'PAUSED' | 'SLEEPING';
export type AgentStatus = 'INACTIVE' | 'ACTIVE' | 'PAUSED' | 'SLEEPING';
export function normalizeAgentStatus(status: string): AgentStatus {
switch (status?.toUpperCase()) {
case 'ACTIVE':
case 'INACTIVE':
case 'PAUSED':
case 'SLEEPING':
return status.toUpperCase() as AgentStatus;
default:
return 'INACTIVE';
}
}
🤖 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 `@apps/axiomid/src/lib/agent-flow.ts` at line 1, Update the AgentStatus
handling to tolerate legacy lowercase persisted values by adding a read-time
normalizer and a one-time DB backfill: add a function (e.g.,
normalizeAgentStatus) that maps any lowercase or mixed-case status strings to
the uppercase union (referencing the AgentStatus type) and call it wherever
agent records are deserialized or serialized for responses (e.g., in any
getAgent / serializeAgent / toApiPayload code paths) so returned payloads always
emit uppercase statuses; additionally create and run a migration script that
updates existing DB rows to uppercase the status column to permanently fix
persisted data.

Comment thread apps/axiomid/src/lib/pi-sdk.ts Outdated
Comment thread apps/axiomid/src/lib/pi-sdk.ts Outdated
Comment thread apps/axiomid/src/lib/pi-wallet.ts Outdated
Comment thread apps/axiomid/src/app/context/wallet-context.tsx Outdated
Comment thread apps/axiomid/src/app/layout.tsx
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
apps/axiomid/src/app/terms/page.tsx (1)

5-11: ⚡ Quick win

Extract shared legal-page shell to avoid drift between Terms and Privacy.

BentoCard + header/footer structure is duplicated here and in apps/axiomid/src/app/privacy/page.tsx (same classes/markup pattern). Consider moving this into a shared component (e.g., LegalPageShell) so legal pages stay visually and behaviorally consistent as edits continue.

Also applies to: 19-34, 106-111

🤖 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 `@apps/axiomid/src/app/terms/page.tsx` around lines 5 - 11, The BentoCard
wrapper and repeated header/footer layout is duplicated between the Terms and
Privacy pages; extract a shared component (e.g., LegalPageShell) that
encapsulates the current BentoCard markup plus the common header and footer
structure, export it, and replace the BentoCard + header/footer usages in both
page components with this new LegalPageShell so both pages consume the same
shell; update/rename the existing BentoCard (or remove it) and ensure the pages
pass their unique body content as children to LegalPageShell and preserve any
className/prop forwarding (refer to BentoCard and the Terms/Privacy page
components for locations to change).
🤖 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 `@apps/axiomid/src/app/context/wallet-context.tsx`:
- Around line 77-89: The code obtains a Pi accessToken from authenticatePi
(accessToken, piUser, walletAddress) but later POSTs to the agent endpoints
without including accessToken, causing 401s for pi:* wallets; update the fetch
calls that create/activate agents (the POSTs to the agent routes) to include the
accessToken (either in the JSON body alongside uid/username/walletAddress or as
an Authorization header) so the Pi-protected endpoints can validate the request
(refer to variables accessToken, piUser.uid, walletAddress and the fetch calls
that contact the agent and agent/activate routes).
- Around line 61-63: The current logic sets shouldUsePi = isPiBrowser() ||
isSandbox(), which causes sandbox previews to force Pi auth for all visitors and
then auto-runs connectWallet(); change the gate so sandbox alone does not enable
Pi: only enable Pi when isPiBrowser() is true OR when sandbox is true AND an
explicit runtime gate is present (e.g., a query param or env flag), and wrap the
connectWallet() auto-invoke (the block calling connectWallet()) behind that
stricter condition; update references to shouldUsePi, isPiBrowser(),
isSandbox(), and the connectWallet() call so sandbox no longer implicitly flips
users into Pi flow.

In `@apps/axiomid/src/app/privacy/page.tsx`:
- Line 60: Replace the incorrect phrase "personal identifiable information" in
the privacy copy with "personally identifiable information" (or "personal
information") in the JSX string inside the privacy page component (the privacy
text shown in page.tsx), updating the sentence that currently reads "We do not
collect personal identifiable information..." to "We do not collect personally
identifiable information..." to fix the wording.

---

Nitpick comments:
In `@apps/axiomid/src/app/terms/page.tsx`:
- Around line 5-11: The BentoCard wrapper and repeated header/footer layout is
duplicated between the Terms and Privacy pages; extract a shared component
(e.g., LegalPageShell) that encapsulates the current BentoCard markup plus the
common header and footer structure, export it, and replace the BentoCard +
header/footer usages in both page components with this new LegalPageShell so
both pages consume the same shell; update/rename the existing BentoCard (or
remove it) and ensure the pages pass their unique body content as children to
LegalPageShell and preserve any className/prop forwarding (refer to BentoCard
and the Terms/Privacy page components for locations to change).
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1736ef84-08c5-422e-9925-9382aa4f8552

📥 Commits

Reviewing files that changed from the base of the PR and between 3ba6a82 and e1c7a20.

📒 Files selected for processing (6)
  • apps/axiomid/src/app/api/__tests__/auth-connect.test.ts
  • apps/axiomid/src/app/context/wallet-context.tsx
  • apps/axiomid/src/app/layout.tsx
  • apps/axiomid/src/app/privacy/page.tsx
  • apps/axiomid/src/app/terms/page.tsx
  • apps/axiomid/src/lib/pi-sdk.ts
💤 Files with no reviewable changes (1)
  • apps/axiomid/src/app/api/tests/auth-connect.test.ts

Comment thread apps/axiomid/src/app/context/wallet-context.tsx Outdated
Comment on lines +77 to +89
const { accessToken, user: piUser } = await authenticatePi(["username"]);

piUid = auth.user.uid;
piUsername = auth.user.username;
accessToken = auth.accessToken;
walletAddress = `pi:${piUid}`;
} else {
debug("using demo wallet");
// Reuse stored wallet if available, otherwise generate new demo wallet
const storedWallet = localStorage.getItem("axiomid_wallet");
if (storedWallet && storedWallet.startsWith("demo:")) {
walletAddress = storedWallet;
} else {
walletAddress = `demo:${crypto.randomUUID().slice(0, 8)}`;
}
}
const walletAddress = `pi:${piUser.uid}`;

debug("saving wallet to localStorage", walletAddress);
localStorage.setItem("axiomid_wallet", walletAddress);
if (accessToken) localStorage.setItem("axiomid_token", accessToken);

const endpoint = usePi ? "/api/auth/pi" : "/api/auth/connect";
const body = usePi
? JSON.stringify({ accessToken, uid: piUid, walletAddress, username: piUsername })
: JSON.stringify({ walletAddress, piUid, piUsername, accessToken });
debug(`POST ${endpoint}`, { walletAddress, hasAccessToken: !!accessToken });
const res = await fetch(endpoint, {
const res = await fetch("/api/auth/pi", {
method: "POST",
headers: { "Content-Type": "application/json" },
body,
body: JSON.stringify({
accessToken,
uid: piUser.uid,
username: piUser.username,
walletAddress,
}),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pass the Pi access token to the agent APIs again.

Lines 77-89 obtain accessToken, but Lines 164-165 and 180-181 no longer send it to the Pi-protected agent routes. apps/axiomid/src/app/api/agent/route.ts:5-29 and apps/axiomid/src/app/api/agent/activate/route.ts:4-31 still reject pi:* wallets when accessToken is missing, so authenticated Pi users will now get 401s when creating or activating an agent.

Also applies to: 164-165, 180-181

🤖 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 `@apps/axiomid/src/app/context/wallet-context.tsx` around lines 77 - 89, The
code obtains a Pi accessToken from authenticatePi (accessToken, piUser,
walletAddress) but later POSTs to the agent endpoints without including
accessToken, causing 401s for pi:* wallets; update the fetch calls that
create/activate agents (the POSTs to the agent routes) to include the
accessToken (either in the JSON body alongside uid/username/walletAddress or as
an Authorization header) so the Pi-protected endpoints can validate the request
(refer to variables accessToken, piUser.uid, walletAddress and the fetch calls
that contact the agent and agent/activate routes).

Information We Collect
</h2>
<p>
When you connect your wallet via Pi Network, we collect your Pi wallet address and Pi Network UID for the purpose of authentication and XP tracking. We do <strong className="text-neon-green">not</strong> collect personal identifiable information such as your name, email, or IP address beyond standard server logs.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix wording in privacy copy at Line 60.

"personal identifiable information" should be "personally identifiable information" (or "personal information") to avoid legal-text ambiguity.

🤖 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 `@apps/axiomid/src/app/privacy/page.tsx` at line 60, Replace the incorrect
phrase "personal identifiable information" in the privacy copy with "personally
identifiable information" (or "personal information") in the JSX string inside
the privacy page component (the privacy text shown in page.tsx), updating the
sentence that currently reads "We do not collect personal identifiable
information..." to "We do not collect personally identifiable information..." to
fix the wording.

Comment on lines 677 to 696
@@ -691,7 +691,7 @@ export default function Dashboard() {

<button
onClick={handleToggleAgentState}
className="w-full px-3 py-2 rounded-xl bg-white/5 border border-white/10 hover:border-[#00ff41]/30 hover:bg-[#00ff41]/10 hover:text-[#00ff41] text-xs font-mono text-white transition-all text-center"
className="w-full px-3 py-2 rounded-xl bg-white/5 border border-white/10 hover:border-neon-green/30 hover:bg-neon-green/10 hover:text-neon-green text-xs font-mono text-white transition-all text-center"
>
{user.agent.status === "active" ? "■ PAUSE RUNTIME" : "▶ ACTIVATE RUNTIME"}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Stale lowercase status comparison breaks agent UI

The agent routes were updated to write uppercase values ("ACTIVE", "INACTIVE"), but these two comparisons still use the old lowercase "active". As a result, every user's agent badge will always render with the gray/inactive style and the button will always read "▶ ACTIVATE RUNTIME" — even when the agent is actually ACTIVE. Fix by changing both comparisons to === "ACTIVE".

Fix in Conductor

Comment thread apps/axiomid/src/lib/pi-sdk.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/axiomid/src/app/context/wallet-context.tsx (1)

179-209: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing accessToken for Pi wallets will cause 401 errors on agent APIs.

createAgent and activateAgent send only walletAddress (and name), but apps/axiomid/src/app/api/agent/route.ts and /api/agent/activate/route.ts require accessToken for wallets starting with pi: and return 401 if it's missing. Pi wallet users will be unable to create or activate agents.

The accessToken obtained during connectWallet (line 92) needs to be stored (in state or localStorage) and included in these requests.

Proposed fix direction
  1. Add state to store the Pi access token:
+ const [piAccessToken, setPiAccessToken] = useState<string | null>(null);
  1. Store it during Pi auth in connectWallet:
  const { accessToken, user: piUser } = await authenticatePi(["username"]);
+ setPiAccessToken(accessToken);
  1. Include it in agent API calls:
  body: JSON.stringify({ 
    walletAddress: user.walletAddress, 
    name,
+   accessToken: piAccessToken 
  }),

Alternatively, store the token in localStorage alongside the wallet address and retrieve it when needed.

🤖 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 `@apps/axiomid/src/app/context/wallet-context.tsx` around lines 179 - 209,
createAgent and activateAgent omit the Pi wallet accessToken, causing 401s for
wallets starting with "pi:"; update the wallet context to persist the
accessToken obtained in connectWallet (store in component state or localStorage)
and include that accessToken when calling /api/agent and /api/agent/activate
(e.g., send accessToken alongside walletAddress and name or include in
Authorization header) so createAgent and activateAgent pass the token to the
API.
🤖 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.

Outside diff comments:
In `@apps/axiomid/src/app/context/wallet-context.tsx`:
- Around line 179-209: createAgent and activateAgent omit the Pi wallet
accessToken, causing 401s for wallets starting with "pi:"; update the wallet
context to persist the accessToken obtained in connectWallet (store in component
state or localStorage) and include that accessToken when calling /api/agent and
/api/agent/activate (e.g., send accessToken alongside walletAddress and name or
include in Authorization header) so createAgent and activateAgent pass the token
to the API.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 504db143-9dfb-4cbc-9200-40dd7de1faa9

📥 Commits

Reviewing files that changed from the base of the PR and between 0e86d8e and 666a731.

📒 Files selected for processing (2)
  • apps/axiomid/src/app/context/wallet-context.tsx
  • apps/axiomid/src/lib/pi-sdk.ts

…tion hooks, PiButton, payment lifecycle routes

- Rewrite pi-sdk.ts with PiSdkBase.connect() for reliable init→authenticate
- Create usePiConnection/usePiPurchase hooks wrapping PiSdkBase
- Create PiButton.tsx component from official template
- Rewrite wallet-context to use connectPi with isPiSdkLoaded detection
- Add cancel/error/incomplete payment API routes
- Set paymentBasePath = 'api/pi/payment'

export async function POST(request: Request) {
try {
const { paymentId, transactionId } = await request.json();
Comment on lines +71 to +78
export function usePiPurchase(paymentData: PaymentData): () => void {
const purchase = useCallback(() => {
const pi = new PiSdkBase();
pi.createPayment(paymentData);
}, [paymentData]);

return purchase;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 usePiPurchase creates a payment with no lifecycle callbacks

pi.createPayment(paymentData) is invoked without the required onReadyForServerApproval, onReadyForServerCompletion, onCancel, or onError handlers. The Pi SDK holds the payment open waiting for your server to approve it via the configured paymentBasePath; without a callback to trigger or acknowledge that step, the payment will hang indefinitely and never complete. Any user-facing purchase triggered by this hook results in a stuck payment that cannot be recovered without a full page reload or Pi support intervention.

Fix in Conductor

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/axiomid/src/app/context/wallet-context.tsx (1)

168-175: ⚠️ Potential issue | 🟠 Major

Fix Pi wallet agent auth: server still requires accessToken

/api/agent and /api/agent/activate (and /api/agent/main) still parse walletAddress and, when it starts with pi:, require accessToken (returning 401 if it’s missing) and call https://api.minepi.com/v2/me with Authorization: Bearer ${accessToken}. With the client now sending only { walletAddress, name }, the request will fail for pi:* wallets—revert to sending accessToken (or update the server contract to not require it).

🤖 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 `@apps/axiomid/src/app/context/wallet-context.tsx` around lines 168 - 175, The
client createAgent function is no longer sending accessToken but the server
endpoints (/api/agent, /api/agent/activate, /api/agent/main) still require it
for pi:* wallets; update the payload sent from createAgent to include the user's
accessToken (e.g., include accessToken from the user object in the POST body
alongside walletAddress and name) or conditionally include accessToken when
user.walletAddress startsWith('pi:'), so the server can call
https://api.minepi.com/v2/me with Authorization: Bearer ${accessToken} and avoid
401s.
🤖 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 `@apps/axiomid/src/app/api/pi/payment/incomplete/route.ts`:
- Around line 13-20: The outbound fetch to
`https://api.minepi.com/v2/payments/${paymentId}` has no timeout/AbortSignal;
wrap the call with an AbortController, pass controller.signal into the fetch
options (where Authorization header is set), create a timer (e.g. const timeout
= setTimeout(() => controller.abort(), 5000)) before the fetch and clear it
after the response (clearTimeout(timeout)), and handle the abort case by
catching the thrown DOMException/AbortError and returning an appropriate error
response; update the fetch invocation in the route handler where `fetch(..., {
headers: { Authorization: \`Key ${apiKey}\` } })` appears to include `signal:
controller.signal`.

In `@apps/axiomid/src/app/context/wallet-context.tsx`:
- Around line 39-43: The checkPiBrowser() helper is over-detecting Pi because it
returns true when isPiSdkLoaded() is true; remove using isPiSdkLoaded() as the
browser detector and make checkPiBrowser() rely solely on a userAgent match
(e.g., /Pi Browser|minepi/i.test(navigator.userAgent)) while still guarding for
navigator === undefined; then update the auto-connect logic that currently calls
checkPiBrowser() (the block that forces Pi auth/auto-connect) to first call the
new userAgent-based check and only attempt auto-connect if the SDK is actually
present (check isPiSdkLoaded() or a window.Pi object) at the point of
connecting, not inside checkPiBrowser().

---

Outside diff comments:
In `@apps/axiomid/src/app/context/wallet-context.tsx`:
- Around line 168-175: The client createAgent function is no longer sending
accessToken but the server endpoints (/api/agent, /api/agent/activate,
/api/agent/main) still require it for pi:* wallets; update the payload sent from
createAgent to include the user's accessToken (e.g., include accessToken from
the user object in the POST body alongside walletAddress and name) or
conditionally include accessToken when user.walletAddress startsWith('pi:'), so
the server can call https://api.minepi.com/v2/me with Authorization: Bearer
${accessToken} and avoid 401s.
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 36463832-b265-4ce4-b0a4-59c0fe9f5953

📥 Commits

Reviewing files that changed from the base of the PR and between 666a731 and 56a13f9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • apps/axiomid/package.json
  • apps/axiomid/src/app/api/pi/payment/cancel/route.ts
  • apps/axiomid/src/app/api/pi/payment/error/route.ts
  • apps/axiomid/src/app/api/pi/payment/incomplete/route.ts
  • apps/axiomid/src/app/context/wallet-context.tsx
  • apps/axiomid/src/components/PiButton.tsx
  • apps/axiomid/src/lib/pi-sdk-hooks.ts
  • apps/axiomid/src/lib/pi-sdk.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/axiomid/package.json

Comment on lines +13 to +20
const response = await fetch(
`https://api.minepi.com/v2/payments/${paymentId}`,
{
headers: {
Authorization: `Key ${apiKey}`,
},
}
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add a timeout to the outbound Minepi fetch in apps/axiomid/src/app/api/pi/payment/incomplete/route.ts.

The handler performs a blocking external call to https://api.minepi.com/v2/payments/${paymentId} without any AbortSignal/timeout, so upstream slowness can tie up request handling and cascade latency.

🔧 Suggested fix
-    const response = await fetch(
-      `https://api.minepi.com/v2/payments/${paymentId}`,
-      {
-        headers: {
-          Authorization: `Key ${apiKey}`,
-        },
-      }
-    );
+    const controller = new AbortController();
+    const timeout = setTimeout(() => controller.abort(), 8000);
+    const response = await fetch(
+      `https://api.minepi.com/v2/payments/${paymentId}`,
+      {
+        headers: {
+          Authorization: `Key ${apiKey}`,
+        },
+        signal: controller.signal,
+      }
+    ).finally(() => clearTimeout(timeout));
📝 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
const response = await fetch(
`https://api.minepi.com/v2/payments/${paymentId}`,
{
headers: {
Authorization: `Key ${apiKey}`,
},
}
);
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 8000);
const response = await fetch(
`https://api.minepi.com/v2/payments/${paymentId}`,
{
headers: {
Authorization: `Key ${apiKey}`,
},
signal: controller.signal,
}
).finally(() => clearTimeout(timeout));
🤖 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 `@apps/axiomid/src/app/api/pi/payment/incomplete/route.ts` around lines 13 -
20, The outbound fetch to `https://api.minepi.com/v2/payments/${paymentId}` has
no timeout/AbortSignal; wrap the call with an AbortController, pass
controller.signal into the fetch options (where Authorization header is set),
create a timer (e.g. const timeout = setTimeout(() => controller.abort(), 5000))
before the fetch and clear it after the response (clearTimeout(timeout)), and
handle the abort case by catching the thrown DOMException/AbortError and
returning an appropriate error response; update the fetch invocation in the
route handler where `fetch(..., { headers: { Authorization: \`Key ${apiKey}\` }
})` appears to include `signal: controller.signal`.

Comment on lines +39 to +43
function checkPiBrowser(): boolean {
if (typeof navigator === "undefined") return false;
return /Pi Browser|minepi/i.test(navigator.userAgent);
}

function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
return Promise.race([
promise,
new Promise<T>((_, reject) =>
setTimeout(() => reject(new Error(`Timed out after ${ms}ms`)), ms)
),
]);
if (isPiSdkLoaded()) return true;
const ua = navigator.userAgent;
return /Pi Browser|minepi/i.test(ua);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

checkPiBrowser() now over-detects and forces Pi auth for regular users.

Using isPiSdkLoaded() as a browser detector (Line 41) becomes true when the SDK script is globally injected, which makes the auto-connect block (Lines 126–128) run for non-Pi visitors and bypass demo flow.

🔧 Suggested fix
 function checkPiBrowser(): boolean {
   if (typeof navigator === "undefined") return false;
-  if (isPiSdkLoaded()) return true;
   const ua = navigator.userAgent;
   return /Pi Browser|minepi/i.test(ua);
 }

Also applies to: 124-128

🤖 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 `@apps/axiomid/src/app/context/wallet-context.tsx` around lines 39 - 43, The
checkPiBrowser() helper is over-detecting Pi because it returns true when
isPiSdkLoaded() is true; remove using isPiSdkLoaded() as the browser detector
and make checkPiBrowser() rely solely on a userAgent match (e.g., /Pi
Browser|minepi/i.test(navigator.userAgent)) while still guarding for navigator
=== undefined; then update the auto-connect logic that currently calls
checkPiBrowser() (the block that forces Pi auth/auto-connect) to first call the
new userAgent-based check and only attempt auto-connect if the SDK is actually
present (check isPiSdkLoaded() or a window.Pi object) at the point of
connecting, not inside checkPiBrowser().

…handler, iframe detection

- patchPostMessageForSandbox() bridges cross-origin postMessage in iframe
- listenForPiSDKMessages() responds to @pi:app:sdk:communication_information_request
- SandboxProvider initialized on mount in layout.tsx
- shouldUseSandbox() detects iframe from sandbox.minepi.com
- Add NEXT_PUBLIC_DEV_URL to .env.example
Comment thread apps/axiomid/src/lib/pi-sdk.ts Outdated
Comment thread apps/axiomid/src/lib/pi-sdk.ts Outdated
Comment on lines +1 to +12
"use client";

import { useEffect } from "react";
import { initSandboxCompatibility } from "@/lib/pi-sandbox";

export function SandboxProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
initSandboxCompatibility();
}, []);

return <>{children}</>;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 security window.postMessage is patched unconditionally for every visitor

SandboxProvider wraps the entire app and always calls initSandboxCompatibility(), which replaces window.postMessage with a version that forwards every outgoing message to window.parent with "*" as targetOrigin whenever the app is inside an iframe (see pi-sandbox.ts lines 4–10). This runs for all users regardless of whether sandbox mode is active, broadcasting any postMessage sent in an iframe context — including auth tokens or other app state — to an arbitrary parent origin. Consider gating this behind the same sandbox flag used elsewhere.

Fix in Conductor

Comment on lines +13 to +31
const response = await fetch(
`https://api.minepi.com/v2/payments/${paymentId}`,
{
headers: {
Authorization: `Key ${apiKey}`,
},
}
);

if (!response.ok) {
return NextResponse.json(
{ error: "Failed to fetch incomplete payment" },
{ status: response.status }
);
}

const paymentData = await response.json();

return NextResponse.json({ success: true, payment: paymentData });
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Incomplete payment is fetched but never approved or completed

The Pi Network server-side flow for an incomplete payment requires calling /v2/payments/{id}/approve and then /v2/payments/{id}/complete (with the on-chain txid). This handler only performs a GET to fetch the payment status and returns the result — it never calls approve or complete. Any payment surfaced by the SDK as incomplete during pi.connect() will remain permanently stuck in the pending state, as neither the app nor the SDK will make further attempts to resolve it.

Fix in Conductor

const username = authResult.user.username || uid;

onLog?.(`[PI-SDK] 👤 User: ${username} (UID: ${uid})`);
onLog?.(`[PI-SDK] 🔑 Access token: ${authResult.accessToken.slice(0, 20)}...`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 security Access token fragment surfaced in dashboard terminal

The first 20 characters of the Pi access token are written to onLog, which flows through pushLogwalletLogs → the visible dashboard terminal via the useEffect in dashboard/page.tsx. Any person with access to the dashboard (or observing the screen) sees the token fragment. While only the first 20 bytes are exposed, even a partial JWT token can reveal payload claims; more importantly, establishing the habit of logging tokens makes future scope creep likely. runWalletTest on line 185 makes the same mistake with 30 characters. Both log lines should be removed or replaced with a generic confirmation message.

Fix in Conductor

Comment on lines +65 to +68
if (typeof Pi.init === "function") {
onLog?.("[PI-SDK] 🔄 Calling Pi.init({ version: \"2.0\" })...");
await Pi.init({ version: "2.0" });
onLog?.("[PI-SDK] ✅ Pi.init() completed");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Pi.init() is called without a sandbox flag in connectPi, so the Pi SDK always initialises against the production network regardless of NEXT_PUBLIC_PI_SANDBOX. Any payment made through the sandbox flow will be submitted to the live Pi Network, turning test transactions into real ones for affected users.

Suggested change
if (typeof Pi.init === "function") {
onLog?.("[PI-SDK] 🔄 Calling Pi.init({ version: \"2.0\" })...");
await Pi.init({ version: "2.0" });
onLog?.("[PI-SDK] ✅ Pi.init() completed");
if (typeof Pi.init === "function") {
const sandbox = process.env.NEXT_PUBLIC_PI_SANDBOX === "true";
onLog?.(`[PI-SDK] 🔄 Calling Pi.init({ version: "2.0", sandbox: ${sandbox} })...`);
await Pi.init({ version: "2.0", sandbox });
onLog?.("[PI-SDK] ✅ Pi.init() completed");

Fix in Conductor

Comment on lines +31 to +35
React.useEffect(() => {
if (ready && !connected && !error) {
connect();
}
}, [ready, connected, error, connect]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Pi authentication fires automatically on SDK ready without user interaction

The effect calls connect() the moment ready becomes true and the user is not yet connected. This means rendering <PiButton> anywhere in the app silently triggers the full Pi OAuth flow (prompts, authentication dialog) before the user has clicked anything. This will surprise users who navigate to a page that merely displays this component — the Pi Browser auth prompt appears without any interaction. Consider removing the auto-connect effect and relying solely on the explicit onClick → purchase handler, or at minimum gate it behind a user-intent signal.

Fix in Conductor

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