Skip to content

feat(example): perch guided tour — smart accounts → OZ → Nido → perch → M-of-N, on testnet - #191

Open
willemneal wants to merge 4 commits into
feat/testkitfrom
feat/perch-onchain-status
Open

feat(example): perch guided tour — smart accounts → OZ → Nido → perch → M-of-N, on testnet#191
willemneal wants to merge 4 commits into
feat/testkitfrom
feat/perch-onchain-status

Conversation

@willemneal

@willemneal willemneal commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

A single guided, six-act tour teaching smart accounts, the OpenZeppelin model, what Nido adds, how perch makes authorization easier and safer, and how a rule can require M-of-N signatures — each ending in a live testnet demonstration. Styled in Nido's "Warm Nest" design language. Stacks on #189 (testkit); supersedes #190 (the standalone console is folded in, so the repo ships one example).

Six acts

  1. One key, total power — a G-address is all-or-nothing.
  2. The account becomes a program — a smart account runs your __check_auth.
  3. OZ gives you the vocabulary — Signer + ContextRule + policies; the hand-written-Policy contrast + INV-2 footgun.
  4. Nido makes it human — signers across every verifier: secp256r1 (live), post-quantum ML-DSA-65 (· sim, Guest-wasm ML-DSA-65 verifier contract (pre-CAP-0087 groundwork) #143), a Delegated → another G-account treasury.
  5. perch — the account's full policy (perch composing with OZ-native policies) rendered as a signers × rules matrix, then describe → attenuate (narrow accepted / widen refused) → enforce live on testnet.
  6. Add signers · M-of-N — the same account model holds several co-signers and can require a quorum. The policy matrix gains a 2-of-3 rule via Nido's OZ multisig policy — perch scopes what a key may do; the threshold policy governs how many must sign, composed on one account. Proven live.

Real testnet proof

Act 5 (perch scope): post → SUCCESS (ab3d7802…); clear → FAILED, interpreter enforce trapped Denied (4170d3d2…). Policy compiled by perch perch-plan (stellar-registry/perch#31).

Act 6 (M-of-N quorum): on the 2-of-3 account CCJLM2X6…, post signed by 2 of 3 → SUCCESS (21302c3e…); signed by 1 → FAILED, multisig enforce says threshold not met (6f0265f9…). The M assertions over one auth digest ride a single AuthPayload (injectSignedAuthPayload); scripts/prove-threshold.ts deploys the account and reproduces both.

Client-side footprint fix: recording sim skips __check_auth, so re-simulate the signed tx (enforcing) and submit with that footprint — untested until here since every OZ/perch/nido test mocks auth.

🤖 Generated with Claude Code

}

export const server = new rpc.Server(RPC_URL);
export const poster = secp256r1Keypair(POSTER_SEED);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Low Severity severity

Signature & Authentication: Predictable signer key enables impersonation in poster

The demo derives the poster signing key from a committed, deterministic seed (POSTER_SEED). Because this seed is public, any third party can generate the same secp256r1 keypair and produce valid signatures that the on-chain WebAuthn verifier will accept, allowing them to impersonate the demo signer for any actions permitted by the perch policy (here: spamming board.post(..., self) on testnet).

Do not commit deterministic signing seeds for accounts intended to represent a single principal. Generate the key per user/session, or load the seed from a non-committed secret (env/keystore). If this must remain deterministic for reproducibility, hard-fail outside testnet and clearly isolate the demo account from anything of value.


Actions
  • Reply /almanax ask <question> to ask a follow-up question.
  • Reply /almanax dismiss [<reason>] and it won't appear again in future scans.
  • Reply /almanax resolve [<reason>] to mark the finding as resolved.
  • Reply /almanax severity <level> [<reason>] to override the severity.

@github-actions

Copy link
Copy Markdown

Preview deployed!

https://191.nido.fyi

Account URLs use numeric preview suffixes, for example <contract-address>--191.nido.fyi.

@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown

Example dApp preview deployed!

status-message runs on testnet, wallet = THIS PR's preview (https://191.nido.fyi); the perch tour drives perch on real testnet. The live home is https://nidohq.github.io/nido/ once merged.

const jsonView = el('div', 'codebox');
jsonView.innerHTML = [
'{ <span class="k">"signer"</span>: <span class="s">"ci"</span>, <span class="k">"scope"</span>: <span class="s">"board"</span>,',
` <span class="k">"functions"</span>: [${state.attnFns.map((f) => `<span class="s">"${f}"</span>`).join(', ')}],`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Low Severity severity

Input and Parameter Validation: HTML injection via attnFns in act5

act5 renders state.attnFns into innerHTML without escaping. If attnFns ever becomes attacker-influenced (e.g., future in-browser policy authoring, URL/state hydration, or any injected script), this enables DOM XSS in a page that can sign and submit real Soroban testnet transactions.

Avoid innerHTML for dynamic values; render function names via textContent/DOM nodes, or strictly HTML-escape f before interpolation (e.g., replace &, <, >, ", '). Treat any future policy-authoring inputs as untrusted.


Actions
  • Reply /almanax ask <question> to ask a follow-up question.
  • Reply /almanax dismiss [<reason>] and it won't appear again in future scans.
  • Reply /almanax resolve [<reason>] to mark the finding as resolved.
  • Reply /almanax severity <level> [<reason>] to override the severity.

app.append(stage);
}
render();
(window as unknown as { __tour: unknown }).__tour = { go, state };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Low Severity severity

Signature & Authentication: Exposed fee signer in global __tour in render

The app exports { go, state } onto window.__tour; state includes feeKp (a Keypair used to sign and pay fees). Any XSS gadget or third-party script running in-origin can read/exfiltrate this secret and use it to submit arbitrary fee-paying transactions during the session.

Remove the global export in production builds, or export a redacted/debug-only view that never includes secrets (Keypair). If you need debugging, gate it behind an explicit dev flag and only expose non-sensitive fields.

Suggested change
(window as unknown as { __tour: unknown }).__tour = { go, state };

Actions
  • Reply /almanax ask <question> to ask a follow-up question.
  • Reply /almanax dismiss [<reason>] and it won't appear again in future scans.
  • Reply /almanax resolve [<reason>] to mark the finding as resolved.
  • Reply /almanax severity <level> [<reason>] to override the severity.

willemneal and others added 2 commits August 17, 2026 10:52
… on testnet

A five-act guided tour (in Nido's "Warm Nest" design language) that scopes a CI
key with perch, from a raw keypair to a policy the chain enforces:

  1 One key, total power  2 The account becomes a program  3 OZ vocabulary +
  the hand-written-Policy contrast  4 Nido: signers across every verifier
  (secp256r1, post-quantum ML-DSA-65, Delegated → another account)  5 perch:
  the full policy (perch composing with OZ-native policies) → describe →
  attenuate (machine-checked narrowing) → enforce live on testnet
  (post allowed, clear denied).

Uses @nidohq/testkit for real reachable-calls/attenuation and @nidohq/passkey-sdk
+ @stellar/stellar-sdk for the live invoke (footprint-resimulation fix for
policy-gated accounts). Playwright e2e drives all five acts + the on-chain
enforce; snapshots in artifacts/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Builds + nests examples/perch-status-onchain alongside status-message (Pages
home at /<repo>/perch-status-onchain/, and the per-PR Cloudflare preview),
announcing its URL in the preview comment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@willemneal
willemneal force-pushed the feat/perch-onchain-status branch from 1ca2fed to 2d153f8 Compare August 17, 2026 17:12
@willemneal
willemneal changed the base branch from feat/perch-authz-console to feat/testkit August 17, 2026 17:12
@willemneal willemneal changed the title feat(example): perch enforced on-chain in a status-message dApp (testnet) feat(example): perch guided tour — smart accounts → OZ → Nido → perch, on testnet Aug 17, 2026
willemneal and others added 2 commits August 18, 2026 14:30
Turns Act 5 §① from a static "describe" panel into a live policy builder: a
two-column card where you author the CI key's rule with toggles and watch the
whole document re-derive on every change.

Controls (left): the functions the key may call (post / clear, with clear
flagged as the risky one), the `args[1] = self` author guard, and an optional
`not-after-ledger` expiry with a number input. Output (right): the exact
kebab-case PolicyDoc (syntax-highlighted), its live `doc_hash`, the reachable
calls read straight off the doc, and a dynamic safety read that names the
problem — over-broad (can `clear`), open-author (no self guard), or "tightly
scoped" when it's `post()`-only with the self guard on.

`policyModel.ts` gains `BuildConfig` + `buildDoc(cfg)` (functions/self-arg/
expiry) with `ciDoc` retired in favor of it; `main.ts` swaps `state.attnFns`
for `state.build` and threads it through describe, attenuation (narrow snaps to
publish-only, widen is refused — unchanged assertions), and the doc/hash/reach
views; a small `hjson` highlighter renders the wire doc. Attenuation and the
on-chain enforce step are unchanged and still drive the same deployed policy.

Adds `.tog`/`.toggles` builder styling in the Warm Nest system. Playwright
gains an offline builder check (over-broad → toggle clear → tightly scoped →
restore) ahead of the existing narrow/widen/enforce flow. typecheck + build
clean; two artifact screenshots show the default and scoped+expiry states.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extends the perch tour with a sixth act that shows a Nido account holding
several co-signers and requiring a quorum. perch scopes *what* a key may do;
Nido's OZ multisig policy governs *how many* must sign — composed on one
account, no bespoke account code either way.

- policyModel: a rendered policy panel (signers × rules matrix) reused by
  Act 5's full-policy view and Act 6; adds the 2-of-3 `ops-quorum` rule.
- perchOnchain: `proveThreshold(feeKp, keyCount)` drives the 2-of-3 account's
  Default rule live — M assertions over one auth digest in a single
  AuthPayload (injectSignedAuthPayload). Mirrors the proven invokeBoardCall
  footprint flow; the below-threshold case borrows the passing footprint to
  land a real, cleanly-failed on-chain tx.
- scripts/prove-threshold.ts: deploys the 2-of-3 account (multisig policy,
  threshold=2) and reproduces allow(2)/deny(1) on testnet.
- e2e: Act 6 steps prove 2-of-3 allowed / 1-of-3 denied live; regenerates
  08/09/10 screenshots. Drops two stale pre-Act-6 artifacts.
- README: six-act framing, Act 6 deployed pieces + proof tx hashes.

Proven live on testnet: account CCJLM2X6…, 2-sig allow 21302c3e…,
1-sig deny 6f0265f9… (FAILED).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AVMqraqa1ERu7HATfbNiEj
@almanax-ai

almanax-ai Bot commented Aug 22, 2026

Copy link
Copy Markdown

Plan expired

Your subscription has expired. Please renew your subscription to continue using CI/CD integration and other features.

@willemneal willemneal changed the title feat(example): perch guided tour — smart accounts → OZ → Nido → perch, on testnet feat(example): perch guided tour — smart accounts → OZ → Nido → perch → M-of-N, on testnet Aug 22, 2026
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