Skip to content

/attester: read-only payout-account checks (replace the stub) #42

Open

Description

@abrcrmb

Task

app/attester/page.tsx is a stub. Build the attester-facing read-only tool: given a Stellar account and a USDC issuer, report whether the payout account exists, holds enough XLM for the reserve, and has an authorised USDC trustline — the same answers check-attester-readiness.ts gives on the command line, in a browser.

Context

  • app/attester/page.tsx:5 renders <PageStub titleKey="pages.attester.title" /> — nothing else.
  • Equivalent logic exists as a script: kinlock-registry/scripts/check-attester-readiness.ts, which checks account existence, XLM float and an authorised trustline; it deliberately does not automate the test release (:6-9).
  • app/payee/page.tsx:10 and its usePayeeDashboard.ts:10,48 show this repo's shape for a data-fetching page: hooks plus shared components, SDK/adapter for anything chain-facing.
  • Registry data drives everything country-ish: AGENTS.md §3.11 — no hard-coded country, currency or anchor; read from registry data.
  • messages/en.json (293 keys) + lib/i18n/messages.ts:15, enforced by lib/i18n/inline-strings.test.ts:22.

Before you start: mandatory context review

Kinlock moves other people's money, so the docs are binding constraints, not background reading.
They record decisions this task must not silently reopen. Read them in this order and tick each box:

  • docs/ARCHITECTURE_ESSENTIALS.md — start here, it is short (AGENTS.md §1)
  • docs/ARCHITECTURE.md — only the sections named in Context above
  • docs/PRD.md — if this task touches user-facing behaviour or scope
  • AGENTS.md §3 (hard rules), §4 (deferred list), §8 for this repo, §12 (roadmap rule)
  • ROADMAP.md — the row(s) listed under Related roadmap rows, including their Depends on

In your first PR comment, name the hard-rule numbers or doc sections that constrain this task. That
line is how we know the review happened.

Then:

  • If code and docs disagree, or two docs disagree: stop and say so on this issue. Do not pick one
    silently, and do not "fix" either side without approval (AGENTS.md §1).
  • If the task looks like it needs anything from the deferred list (§4), a change to a hard rule (§3),
    or a new dependency (§2): raise it here first. Those need a human; deciding them is not your job.
  • If a row you are told to finish is BLOCKED or depends on a row that is not DONE, say so here
    instead of starting.

Why we need this

  • An attester verifying a school's payout account today has to run a Node script. That is the difference between "we can onboard payees in the pilot" and "only the maintainers can".
  • Hard rule: a lock can only pay out to a payout address that can actually receive USDC. A missing or unauthorised trustline means the school cannot claim, and AGENTS.md §8.2 requires preflight to see it. A read-only check page is the cheapest way to surface it early.
  • M3-12 is the last unclaimed piece of the attester flow before the pilot.

Scope

In scope

  • app/attester/** (page, form, results view — new sub-components allowed)
  • lib/ read-only account-check helper, ideally mirroring check-attester-readiness.ts's three checks
  • messages/en.json keys
  • Tests for the checks and the view

Do not touch (security-sensitive or generated; changing these needs a human first — AGENTS.md §2, docs/project_structure.md §8)

  • app/claim/** and lib/claim-links/** — the claim-link fragment (#r=…&s=…) must never reach a server, log or analytics tool (hard rule 5)
  • next.config.ts, proxy.ts / middleware.ts, CSP headers
  • lib/wallet/** and any direct contract call site — money-moving calls go through the SDK and the wallet adapter only
  • app/send/**, app/locks/**, app/payee/** action logic — release, refund and decline paths stay with maintainers
  • package.json, pnpm-lock.yaml (no new dependencies without approval), .github/workflows/**
  • anything that signs, submits a transaction, or moves a trustline — this page is read-only. A "test release" button is out of scope and needs a decision (M1-26)
  • kinlock-registry data files — read them, never write

Suggested implementation

  1. Read ARCHITECTURE_ESSENTIALS.md, AGENTS.md §3 (rules 3, 5, 11) and §8.3–8.4, plus kinlock-registry/scripts/check-attester-readiness.ts so your results match it field for field.
  2. Use Horizon's public read endpoints through the existing SDK/lib patterns; validate input with the SDK's StrKey utilities (AGENTS.md §8.2), and never accept or ask for a secret key, seed phrase or signing — if the UI would tempt a user to paste one, redesign it.
  3. Three explicit results: account exists / does not; XLM float vs the reserve requirement (state where the reserve number comes from; if you cannot verify it, show the float and say the threshold needs checking rather than inventing it); USDC trustline absent / unauthorised / authorised — show the issuer, not just "USDC" (§8.3).
  4. Say what the attester should do about each failure in plain language, sourced from ATTESTER_CHECKLIST.md.
  5. No country, currency or anchor hard-coded (§3.11): the USDC issuer comes from registry or config, not a literal in your component.
  6. Log nothing personal, and never log an address alongside payee details (hard rule 5).
  7. Accessibility from the start: labels, focus order, role="status" on results, contrast — copy components/indexer/LagNotice.tsx:8.

A different approach is usually welcome — post it here before building and we will answer. This is a
suggestion, not a spec.

Edge cases and traps

  • Trustline absent and trustline present-but-unauthorised are different answers with different fixes. Collapsing them into “not ready” is the main failure mode of this page.
  • The XLM reserve number: state where it comes from. If you cannot verify it from an official source, show the float and say the threshold needs checking — do not invent a figure (§10).
  • An invalid StrKey input must be refused by validation before any network call, with a plain message.
  • Never a field that invites a secret key or seed phrase, and never log an address beside payee details (hard rule 5).
  • The USDC issuer comes from registry or config. A literal issuer or country in a component breaks §3.11.

Done means

  • /attester is a working page, no stub, keyboard- and screen-reader-navigable
  • The three checks return the same verdicts as check-attester-readiness.ts for the same inputs — show at least two side-by-side examples in the PR
  • No signing, no transaction submission, no secret input anywhere in the flow
  • Asset is always shown as code and issuer; no country or currency literal in the component
  • All copy in messages/en.json; tests cover each verdict branch; pnpm lint/typecheck/test/build pass
  • M3-12 flipped only if the parity criterion is met; otherwise IN PROGRESS with the remainder as a new row

Tests

Unit tests for the check logic against fixture Horizon responses (no live network in tests), and one case proving an unauthorised trustline is reported distinctly from a missing one. A DOM testing library would be a new dev dependency — ask on the issue before installing.

Documentation

Add a section to this repo's README describing the page, and note it in ATTESTER_CHECKLIST.md (canonical Kinlock-Org/.github/docs) so the two stay in step. If you introduce an architectural choice (where checks run, which endpoint), open an ADR draft and ask.

Verify before you open the PR

pnpm install
pnpm lint
pnpm typecheck
pnpm test
pnpm build
pnpm dev  # then open /attester and check three accounts against pnpm --dir ../kinlock-registry check-attester-readiness

Run every command above and paste the real output in the PR. We do not accept "should pass" —
AGENTS.md §6: "You actually ran the commands."

Opening the pull request

  • Assignment is required before you start. Comment on this issue and wait to be assigned. Do not
    open a PR against an issue someone already holds — if it has been quiet, ask us to release it first.
  • Closes #42 in the PR description, so the issue and the PR link automatically.
  • Branch feat/…, fix/…, docs/…, test/… or chore/…. Commit subject example:
    feat(app): /attester read-only payout-account checks
  • M3-12 → DONE only with the parity evidence; Changelog entry; Last updated. Do not touch M1-26 — it is registry-owned.
  • Follow .github/PULL_REQUEST_TEMPLATE.md (AGENTS.md §7). In "Hard rules / invariants touched",
    give numbers, not prose.
  • Small and focused: one logical change. Do not mix a refactor into it.
  • Do not add the security-sensitive label — that is the maintainer's flag for contract-logic PRs.
  • Do not reformat or touch files outside Scope. Unrelated diff noise costs review time and can
    break the roadmap merge.
  • Never commit to main; open a PR. Never force-push.
  • Evidence for the PR: Attach screenshots of each trustline verdict (absent, unauthorised, authorised) and the side-by-side parity output against check-attester-readiness.ts.

How we review

Two maintainers: one reviews correctness against the hard rules and the roadmap row's Done when, the
other reviews diff scope and whether the tests were actually run. Order is hard-rule correctness → tests
really executed → diff stays inside Scope → style last. First response on a PR: 2 business days.

What fails review fastest: a change outside Scope, a deleted or weakened assertion, a claim with no
command output behind it, and a ROADMAP.md with no Changelog entry (AGENTS.md §12).
For this issue, look first at the parity evidence against the script for the same inputs, and that nothing in the flow signs, submits or asks for a secret.

Tier and effort

tier/high · roughly 3–5 days. Highest-value issue in this repo's backlog, and the one most likely to need a maintainer conversation — start it early.. Wave complexity tag: High = 200 points — the published mapping is Trivial 100 · Medium 150 · High 200
(Drips' maintainer guide); the cycle's
own values are confirmed on drips.network/wave/stellar before
the Wave opens. Kinlock's tier rules and what each tier means for review:
docs/wave-issue-format.md.

Related roadmap rows

M3-12 — /attester tooling: read-only account checks (TODO)
M1-26 — ATTESTER_CHECKLIST.md and the readiness script (IN PROGRESS)

M3-12 depends on M1-26, which is IN PROGRESS. The three checks this page needs already exist in kinlock-registry/scripts/check-attester-readiness.ts, so you can build against them; the checklist's test-release half is unautomated on purpose, so do not try to surface it — confirm the parity target on the issue before you finish.

Questions

Comment on this issue — a maintainer answers within 2 business days. Say "I'll take this" so we can flag it on
the Wave board and stop two people doing the same work. If you get half way and stall, post what you
have as a draft PR rather than disappearing; a blocked draft is more useful to us than silence.

Activity

  1. added
    waveScoped for Drips Wave contributors
    area:appOwnership: kinlock-app
    tier/highWave tier: multi-file, needs design judgement or unfamiliar toolchain; claim it before starting
    on Oct 8, 2026
  2. jdjioe5-cpu commented on Oct 17, 2026

    @jdjioe5-cpu

    I'll take this!

    Reviewing constraints: ARCHITECTURE_ESSENTIALS.md, AGENTS.md §3 (Hard Rules 3, 5, 11), §8.3 (app rules: no inline strings, asset code + issuer, plain language, accessibility), §8.4 (registry data parity), and docs/project_structure.md §8.
    Related roadmap row: M3-12. Starting implementation of /attester read-only payout-account checks, matching check-attester-readiness.ts verdicts, with comprehensive unit tests and zero transaction/signing capabilities.

  3. jdjioe5-cpu commented on Oct 17, 2026

    @jdjioe5-cpu

    Delivered in PR #50 !

    Summary of Delivery:

    • Read-Only Payout Account Checker (/attester): Fully replaces the placeholder PageStub with an interactive, accessible tool (app/attester/page.tsx, AttesterChecker.tsx, useAttesterChecker.ts, lib/attester/readiness.ts, lib/attester/strkey.ts).
    • Exact Field & Verdict Parity with check-attester-readiness.ts:
      1. Account existence (account.exists);
      2. Native XLM float vs reserve requirement (minRequired = 2 XLM);
      3. USDC trustline verification strictly distinguishing between missing (absent), unapproved (unauthorised), limit reached (no_room), and authorised with room, with actionable remediation guidance for each case.
    • Strict Compliance: Zero private keys, seed phrases, or transaction submission; zero country or currency literals (asset code and issuer derived dynamically); fully externalized i18n copy in messages/en.json; role="status" accessibility; and 100% clean Next.js 16.3.8 production build.
    • Verification: 16/16 test files (178 unit tests) pass green, 0 lint warnings across 88 files, 0 type errors. Side-by-side evidence with live testnet accounts provided in the PR.
    • Roadmap & Docs: Updated README.md, advanced M3-12 to DONE, and recorded Changelog entry.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:appOwnership: kinlock-apptier/highWave tier: multi-file, needs design judgement or unfamiliar toolchain; claim it before startingwaveScoped for Drips Wave contributors

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions