diff --git a/.claude/skills/orbit-station-audit/SKILL.md b/.claude/skills/orbit-station-audit/SKILL.md new file mode 100644 index 000000000..3244fe927 --- /dev/null +++ b/.claude/skills/orbit-station-audit/SKILL.md @@ -0,0 +1,144 @@ +--- +name: orbit-station-audit +description: >- + Build and run the Orbit station configuration audit (`orbit-cli audit`) + end-to-end against a live station, including the icp-cli Internet Identity + setup that reliably trips people up. Use this whenever the user wants to + audit, sanity-check, or security-review an Orbit station or wallet — e.g. + "audit my Orbit wallet", "run orbit-cli audit on station ", + "check my Orbit station for misconfigured approval policies / empty quorums", + or any time they pair an Orbit station canister id with auditing, approval + quorum, or permission checks. Reach for this even if they don't say + "orbit-cli" by name. It covers building the CLI from source (the globally + installed orbit-cli is usually too old to have the `audit` subcommand), + obtaining an identity the station recognizes as a member, and reading the + report. +--- + +# Orbit station audit + +`orbit-cli audit` runs read-only sanity checks against a live station and +prints a severity-sorted report — safe to run any time, since it only issues +`list_*` queries and mutates nothing. Flags, exit codes, and report format are +documented in [`cli/src/audit/README.md`](../../../cli/src/audit/README.md); +read that for reference. This skill is the *operational* guide — it gets you +from a fresh checkout to a successful run and front-loads the three things that +actually derail a first one. + +## The three gotchas (read these first) + +1. **Don't rely on the global `orbit-cli`.** A global on `PATH` may be from an + older checkout that predates the `audit` subcommand (unknown-command error). + Build and run this repo's `cli/dist/cli.js` instead — what the helper script + does — so there's no doubt about which version you're invoking. +2. **Internet Identity principals are per-origin.** The same II anchor yields a + *different* principal for every app origin. The audit must be called by a + principal the station knows as a member, so the identity has to be derived + from the origin the wallet itself uses. In production that origin is + **`orbitwallet.io`** (pinned as `derivationOrigin` in + [`apps/wallet/src/configs/init.config.ts`](../../../apps/wallet/src/configs/init.config.ts)), + *not* the URL bar. Pass the bare host `orbitwallet.io` to `--app` (no + `https://`, even though the config shows a scheme). Deriving from anything + else (`app.orbit.global`, `oisy.com`, the default `cli.id.ai`) gives a + stranger principal that the station will reject. +3. **`icp identity link web` waits for an Enter keypress** before it opens the + browser. Running it non-interactively, pipe a newline in so it proceeds: + `printf '\n' | icp identity link web ...`. The human still completes the + actual sign-in in the browser. + +## Prerequisites + +- Node 20 and pnpm 9 (the repo pins `pnpm@9.12.2` via `packageManager`; + Corepack normally handles this). +- `icp-cli` on `PATH` (`brew install icp-cli`, or + ) — needed for `--identity-source icp`. +- A working copy of the Orbit repo. If you aren't in one: + `git clone https://github.com/dfinity/orbit && cd orbit`. + +## Step 1 — Build the CLI from source + +The helper script does this for you (install if needed → build → run), so +prefer it: + +```bash +.claude/skills/orbit-station-audit/scripts/run-audit.sh --help +``` + +Equivalent by hand, if you'd rather see each step: + +```bash +pnpm install # once, if node_modules is missing +pnpm --filter orbit-cli build # emits cli/dist/cli.js +node cli/dist/cli.js audit --help +``` + +Run the freshly built `cli/dist/cli.js` directly so there's no doubt about +which build is on `PATH`. + +## Step 2 — Get an identity the station recognizes + +The audit signs its calls with an identity that must be a station **member** +(admin-tier users have the required read access by default). For an +Internet-Identity-based wallet, that means an `icp-cli` identity derived from +`orbitwallet.io` (gotcha 2). + +First, reuse an existing one if you already have it: + +```bash +icp identity list +icp identity principal --identity # for each plausible candidate +``` + +If a candidate's principal already matches the principal shown in your wallet +UI under **Settings → Identity**, use that name and skip to Step 3. (Identities +bound to other origins — `oisy.com`, the default `cli.id.ai` — will *not* match.) + +Otherwise create one bound to the wallet's origin: + +```bash +printf '\n' | icp identity link web orbit-audit --app orbitwallet.io +icp identity principal --identity orbit-audit +``` + +A browser opens to `id.ai`. **Sign in with the same Internet Identity anchor +you use to log into Orbit.** The printed principal should match your wallet's +Settings → Identity. The *definitive* test, though, is Step 3: if the audit's +read calls are authorized, you are a member. + +If the principal doesn't match (or Step 3 returns an authorization error), +delete it and re-link trying a different origin or anchor: + +```bash +icp identity delete orbit-audit # note: `delete`, not `remove` +printf '\n' | icp identity link web orbit-audit --app app.orbit.global +``` + +`orbitwallet.io` is correct for the current production wallet; `app.orbit.global` +is the fallback to try if the station predates that derivation origin. + +## Step 3 — Run the audit + +```bash +.claude/skills/orbit-station-audit/scripts/run-audit.sh \ + --station \ + --identity orbit-audit \ + --identity-source icp \ + --output ~/Downloads/orbit-audit-$(date +%F).txt +``` + +Every flag after the script name is forwarded verbatim to `orbit-cli audit`, so +swap in whichever identity matched in Step 2. `--network` defaults to `ic` (pass +`--network local` for a local replica), and dropping `--output` prints to +stdout. `--output` writes internal station metadata (principals, policy ids) — +keep it private. Exit code `2` means at least one blocker (full table in the +README). + +## Troubleshooting + +- **`Unauthorized` / read calls rejected** — the calling principal isn't a + station member. Re-check Step 2: right anchor, right `--app` origin, and the + principal actually matches your wallet UI. +- **`icp identity store not found`** — `icp-cli` isn't installed, or you used + `--identity-source icp` without it. Install icp-cli, or fall back to a dfx PEM + with `--identity-source dfx` (use a *plaintext* identity — passphrase-protected + PEMs aren't supported; see the README). diff --git a/.claude/skills/orbit-station-audit/scripts/run-audit.sh b/.claude/skills/orbit-station-audit/scripts/run-audit.sh new file mode 100755 index 000000000..dc77ef5ba --- /dev/null +++ b/.claude/skills/orbit-station-audit/scripts/run-audit.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# +# Build the Orbit CLI from source and run `orbit-cli audit`. +# +# Why build from source instead of the global `orbit-cli`: a global on PATH may +# be from an older checkout that predates the `audit` subcommand. Building here +# keeps us on the version in this repo, with no doubt about what's invoked. +# +# Usage: +# run-audit.sh --station [--identity ] [--identity-source icp] \ +# [--network ic] [--output ] ... +# +# Everything you pass is forwarded verbatim to `orbit-cli audit`, so any flag +# the audit accepts works here. With no args it prints the audit's help. +set -euo pipefail + +REPO="$(git -C "$(dirname "${BASH_SOURCE[0]}")" rev-parse --show-toplevel)" +CLI_JS="$REPO/cli/dist/cli.js" + +# 1. Install workspace deps once. (If the build below fails on a checkout that +# already has node_modules, the deps are likely stale — run `pnpm install` +# in the repo by hand and re-run.) +if [ ! -d "$REPO/node_modules" ]; then + (cd "$REPO" && pnpm install) +fi + +# 2. Build the CLI (fast; just tsc + a copy step). +(cd "$REPO" && pnpm --filter orbit-cli build) >/dev/null + +# 3. Run the audit, forwarding all arguments (default to --help if none given, +# since `audit` requires --station and would otherwise error). +if [ "$#" -eq 0 ]; then set -- --help; fi +exec node "$CLI_JS" audit "$@"