Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions .claude/skills/orbit-station-audit/SKILL.md
Original file line number Diff line number Diff line change
@@ -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 <canister-id>",
"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
<https://internetcomputer.org/install>) — 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 <name> # 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 <STATION-CANISTER-ID> \
--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).
33 changes: 33 additions & 0 deletions .claude/skills/orbit-station-audit/scripts/run-audit.sh
Original file line number Diff line number Diff line change
@@ -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 <ID> [--identity <NAME>] [--identity-source icp] \
# [--network ic] [--output <PATH>] ...
#
# 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 "$@"
Loading