Skip to content

fix: NEXT_PUBLIC_INDEXER_URL wrongly required, broke verify/send/request - #27

Merged
YazarAyobami merged 1 commit into
mainfrom
fix/indexer-url-optional
Oct 8, 2026
Merged

YazarAyobami merged 1 commit into
mainfrom
fix/indexer-url-optional

Conversation

@abrcrmb

@abrcrmb abrcrmb commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

What and why

Owner reported /send, /verify, and /request "not functioning" even after the previous hotfix (#26). That hotfix was correct but only treated a symptom; this is the actual root cause, one level deeper.

lib/config.ts's PublicConfigSchema required NEXT_PUBLIC_INDEXER_URL. But @kinlock/sdk's own KinlockConfig types it as indexerUrl?: string (optional), precisely because chain-only operations (verifyReceipt, getLock, release, refund, decline, createLock) don't need an indexer. Since no indexer is deployed yet (M2-18, still TODO) and the env var genuinely isn't set in this Vercel project, every publicConfig()/kinlockConfig() call threw before any RPC request was even attempted. next.config.ts's rewrites already handled this correctly (if (!indexer) return {...empty rewrites...}), so this was a kinlock-app-only over-strict validation bug, not a real product or infrastructure gap.

Confirmed with Playwright against production: clicking "Check" on /verify made zero network requests and always produced the generic error, because verifyReceipt → kinlockConfig() → loadPublicConfig() threw synchronously before fetch was ever called.

Changes

  • lib/config.ts: NEXT_PUBLIC_INDEXER_URL: z.url() → z.url().optional().
  • lib/config.test.ts: regression test asserting a missing indexer URL doesn't throw.

No other file needed a change: kinlockConfig()'s indexerUrl assignment and lib/indexer.ts's listPayees() already handle a possibly-undefined value correctly by type (TypeScript confirms this; tsc --noEmit is clean).

Hard rules / invariants touched

None. This restores chain-is-truth behavior (hard rule 3): verify/release/refund/decline read chain state directly and were being blocked from doing so by an app-level config bug, not a chain or auth issue.

Tests

pnpm lint, pnpm typecheck, pnpm test (94/94, new test included), pnpm build all green.

Reproduced against the exact production condition (dev server with the real, indexer-less .env.local) before and after:

  • Before: /verify's Check button made 0 RPC requests, generic error every time.
  • After: a real request to soroban-testnet.stellar.org, correctly resolving "Not valid / No payment with this transaction and event number exists" for a made-up hash.
  • /send and /request still correctly show "The list of payees can't be loaded right now" — that part is accurate (no indexer exists yet), not a bug, and is unchanged by this fix.

Docs / ADR

None needed.

Roadmap

No row status change. Changelog entry added explaining the full diagnosis.

Assumptions and open questions

This unblocks every chain-only SDK call app-wide (not just /verify): /locks/[id] refund, /claim/[id] release/decline, and /send's actual createLock once a payee can be selected. The payee-list UI (/send, /request, /payee) is still correctly blocked until M2-18 (indexer deployment) ships; that's a real, separate, larger piece of infrastructure work (a database, a long-running service, hosting/secrets decisions) that needs the owner's input on hosting choice and isn't something to provision unilaterally.

Deploying this to kinlock-app.vercel.app via vercel --prod immediately after merge, given production is currently broken for this.

lib/config.ts required NEXT_PUBLIC_INDEXER_URL, but @kinlock/sdk's own
KinlockConfig.indexerUrl is typed optional: chain-only operations
(verifyReceipt, getLock, release, refund, decline, createLock) don't
need an indexer. Since no indexer is deployed yet (M2-18) and the env
var genuinely isn't set, every publicConfig()/kinlockConfig() call
threw before any RPC request was attempted. /verify's Check button
made zero network requests as a result.

next.config.ts's rewrites already treated a missing indexer URL as
expected; this was an app-side validation bug, not a real product
gap. Changed the field to z.url().optional() to match the SDK's own
contract, and added a regression test.
@abrcrmb
abrcrmb requested a review from a team as a code owner October 8, 2026 11:24
@YazarAyobami
YazarAyobami merged commit 70978ff into main Oct 8, 2026
4 checks passed
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.

2 participants