From 845573da5ac0c548693b07e728ab6f4ada6618f4 Mon Sep 17 00:00:00 2001 From: planadecu Date: Fri, 9 Oct 2026 16:53:04 +0200 Subject: [PATCH] chore: align repo files with Glassnode standards (GN-180) - Replace CLAUDE.md with AGENTS.md: a pointer to CONTRIBUTING.md plus the agent traps it doesn't cover. Project layout, conventions and CI/publish details that were only in CLAUDE.md move to CONTRIBUTING.md. - Add SECURITY.md: GitHub private vulnerability reporting, with security@glassnode.com as fallback. - .gitignore: ignore .env.*, *.local, *.pem and *.key (keeps .env.example). - Drop an internal ticket reference from the publish.yml comment and point the ci.yml comment at CONTRIBUTING.md. --- .github/workflows/ci.yml | 2 +- .github/workflows/publish.yml | 5 +- .gitignore | 9 +++- AGENTS.md | 18 +++++++ CLAUDE.md | 92 ----------------------------------- CONTRIBUTING.md | 65 ++++++++++++++++++++----- SECURITY.md | 21 ++++++++ 7 files changed, 104 insertions(+), 108 deletions(-) create mode 100644 AGENTS.md delete mode 100644 CLAUDE.md create mode 100644 SECURITY.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a869afa..bc390b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: # "test (24)") are the required checks in the repository rulesets. node: [22, 24] steps: - # Every action is pinned by full commit SHA (see CLAUDE.md "CI"). + # Every action is pinned by full commit SHA (see CONTRIBUTING.md "CI and workflows"). - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # Version enforcement compares with the exact PR base commit. diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ce10e53..e67995e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -56,9 +56,8 @@ jobs: publish: needs: [verify, release] - # Only from the official repository: never from a fork, nor from the old - # planadecu/glassnode-terminal before the transfer (GN-177), where the `npm` environment and - # its required reviewer don't exist. + # Only from the official repository: never from a fork, where the `npm` environment and its + # required reviewer don't exist. if: needs.release.outputs.action != 'skip' && github.repository == 'glassnode/glassnode-terminal' runs-on: ubuntu-latest # Required reviewer (the maintainer) and a `main`-only deployment branch rule are configured diff --git a/.gitignore b/.gitignore index aaab728..69ceeb8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,12 @@ node_modules/ dist/ *.tsbuildinfo .DS_Store -.env .claude/worktrees/ + +# Local env and credentials +.env +.env.* +!.env.example +*.local +*.pem +*.key diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..8bfdaed --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,18 @@ +# glassnode-terminal: agent instructions + +Read [CONTRIBUTING.md](CONTRIBUTING.md) first: setup, project layout, conventions, the version +bump and the release flow all live there. Before opening a PR, run the checks in its "Run the +local checks" step. + +## Don't + +- Edit `src/lib/x402-catalog.ts` by hand: it is generated by `pnpm run gen:x402-metrics` + (`scripts/generate-x402-metrics.mjs`). +- Run `pnpm run gen:x402-metrics` or the app against the live API unless asked: it needs a real + `GLASSNODE_API_KEY`, and in x402 mode (`X402_PRIVATE_KEY`) every call, metadata included, + spends USDC from a real wallet. Tests and the CLI smoke test need no credentials. +- Change `scripts/release-*.{sh,mjs}` here alone: they are shared with + [glassnode-api-ts-client](https://github.com/glassnode/glassnode-api-ts-client) and tested in + `test/release-scripts.spec.ts`. Say in the PR that the other repo needs the same change. +- Move a GitHub Action to a tag such as `@v7`: every action is pinned by full commit SHA (see + CONTRIBUTING.md "CI and workflows"). diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 6fd7cc7..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,92 +0,0 @@ -# Claude Instructions - -## Project Overview - -Interactive TUI (Terminal User Interface) for Glassnode on-chain crypto data, built with Ink (React for CLI) and the `glassnode-api` package. - -## Project Structure - -- `/src` - Source code (TypeScript + React/Ink) - - `/src/components` - Ink UI components (AssetList, MetricList, DataView, ParamBar, StatusBar, SearchOverlay, Spinner, HighlightedText, LogView, PriceTicker) - - `/src/hooks` - React hooks - - `useAssets`, `useMetrics`, `useMetricMetadata` - Startup/catalog data (cached) - - `useMetricData` - Live data for the selected metric + asset - - `usePulses` - Live prices over the Pulses WebSocket (price ticker) - - `useListNavigation` - Scrollable list navigation - - `/src/lib` - Pure library code (no React dependency, except `logger.ts`'s `useLogs` hook) - - `startup-data.ts` - Fetches all assets, metrics, and metric metadata at startup; caches to `~/.glassnode-terminal/cache/` for 1 day (separate cache per mode) - - `cache.ts` - File-based cache with 1-day TTL - - `api-client.ts` - Singleton GlassnodeAPI client; API-key mode (`GLASSNODE_API_KEY`) or x402 pay-per-call mode (`X402_PRIVATE_KEY`) - - `x402-catalog.ts` - AUTO-GENERATED bundled metric catalog for x402 mode; regenerate with `pnpm run gen:x402-metrics`, never edit by hand - - `logger.ts` - In-memory log of API calls (API key redacted), shown in LogView - - `time-parse.ts` - Parse relative times (30d, 1h) and ISO dates to unix timestamps - - `format.ts` - Date, number, metric name formatting utilities - - `types.ts` - Shared types (Pane, BrowseMode, MetricParams, DataPoint, constants) - - `App.tsx` - Root component: three-pane layout, browse mode toggle, keyboard handling - - `cli.tsx` - Entry point: resolves mode (`--api` / `--x402` / `GLASSNODE_MODE`), validates credentials, renders App -- `/scripts` - `generate-x402-metrics.mjs` (builds `src/lib/x402-catalog.ts`) -- `/test` - Test files (Vitest, `*.spec.ts(x)`) -- `/dist` - Compiled output (not checked into git) - -## Development Workflow - -- Use Node.js v24 for development (published package supports Node >= 22) -- Use pnpm 11 as package manager; supply-chain settings (build allowlist, 7-day release-age cooldown) live in `pnpm-workspace.yaml` -- Run tests with `pnpm test` -- Build the project with `pnpm run build` -- Run the app with `GLASSNODE_API_KEY=xxx pnpm start` (or `node --env-file=.env dist/cli.js [--api|--x402]`) -- `glassnode-api` comes from npm; it is maintained by the repo author and exempt from the release-age cooldown -- Repository: https://github.com/glassnode/glassnode-terminal (moved from planadecu/glassnode-terminal; GN-177). See `CONTRIBUTING.md` for the PR flow - -## Architecture - -- All startup data (assets sorted by market cap, metric list, metric metadata with display names) is fetched once and cached for 1 day -- Metric display names come from `MetricMetadata.descriptors.name` (from the Glassnode API), with a path-based fallback -- After startup, live calls are `useMetricData` (selected metric + asset) and the `usePulses` WebSocket (price ticker) -- Two browse modes: Asset→Metric and Metric→Asset, toggled with `m` key -- Parameter cycling (interval, since, currency) from any pane via `i`, `s`, `c` keys -- Other keys: `/` search, `v` table/chart, `p` price ticker, `l` log pane, `q` quit - -## Coding Standards - -- TypeScript for all source files -- React JSX for Ink components (.tsx) -- Pure library code in /src/lib has no React imports -- Use the `glassnode-api` package types (AssetMetadata, MetricMetadata) — don't redefine them - -## Parameter Mapping (API query params) - -| Param | API key | Values | -|----------|---------|---------------------------| -| asset | `a` | BTC, ETH, SOL, etc. | -| interval | `i` | 10m, 1h, 24h, 1w, 1month | -| since | `s` | unix timestamp | -| currency | `c` | usd, native | - -## Versioning - -Follow [semver](https://semver.org/) (major: breaking for users — removed flags/env vars, changed defaults, higher Node floor; minor: new features; patch: fixes, dependency bumps, refactors). - -**Every change to the published package** (except PRs into `release/**` branches, which still add a CHANGELOG entry) MUST: - -1. Bump `version` in `package.json` -2. Add a `## ` entry at the top of `CHANGELOG.md` - -Changes that only touch files outside the package (`NO_RELEASE_PATHS` in `scripts/check-changelog-heading.mjs`: docs, `CODEOWNERS`, `.github/`, `scripts/`, `test/`) need neither. - -The `version` in `package.json` is exactly what gets published: CI never bumps it. A change merged without a bump publishes nothing. - -## CI - -- `.github/workflows/ci.yml` runs on PRs into `main` and `release/**`, and `publish.yml` calls it (`workflow_call`) as its `verify` job: the CHANGELOG heading check (`scripts/check-changelog-heading.mjs`: the top heading must be exactly `## `; into `release/**`, `## (unreleased)` is also accepted), install, build, tests and a CLI smoke test, on Node 22 and 24 (`test (22)`, `test (24)` are the required checks). PRs into `main` additionally require a version greater than the base commit (`BASE_SHA`) and nonempty release notes. -- Every action is pinned by full commit SHA with a `# vX.Y.Z` comment, never a movable tag. To bump one, resolve the tag with `gh api repos///git/ref/tags/` (dereference an annotated tag with `gh api repos///git/tags/`), update SHA and comment together, and read the release notes. Dependabot proposes these updates. - -## Publishing - -Same flow as `glassnode-api` (github.com/glassnode/glassnode-api-ts-client): - -- `.github/workflows/publish.yml` releases the `version` already in `package.json`. It never bumps the version, commits or pushes to `main`, which is protected by a ruleset (changes land only through PRs with the required checks). -- On every push to `main`: `verify` runs `ci.yml`; `release` (read-only) runs `scripts/release-state.sh`, which checks npm (only an E404 means "not published"; any other failure fails the job) and plans the dist-tag with `scripts/release-plan.mjs` (`latest`, `backport-.` for a version below `latest`, `next` for a prerelease); `publish` runs in the `npm` **environment** (required reviewer, `main` only), re-checks, runs `npm publish --tag ` (via `prepublishOnly`: clean + build), pushes the `v` tag and creates the GitHub Release from the version's `CHANGELOG.md` section. A version already on npm is skipped without asking for approval. -- **npm Trusted Publishing (OIDC)** with provenance — no `NPM_TOKEN`. The Trusted Publisher for `glassnode-terminal` on npmjs.com must name repo `glassnode/glassnode-terminal`, workflow `publish.yml` and environment `npm`. -- Merge release PRs one at a time: GitHub keeps only one pending run per concurrency group, so a newer merge can replace an older publish still waiting for approval (CONTRIBUTING.md "Releases" explains detection and recovery). -- `scripts/release-*.{sh,mjs}` are shared with glassnode-api and tested in `test/release-scripts.spec.ts`; keep them in sync. `check-changelog-heading.mjs` additionally enforces PR version bumps here. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ce1bf12..3e7a512 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,8 @@ the release** (`.github/workflows/publish.yml`). `main` only accepts changes thr Node.js >= 22, and CI runs on both. - **pnpm** (the version is pinned in `package.json` `packageManager`; `corepack enable` picks it up). Supply-chain settings (build-script allowlist, 7-day release-age cooldown) live in - `pnpm-workspace.yaml`. + `pnpm-workspace.yaml`. `glassnode-api` and `ink-uplot` are published by this project's author + and are exempt from the cooldown, so their new releases can be picked up right away. - System libraries for [node-canvas](https://github.com/Automattic/node-canvas#compiling) (charts). On macOS: `brew install pkg-config cairo pango`. - A Glassnode API key (`GLASSNODE_API_KEY`) to run the app against the live API. @@ -32,14 +33,38 @@ pnpm install ### 2. Make the change -Project layout (more detail in `CLAUDE.md`): - -- `src/cli.tsx` - entry point: picks API-key or x402 mode, validates credentials, renders the app -- `src/App.tsx` - the three-pane layout and keyboard handling -- `src/components/` - Ink UI components; `src/hooks/` - React hooks -- `src/lib/` - pure library code (API client setup, startup data and cache, formatting) +Project layout: + +- `src/cli.tsx` - entry point: picks the mode (`--api` / `--x402` / `GLASSNODE_MODE`), validates + credentials, renders the app +- `src/App.tsx` - the three-pane layout, browse mode toggle and keyboard handling +- `src/components/` - Ink UI components (asset, metric and data panes, parameter bar, search + overlay, price ticker, log view) +- `src/hooks/` - React hooks: `useAssets`, `useMetrics`, `useMetricMetadata` (startup catalog + data, cached), `useMetricData` (live data for the selected metric and asset), `usePulses` (live + prices over the Pulses WebSocket), `useListNavigation` (scrollable lists) +- `src/lib/` - pure library code: + - `api-client.ts` - the shared `GlassnodeAPI` client, in API-key or x402 pay-per-call mode + - `startup-data.ts`, `cache.ts` - fetch all assets, metrics and metric metadata at startup and + cache them in `~/.glassnode-terminal/cache/` for 1 day (a separate cache per mode) + - `x402-catalog.ts` - the metric catalog bundled for x402 mode; generated by + `pnpm run gen:x402-metrics`, never edited by hand + - `logger.ts` - in-memory log of API calls (API key redacted) shown in the log view + - `time-parse.ts`, `format.ts`, `types.ts` - time parsing, formatting, shared types - `scripts/` - the x402 catalog generator and the release tooling -- `test/` - Vitest tests +- `test/` - Vitest tests (`*.spec.ts(x)`) + +Conventions: + +- TypeScript everywhere; Ink components are `.tsx`. +- `src/lib/` has no React imports, except the `useLogs` hook in `logger.ts`. +- Use the `glassnode-api` types (`AssetMetadata`, `MetricMetadata`, ...) instead of redefining + them. Metric display names come from `MetricMetadata.descriptors.name`, with a path-based + fallback. +- After startup, the only live calls are `useMetricData` and the `usePulses` WebSocket; + everything else comes from the cached startup data. +- The parameter bar maps to these API query parameters: asset `a` (BTC, ETH, ...), interval `i` + (10m, 1h, 24h, 1w, 1month), since `s` (unix timestamp), currency `c` (usd, native). Add or update tests for what you change. Run the app (`GLASSNODE_API_KEY=… pnpm start`) to check UI changes by eye; charts draw differently in kitty, iTerm2/VS Code and plain terminals. @@ -117,11 +142,29 @@ version is already npm's `latest`, the older one is published under the `backport-.` dist-tag instead, so `latest` never moves backwards, and its GitHub Release is not marked "Latest". A prerelease version is published under `next`. +## CI and workflows (for maintainers) + +- `.github/workflows/ci.yml` runs on PRs into `main` and `release/**`; `publish.yml` calls it + (`workflow_call`) as its `verify` job, so a release runs exactly the PR checks. The job runs on + Node 22 and 24, and `test (22)` and `test (24)` are the required checks in the repository + rulesets: renaming the job or the matrix means updating the rulesets. +- Every action is pinned by full commit SHA with a `# vX.Y.Z` comment, never a movable tag. + Dependabot proposes updates. To bump one by hand, resolve the tag with + `gh api repos///git/ref/tags/` (dereference an annotated tag with + `gh api repos///git/tags/`), update the SHA and the comment together, and + read the release notes. +- `publish.yml` publishes with npm Trusted Publishing (OIDC) and provenance; there is no + `NPM_TOKEN`. The Trusted Publisher for `glassnode-terminal` on npmjs.com must name the + repository `glassnode/glassnode-terminal`, the workflow `publish.yml` and the environment `npm`. +- `scripts/release-*.{sh,mjs}` are shared with + [glassnode-api-ts-client](https://github.com/glassnode/glassnode-api-ts-client) and tested in + `test/release-scripts.spec.ts`; keep the two repositories in sync. + `scripts/check-changelog-heading.mjs` additionally enforces the PR version bump here. + ## Security -- Report vulnerabilities privately, through GitHub's private vulnerability reporting on this - repository (the **Security** tab → **Report a vulnerability**). Never open a public issue for - them. +- Report vulnerabilities privately, as described in [SECURITY.md](SECURITY.md). Never open a + public issue for them. - Never commit API keys, wallet private keys (`X402_PRIVATE_KEY`) or other secrets, including in `.env` files, tests or logs. `.env` is git-ignored. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..4e096e4 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,21 @@ +# Security policy + +## Contents + +- [Reporting a vulnerability](#reporting-a-vulnerability) +- [Supported versions](#supported-versions) + +## Reporting a vulnerability + +Don't open a public issue. Report it privately: + +- **GitHub:** this repository's Security tab → "Report a vulnerability" (private vulnerability + reporting). +- **Email:** security@glassnode.com, if you can't use GitHub. + +Include the affected version, steps to reproduce and the impact. Glassnode has no bug bounty +program. + +## Supported versions + +Only the latest release published to npm receives security fixes.