Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
029ad95
feat: Add Carbon Atlas — business-friendly multi-policy indexer frontend
gautamp8 Mar 11, 2026
b588f7b
fix: Address review feedback — trust chain redesign, project dedup, c…
gautamp8 Mar 16, 2026
e5651e0
fix: Rename to Carbon Atlas, fix Hedera Policy URL, clarify projected…
gautamp8 Mar 16, 2026
6adab31
feat: Add mainnet support with multi-network architecture and trust c…
gautamp8 Mar 23, 2026
c514c58
fix: Stop retry loops on 500s, improve mainnet VC rendering, don't ca…
gautamp8 Mar 23, 2026
4a63cc8
feat: Show document dates in trust chain, fix project detail page, ex…
gautamp8 Mar 23, 2026
cafd54b
Rename key assumptions, fix project location, add devices map navigat…
gautamp8 Mar 23, 2026
7fc052f
feat: Add Market Explorer with analytics dashboard, world map, and re…
gautamp8 Mar 25, 2026
e6ebf6e
feat: Dynamic header for Market Explorer, data disclaimer, deploy prep
gautamp8 Mar 26, 2026
2a0ef12
fix: Open CORS for public read-only API
gautamp8 Mar 26, 2026
1b27c7c
fix: Add .npmrc with legacy-peer-deps for Vercel builds
gautamp8 Mar 26, 2026
5579c8c
fix: Add .vercelignore to exclude Python backend from Vercel build
gautamp8 Mar 26, 2026
7baa34f
fix: Don't exclude api/ in vercelignore — it conflicts with lib/api a…
gautamp8 Mar 26, 2026
e03462b
docs: Update README with Market Explorer, refresh screenshots
gautamp8 Mar 26, 2026
406ce8c
docs: Replace Mermaid diagrams with plain text for GitHub compatibility
gautamp8 Mar 26, 2026
6f04d2e
feat: Add Project Developers feature and CORSIA eligibility
gautamp8 Mar 27, 2026
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
16 changes: 16 additions & 0 deletions carbon-atlas/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
NEXT_TELEMETRY_DISABLED=1

# Indexer API base URL (network suffix is added dynamically)
INDEXER_API_URL=https://indexer.guardianservice.app/api/v1

# ── Auth (choose one) ─────────────────────────────────────────────
#
# Option A: Auto-auth (recommended) — logs in automatically, refreshes tokens
GUARDIAN_API_URL=https://guardianservice.app/api/v1
GUARDIAN_EMAIL=your_email@example.com
GUARDIAN_PASSWORD=your_password
# Required when multiple users share the same email (see startup error for IDs):
# GUARDIAN_USER_ID=6667c472175828bcc1d49ba4
#
# Option B: Static token — must be manually refreshed when it expires (~14 days)
# INDEXER_API_TOKEN=your_bearer_token_here
53 changes: 53 additions & 0 deletions carbon-atlas/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files
.env*
!.env.example

# internal claude context (not for public repo)
.claude/

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

# pycache
__pycache__/

# remotion (demo videos — not committed upstream)
/remotion/
/out/videos/
/public/remotion-assets/
1 change: 1 addition & 0 deletions carbon-atlas/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
legacy-peer-deps=true
8 changes: 8 additions & 0 deletions carbon-atlas/.vercelignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
pipeline/
alembic/
alembic.ini
tests/
data/
pyproject.toml
uv.lock
docker-compose.yml
103 changes: 103 additions & 0 deletions carbon-atlas/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# CLAUDE.md — Carbon Atlas

## Project Overview

Public-facing Next.js dashboard providing a traceable record of verified emission reductions from the [Gold Standard MECD 431](https://globalgoals.goldstandard.org/431_ee_ics_methodology-for-metered-measured-energy-cooking-devices/) methodology — Metered & Measured Energy Cooking Devices. The methodology is digitized on [Hedera Guardian](https://github.com/hashgraph/guardian), an open-source MRV platform using Hedera Hashgraph DLT.

Supports both **mainnet** (production) and **testnet** networks with live switching.

**Stack:** Next.js 16 (App Router) | React 19 | TanStack Query | shadcn/ui | Tailwind CSS 4 | Vitest

## Architecture

```
NetworkProvider (client context, persisted in localStorage, default: mainnet)
→ fetchProxy passes _network param to server proxy
→ proxy builds URL: ${INDEXER_API_URL}/${network}/${path}
→ Bearer token injected server-side (same token for both networks)
→ TanStack Query keys include network (separate caches per network)
→ Policy ID from config map (lib/config/networks.ts)
```

- **Multi-network:** `lib/config/networks.ts` defines per-network settings (policy IDs, token IDs, topic IDs). `providers/NetworkProvider.tsx` provides React context. Network selector in header switches data source live.
- **Auth proxy:** `app/api/proxy/[...path]/route.ts` injects a Bearer JWT server-side so the token never reaches the client bundle. Token lifecycle is managed by `lib/api/auth.ts` which handles the MGS SSO chain (login → access-token → sso/generate) and auto-refreshes before expiry.
- **Caching:** TanStack Query with 15 min staleTime, 1 hr gcTime. All policy VCs are fetched once and filtered client-side. Cache is keyed per-network.
- **Theming:** `next-themes` with system default, dark/light toggle in header.

## Key Files

| File | Purpose |
|---|---|
| `lib/config/networks.ts` | Network config map — mainnet/testnet policy IDs, tokens, topics |
| `providers/NetworkProvider.tsx` | React context for active network, persisted in localStorage |
| `app/api/proxy/[...path]/route.ts` | Auth proxy — injects Bearer token, routes by `_network` param |
| `lib/api/auth.ts` | Server-side token manager — auto login/refresh via MGS SSO chain |
| `lib/api/vc-documents.ts` | API client — getVcDocuments, getAllPolicyVcs, parseCredentialSubject |
| `lib/utils/trust-chain.ts` | buildChain(), ENTITY_TYPE_CONFIG, getProjectDevelopers |
| `hooks/usePolicyVcDocuments.ts` | TanStack Query hooks with client-side filtering/pagination |
| `hooks/useDashboardStats.ts` | Aggregates real tCO₂e and device counts from VC details |
| `components/vc-views/VCRenderer.tsx` | Routes entity types to specific renderers |
| `components/trust-chain/TrustChainView.tsx` | Trust chain visualization with Credits Issued step |
| `components/section-cards.tsx` | Dashboard stat cards with live data |

## Entity Types

| Entity Type | Description |
|---|---|
| `approved_report` | Verified monitoring report (emission reduction issuance) |
| `report` | Calculated monitoring report |
| `verification_report` | VVB verification report |
| `validation_report` | VVB validation report |
| `daily_mrv_report` | Aggregated device MRV data |
| `approved_project` | Validated project |
| `project` | Calculated project (auto-completed fields) |
| `project_form` | Raw Project Design Document submission |
| `approved_vvb` | Approved Validation & Verification Body |
| `vvb` | VVB registration |

## Trust Chain: Credits Issued Step

The trust chain shows a "Credits Issued" step at the top:
- **Completed** (solid green) when root VC is `approved_report` — shows ER_y amount and links to token on Hashscan
- **Pending** (dashed ghost) when root is earlier in the lifecycle

## Development

```bash
npm install
cp .env.example .env.local # Add Guardian auth credentials
npm run dev # http://localhost:3000
npm test # Vitest
npm run build # Type-check + production build
```

### Environment Variables

See `.env.example` for the full list. Network-specific config (policy IDs, token IDs) lives in `lib/config/networks.ts`, NOT in env vars. Env vars only hold:
- `INDEXER_API_URL` — base URL without network suffix (network appended dynamically)
- Auth credentials (auto-auth or static token)

Auth supports two modes:
- **Auto-auth (recommended):** Set `GUARDIAN_API_URL`, `GUARDIAN_EMAIL`, `GUARDIAN_PASSWORD` (and optionally `GUARDIAN_USER_ID`). Tokens are managed automatically.
- **Static token:** Set `INDEXER_API_TOKEN` to skip auto-auth. Must be manually rotated when it expires (~14 days).

## Testing

Tests are in `__tests__/` and use Vitest with `environment: "node"` (not jsdom — the tests are pure logic, no DOM needed).

```bash
npm test # Run all tests
npm run test:watch # Watch mode
```

## Branding

- **CarbonMarketsHQ:** `public/cmhq-logo-dark.png`, `public/cmhq-logo-light.png` — sidebar footer
- **ATEC Global:** `public/atec-dark.png`, `public/atec-light.png` — project developer badge
- All logos have dark/light variants for theme support

## Adding a New Network Policy

To add a new policy (or update to a newer version), edit `lib/config/networks.ts`:
1. Add the policy to the appropriate network's `policies` array (latest first)
2. The app defaults to `policies[0]` — the first entry is the active policy
131 changes: 131 additions & 0 deletions carbon-atlas/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Contributing to MECD Indexer

Thanks for your interest in contributing! This project is a public dashboard for exploring carbon credit issuances from the Gold Standard MECD 431 methodology on Hedera Guardian.

## Getting Started

### Prerequisites

- **Node.js** 20+
- **npm** 10+
- A **Guardian Indexer API token** (Bearer JWT) — reach out to the maintainers or generate one from the [Guardian Indexer](https://indexer.guardianservice.app)

### Setup

```bash
git clone https://github.com/gautamp8/mecd-indexer.git
cd mecd-indexer
npm install
cp .env.example .env.local
```

Edit `.env.local` and add your API token:

```
INDEXER_API_TOKEN=your_bearer_jwt_here
INDEXER_API_URL=https://indexer.guardianservice.app/api/v1/testnet
NEXT_PUBLIC_POLICY_HEDERA_ID=1767599197.624837133
NEXT_PUBLIC_HEDERA_NETWORK=testnet
```

Start the dev server:

```bash
npm run dev
```

Open [http://localhost:3000](http://localhost:3000).

## Architecture Overview

```
Guardian Indexer API
-> /api/proxy/[...path] (server-side auth proxy, injects Bearer token)
-> TanStack Query (client-side caching, 15 min stale / 1 hr gc)
-> React components
```

- **Auth proxy** (`app/api/proxy/[...path]/route.ts`): All API calls go through this server-side proxy so the JWT token never reaches the client bundle.
- **Data fetching** (`lib/api/vc-documents.ts`): Fetches all policy VCs in one call, filters client-side by entity type (the indexer API ignores `entityType` filter params).
- **VC renderers** (`components/vc-views/`): Each entity type has a dedicated renderer. `VCRenderer.tsx` dispatches based on `entityType`.
- **Trust chain** (`lib/utils/trust-chain.ts`): Traverses VC relationships depth-first from a root document.

## Project Structure

```
app/ # Next.js App Router pages
api/proxy/ # Auth proxy to Guardian Indexer API
dashboard/ # Overview with stats and recent issuances
issuances/ # Issuance list + trust chain detail
projects/ # Project list + detail
components/
vc-views/ # Entity-type-specific VC renderers
trust-chain/ # Trust chain visualization
shared/ # Reusable components (DeviceDataTable, HederaProofBadge, etc.)
ui/ # shadcn/ui components
hooks/ # TanStack Query hooks
lib/
api/ # API client (fetchProxy, vc-documents)
types/ # TypeScript interfaces
utils/ # Formatting, Hedera URLs, trust chain logic
__tests__/ # Vitest tests
```

## Development Guidelines

### Code Style

- **TypeScript** — all code is typed. Run `npm run build` to type-check.
- **Tailwind CSS 4** — utility-first styling. Use `cn()` from `lib/utils.ts` for conditional classes.
- **shadcn/ui** — all UI components come from shadcn. Add new ones with `npx shadcn@latest add <component>`.
- **No CSS modules or styled-components** — Tailwind only.

### Adding a New VC Renderer

1. Create a new component in `components/vc-views/` (e.g., `NewEntityView.tsx`)
2. The component receives `cs` (parsed credential subject) and `rawDocuments` as props
3. Use the `get(obj, "dotted.path")` helper pattern for nested field access (see existing renderers)
4. Add a case to the switch in `components/vc-views/VCRenderer.tsx`

### Testing

Tests use Vitest with `environment: "node"` (pure logic, no DOM).

```bash
npm test # Run all tests
npm run test:watch # Watch mode
```

Add tests in the `__tests__/` directory. Focus on:
- Data transformation logic (trust chain building, VC parsing)
- API client behavior (pagination, filtering)

### Key API Quirks

These are documented in detail in `CLAUDE.md` but the critical ones:

1. **`options.entityType` filter is ignored by the API** — all filtering must be done client-side
2. **Individual VC lookup uses `consensusTimestamp`** as the path param, not the MongoDB `id`
3. **`documents[]` in list responses** contain MongoDB ref IDs (useless). Only the detail endpoint returns full VC JSON.

## Making Changes

1. Fork the repository
2. Create a feature branch: `git checkout -b feature/your-feature`
3. Make your changes
4. Run tests: `npm test`
5. Run type check: `npm run build`
6. Commit with a clear message describing what and why
7. Push and open a pull request

## Reporting Issues

Open an issue on [GitHub](https://github.com/gautamp8/mecd-indexer/issues) with:
- What you expected to happen
- What actually happened
- Steps to reproduce
- Browser/OS if relevant

## License

MIT — see [LICENSE](LICENSE) for details.
Loading
Loading