pnpm + TypeScript workspace per design doc §13:
- apps/{web,api,worker} skeletons (Next.js 16, Fastify 5, BullMQ)
- packages/{core,solana,prometheus,db,config} (core has real types/DTOs;
solana/prometheus are stubs)
- programs/pyre-core placeholder (future Anchor, v1.0)
- docs/: PYRE_MVP_DESIGN (canonical), ARCHITECTURE, SECURITY, TOKEN_CLASSIFICATION
- CLAUDE.md, README, .env.example (no private-key var by design)
Skeleton + docs only — no Solana/business logic yet. All workspaces typecheck clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
490 lines
20 KiB
Markdown
490 lines
20 KiB
Markdown
# PYRE — Engineering Architecture
|
||
|
||
> Companion to [`PYRE_MVP_DESIGN.md`](./PYRE_MVP_DESIGN.md). The design document is
|
||
> the canonical source of truth; this file describes *how the system is wired
|
||
> together*. Where they appear to disagree, the design document wins.
|
||
|
||
**Tagline:** *Burn the dead. Feed the PYRE. Claim the Spawn.*
|
||
|
||
---
|
||
|
||
## 1. System Overview
|
||
|
||
PYRE is a Solana wallet-cleanup and ritual meme-rebirth protocol. The MVP runs as
|
||
three runtime services backed by two stateful stores, and it depends on two
|
||
classes of external service: a Solana RPC provider and AI APIs.
|
||
|
||
Runtime services:
|
||
|
||
- **`apps/web`** (`pyre-web`) — Next.js frontend. Wallet connect, scanner UI,
|
||
cleanup preview, signing, receipt rendering, Prometheus review, admin pages.
|
||
- **`apps/api`** (`pyre-api`) — Node.js/Fastify (or Express) HTTP API. Scan
|
||
coordination, classification, **unsigned** transaction building, receipt
|
||
storage, AI orchestration, admin API. The only service the browser talks to.
|
||
- **`apps/worker`** (`pyre-worker`) — Node.js BullMQ worker. Slow/async jobs:
|
||
metadata enrichment, AI generation, image prompts, safety/collision checks,
|
||
confirmation tracking, receipt enrichment.
|
||
|
||
Stateful stores:
|
||
|
||
- **PostgreSQL** — durable record of scans, classifications, receipts,
|
||
generations, and Spawn records.
|
||
- **Redis** — BullMQ job queue, scan cache, rate limiting, quote cache,
|
||
generation status.
|
||
|
||
External dependencies (never self-hosted on the MVP VPS):
|
||
|
||
- **Solana RPC** — external provider only. No validator/RPC node on the VPS.
|
||
- **AI APIs** — text, image, and moderation APIs only. No local LLM/image models.
|
||
|
||
### Component / Data-Flow Diagram
|
||
|
||
```
|
||
┌──────────────┐
|
||
│ Wallet │ (Phantom / Solflare via Wallet Adapter)
|
||
│ (browser) │ signs locally — keys never leave the client
|
||
└──────┬───────┘
|
||
│ connect / sign / send
|
||
▼
|
||
┌──────────────┐ static assets / SSR
|
||
│ apps/web │◀───────────────────────────┐
|
||
│ (Next.js) │ │
|
||
└──────┬───────┘ │
|
||
│ HTTPS (JSON) │
|
||
▼ │
|
||
┌───────────────────────────────────────────────┐ │
|
||
│ apps/api │ │
|
||
│ scan · classify · build (UNSIGNED) · receipt │ │
|
||
│ prometheus/generate · admin │ │
|
||
└───┬─────────────┬──────────────┬────────────────┘ │
|
||
│ │ │ │
|
||
│ SQL │ cache/queue │ getAccounts / simulate / │
|
||
▼ ▼ │ sendTx / confirm │
|
||
┌──────────┐ ┌──────────┐ ▼ │
|
||
│ Postgres │ │ Redis │ ┌──────────────┐ │
|
||
│ │ │ (BullMQ) │ │ Solana RPC │ (external) │
|
||
└──────────┘ └────┬─────┘ └──────────────┘ │
|
||
│ │
|
||
│ jobs pulled from queue │
|
||
▼ │
|
||
┌──────────────┐ ┌──────────────┐ │
|
||
│ apps/worker │───▶│ AI APIs │ (external) │
|
||
│ (BullMQ) │ │ text/image/ │ │
|
||
│ │ │ moderation │ │
|
||
└──────┬───────┘ └──────────────┘ │
|
||
│ writes results / status │
|
||
├───────────────▶ Postgres │
|
||
└───────────────▶ Redis ────────────────────┘
|
||
(generation status read back by web/api)
|
||
```
|
||
|
||
The browser only ever talks to `apps/web` and `apps/api`. The worker is not
|
||
network-exposed; it consumes jobs from Redis and writes results back to Postgres
|
||
and Redis. RPC and AI calls flow outbound only.
|
||
|
||
---
|
||
|
||
## 2. Component Responsibilities
|
||
|
||
### `apps/web` — User-facing frontend
|
||
|
||
Next.js + TypeScript + Tailwind + Solana Wallet Adapter, with React Query or
|
||
Zustand for state. Responsibilities (§11):
|
||
|
||
- Wallet connect via Solana Wallet Adapter.
|
||
- Token-account display and classification grouping (closeable / burnable /
|
||
transmutable / protected / unsupported).
|
||
- User confirmations and **transaction preview** UI.
|
||
- Transaction **signing** (client-side, via the connected wallet only).
|
||
- Receipt rendering and optional share image.
|
||
- Prometheus Spawn-package review and public round/Spawn pages.
|
||
- Admin / generation review UI.
|
||
|
||
### `apps/api` — Backend HTTP API
|
||
|
||
Node.js + Fastify/Express + TypeScript, fronting PostgreSQL, Redis, BullMQ.
|
||
Responsibilities (§11):
|
||
|
||
- Token-scan coordination (calls RPC, persists results).
|
||
- Classification helpers and route evaluation.
|
||
- **Builds UNSIGNED Solana transactions** — never signs, never holds keys.
|
||
- Metadata preparation and AI generation orchestration (enqueues worker jobs).
|
||
- Receipt storage and Spawn-record storage.
|
||
- Public API and protected admin API.
|
||
|
||
### `apps/worker` — Background worker
|
||
|
||
Node.js worker process driven by BullMQ over Redis, with AI API clients.
|
||
Responsibilities (§11):
|
||
|
||
- Slow token-metadata enrichment.
|
||
- AI generation jobs (Prometheus).
|
||
- Image-prompt generation.
|
||
- Safety checks and ticker/name collision checks.
|
||
- Background confirmation tracking.
|
||
- Receipt enrichment.
|
||
|
||
### `packages/core` — Shared types & business logic
|
||
|
||
The single home for cross-service domain definitions (§13):
|
||
|
||
- Classification **enums**: `EMPTY_CLOSE_ONLY`, `INCINERATE_ONLY`,
|
||
`TRANSMUTABLE`, `PROTECTED_SKIP`, `UNSUPPORTED`.
|
||
- Conservative **risk rules** (the §7 safety rules: classic SPL only, skip
|
||
Token-2022/NFTs/LP/frozen/delegated, USD threshold, price-impact and stale-quote
|
||
guards).
|
||
- Shared **DTOs** for the API request/response shapes.
|
||
- **Receipt schema**.
|
||
- **Prometheus input/output schema**.
|
||
|
||
### `packages/solana` — Solana transaction helpers
|
||
|
||
Everything that touches the chain at the instruction level (§13):
|
||
|
||
- Token-account **parsing** (decode raw account state into the scan model).
|
||
- **Close-account** transaction builder.
|
||
- **Burn** transaction builder.
|
||
- **Simulation** helpers (simulate before signing — §7).
|
||
- **Transaction decoder** (decode the built tx so it can be matched against the
|
||
preview — §8 step 5, §16).
|
||
|
||
### `packages/prometheus` — AI generation logic
|
||
|
||
The creative engine's code (§13), invoked by the worker:
|
||
|
||
- Prompt **templates**.
|
||
- The **meta mixer** (probabilistic influence blend — §9).
|
||
- Output **parser**.
|
||
- **Safety checks** (blocked terms, scam/impersonation/copyright filters).
|
||
- **Image-prompt generator**.
|
||
|
||
### `packages/db` — Database layer
|
||
|
||
Schema, migrations, and table definitions (§13/§15). Owns the canonical SQL and
|
||
migration history; `apps/api` and `apps/worker` depend on it for data access.
|
||
|
||
### `packages/config` — Shared config
|
||
|
||
Shared configuration and environment loading (§13). Centralizes reading
|
||
`.env`-supplied secrets (RPC URL, AI keys, DB/Redis connection strings) so no
|
||
secret is hard-coded.
|
||
|
||
### `programs/pyre-core` — Future Anchor program
|
||
|
||
NOT part of the first burner MVP. The future on-chain trust core (rounds, Essence
|
||
vault, claims, refunds) — built only at Phase 7 / v1.0.
|
||
|
||
---
|
||
|
||
## 3. Request / Data Flow — The Burner Journey
|
||
|
||
The core flow is the 8-step burner sequence from §8. The critical trust property:
|
||
**the server builds an UNSIGNED transaction, the client signs it, and the decoded
|
||
transaction is matched against the preview before signing.**
|
||
|
||
```
|
||
1. Connect ──▶ 2. Scan ──▶ 3. Classify ──▶ 4. Preview ──▶ 5. Build (UNSIGNED)
|
||
│
|
||
▼
|
||
8. Receipt ◀── 7. Confirm ◀── 6. Sign (client) ◀── decode & match against preview
|
||
```
|
||
|
||
1. **Wallet Connect** — user connects via Solana Wallet Adapter (`apps/web`).
|
||
No keys leave the browser.
|
||
2. **Account Scan** — `apps/web` calls `POST /api/scan`. `apps/api` queries token
|
||
accounts via the external RPC, capturing for each: owner, ATA address, mint,
|
||
token program, balance, decimals, rent estimate, metadata (if available),
|
||
token-program type, frozen/delegated state. Results persist to
|
||
`wallet_scans` / `token_accounts`; slow metadata enrichment may be deferred to
|
||
a worker job. Scan results are cached in Redis.
|
||
3. **Classification** — each account is classified server-side into the §6
|
||
categories and grouped for the UI. **Client-submitted classifications are not
|
||
trusted** — the server recomputes where needed (§16).
|
||
4. **Preview** — before any signing the user sees: accounts to close, tokens to
|
||
burn, accounts skipped, estimated rent returned, transaction fees, warnings,
|
||
and the destination wallet for recovered rent (which is the user's own wallet).
|
||
5. **Build Transaction** — `apps/web` calls `POST /api/build/close-empty` (and/or
|
||
`/api/build/burn`). `apps/api` uses `packages/solana` to build an **unsigned**
|
||
transaction and returns it base64-encoded plus the preview. The client decodes
|
||
the transaction (`packages/solana` decoder) and **matches it against the
|
||
preview**; any mismatch aborts before signing.
|
||
6. **User Signs** — locally, with the connected wallet. PYRE never requests
|
||
private keys and never performs custodial signing.
|
||
7. **Confirmation** — the signed transaction is sent and confirmed via RPC. A
|
||
worker confirmation-tracking job may follow the signature to durable finality.
|
||
8. **Receipt** — `apps/web` calls `POST /api/receipt`; `apps/api` records and
|
||
returns the receipt: tx signature, accounts closed, tokens burned, rent
|
||
returned, accounts skipped, warnings, timestamp, optional share image. A worker
|
||
receipt-enrichment job may backfill metadata.
|
||
|
||
All transactions are simulated before final signing (§7), and all build requests
|
||
are logged (§16).
|
||
|
||
---
|
||
|
||
## 4. API Surface
|
||
|
||
Reproduced from §14. All endpoints are served by `apps/api`. Admin endpoints are
|
||
authenticated and protected separately (§16).
|
||
|
||
### `POST /api/scan`
|
||
|
||
```jsonc
|
||
// in:
|
||
{ "wallet": "wallet_pubkey" }
|
||
|
||
// out:
|
||
{
|
||
"scanId": "uuid",
|
||
"wallet": "wallet_pubkey",
|
||
"summary": {
|
||
"totalAccounts": 0,
|
||
"emptyCloseOnly": 0,
|
||
"incinerateOnly": 0,
|
||
"transmutable": 0,
|
||
"protectedSkip": 0,
|
||
"unsupported": 0,
|
||
"estimatedRentLamports": 0
|
||
},
|
||
"accounts": []
|
||
}
|
||
```
|
||
|
||
### `POST /api/build/close-empty`
|
||
|
||
```jsonc
|
||
// in:
|
||
{ "wallet": "...", "accountAddresses": ["ata1", "ata2"] }
|
||
|
||
// out:
|
||
{
|
||
"transactionBase64": "...",
|
||
"preview": {
|
||
"accountsToClose": [],
|
||
"estimatedRentReturnedLamports": 0,
|
||
"rentDestination": "user_wallet_pubkey"
|
||
}
|
||
}
|
||
```
|
||
|
||
### `POST /api/build/burn`
|
||
|
||
```jsonc
|
||
// in:
|
||
{ "wallet": "...", "items": [{ "tokenAccount": "...", "mint": "...", "amount": "..." }] }
|
||
|
||
// out:
|
||
{
|
||
"transactionBase64": "...",
|
||
"preview": {
|
||
"tokensToBurn": [],
|
||
"accountsPotentiallyClosable": []
|
||
}
|
||
}
|
||
```
|
||
|
||
### `POST /api/receipt`
|
||
|
||
```jsonc
|
||
// in:
|
||
{ "wallet": "...", "txSignature": "...", "scanId": "uuid" }
|
||
|
||
// out:
|
||
{
|
||
"receiptId": "uuid",
|
||
"txSignature": "...",
|
||
"rentReturnedLamports": 0,
|
||
"closedAccounts": [],
|
||
"burnedTokens": [],
|
||
"skippedTokens": []
|
||
}
|
||
```
|
||
|
||
### `POST /api/prometheus/generate`
|
||
|
||
```jsonc
|
||
// in:
|
||
{ "receiptId": "uuid", "chaos": 0.25, "operatorSeed": "optional" }
|
||
|
||
// out:
|
||
{
|
||
"generationId": "uuid",
|
||
"spawnName": "...",
|
||
"ticker": "...",
|
||
"lore": "...",
|
||
"imagePrompt": "...",
|
||
"metadata": {},
|
||
"riskFlags": []
|
||
}
|
||
```
|
||
|
||
### Admin endpoints
|
||
|
||
Admin / generation-review endpoints (approve/reject generations, record Pump.fun
|
||
launches, view system events) are protected and not part of the public surface.
|
||
They back the admin review UI and the Spawn-record workflow (§10, §16).
|
||
|
||
---
|
||
|
||
## 5. Data Model
|
||
|
||
### Initial PostgreSQL tables (§15)
|
||
|
||
```
|
||
wallet_scans: id, wallet, status, created_at, completed_at, summary_json
|
||
|
||
token_accounts: id, scan_id, wallet, ata, mint, token_program,
|
||
raw_balance, ui_balance, decimals, symbol, name,
|
||
classification, warnings_json, estimated_rent_lamports,
|
||
created_at
|
||
|
||
cleanup_receipts: id, wallet, scan_id, tx_signature, rent_returned_lamports,
|
||
closed_accounts_count, burned_tokens_count, status,
|
||
created_at, receipt_json
|
||
|
||
prometheus_generations: id, receipt_id, input_json, output_json, status,
|
||
risk_flags_json, created_at, approved_at, rejected_at
|
||
|
||
spawn_records: id, generation_id, spawn_name, ticker, mint, metadata_uri,
|
||
pumpfun_url, launch_tx, status, created_at
|
||
```
|
||
|
||
Additional initial tables called out in §11/§15: `token_classifications`,
|
||
`burn_events`, `close_account_events`, `spawn_candidates`, and `system_events`
|
||
(audit/event log).
|
||
|
||
### Future tables (NOT in the burner MVP)
|
||
|
||
These arrive with the Essence ledger and PYRE Core program (Phases 6–7):
|
||
|
||
- `pyre_rounds` — round state machine.
|
||
- `essence_contributions` — recorded Essence per wallet per round.
|
||
- `claim_records` — Spawn claim accounting.
|
||
- `refund_records` — refunds for failed rounds.
|
||
- `influence_fields` — meta-influence inputs for Prometheus weighting.
|
||
|
||
> Per §5/§15: Essence may start in the database, but it must not be called a
|
||
> deposit until an on-chain custody model exists, and claims must not be promised
|
||
> until claim logic exists.
|
||
|
||
---
|
||
|
||
## 6. Redis Usage
|
||
|
||
Redis serves five roles (§11):
|
||
|
||
- **Job queue** — backs BullMQ; `apps/api` enqueues, `apps/worker` consumes.
|
||
- **Scan cache** — caches scan results so repeat views don't re-hit RPC.
|
||
- **Rate limiting** — throttles scan and build endpoints (§16).
|
||
- **Temporary quote cache** — short-lived swap/route quotes; stale quotes are
|
||
rejected by classification (§7).
|
||
- **Generation status** — live status of in-flight Prometheus jobs, read back by
|
||
`apps/web` / `apps/api`.
|
||
|
||
### BullMQ Worker Jobs
|
||
|
||
`apps/worker` processes these job types (§11):
|
||
|
||
- **Metadata enrichment** — slow token name/symbol/metadata lookups.
|
||
- **AI generation** — Prometheus Spawn generation.
|
||
- **Image prompts** — image-prompt generation for the Spawn.
|
||
- **Safety checks** — moderation of generated names/tickers/lore.
|
||
- **Collision checks** — ticker/name duplicate detection.
|
||
- **Confirmation tracking** — follow tx signatures to finality in the background.
|
||
- **Receipt enrichment** — backfill receipt detail after confirmation.
|
||
|
||
---
|
||
|
||
## 7. Solana RPC Requirements
|
||
|
||
PYRE uses an **external RPC provider only**. Per §11/§12, the MVP VPS must **not**
|
||
run a Solana validator or RPC node.
|
||
|
||
Required RPC capabilities (§11):
|
||
|
||
- Get token accounts by owner.
|
||
- Get account info.
|
||
- Simulate transactions.
|
||
- Send transactions.
|
||
- Confirm transactions.
|
||
- Parse token-account state.
|
||
|
||
The RPC endpoint URL is supplied via environment config (`packages/config`) and
|
||
treated as a secret. All chain access flows through `packages/solana`.
|
||
|
||
---
|
||
|
||
## 8. AI Services
|
||
|
||
AI is **API-based only** (§11/§12). The MVP server must **not** run local LLMs or
|
||
local image-generation models.
|
||
|
||
Service classes used:
|
||
|
||
- **Text generation** — Spawn name, ticker, lore, tagline, description, launch
|
||
copy (§9 outputs).
|
||
- **Image generation** — from the generated image prompt.
|
||
- **Moderation / safety** — filtering hate/explicit/extremist/copyrighted/
|
||
impersonation/scam-like output (§16).
|
||
|
||
All AI calls are made from `apps/worker` using clients configured via secrets in
|
||
`packages/config`. Prometheus generates a *candidate package for human review* —
|
||
it never launches tokens or controls funds (§9).
|
||
|
||
---
|
||
|
||
## 9. Infrastructure & Deployment
|
||
|
||
A single **8GB VPS** is sufficient for the MVP (§12). It runs:
|
||
|
||
- `pyre-web`, `pyre-api`, `pyre-worker`
|
||
- PostgreSQL
|
||
- Redis
|
||
- **nginx** (reverse proxy / TLS termination in front of web + api)
|
||
- **PM2 or systemd** (process supervision for the three Node services)
|
||
- logs / log rotation
|
||
- admin dashboard
|
||
|
||
The VPS must **NOT** run (§12):
|
||
|
||
- a Solana validator,
|
||
- a Solana RPC node,
|
||
- a large local LLM,
|
||
- local image generation,
|
||
- heavy indexing at scale.
|
||
|
||
**Base setup already completed (§12/§19):** `pyre` user created, root login
|
||
disabled, SSH key auth, UFW firewall, Fail2ban, basic hardening.
|
||
|
||
**Next setup (§12/§19):** Node.js 22, pnpm, Git, nginx, PostgreSQL, Redis, PM2,
|
||
Claude Code, project repo, environment files, backup script, log rotation.
|
||
|
||
**External accounts required (§19):** Solana RPC provider, OpenAI/Anthropic API
|
||
key, image-generation provider, domain name, GitHub repo, Pump.fun creator
|
||
wallet, optional IPFS/Arweave metadata service.
|
||
|
||
---
|
||
|
||
## 10. Build-Order Note (Phased Roadmap)
|
||
|
||
Components map to the §18 development phases so a reader knows *what gets built
|
||
when*:
|
||
|
||
| Phase | Focus | Components touched |
|
||
|-------|-------|--------------------|
|
||
| **0 — Server & Repo Setup** | VPS, pnpm workspace, web/api/worker skeletons, Postgres + Redis, nginx, env templates | all apps (skeleton), `packages/config`, `infra/` |
|
||
| **1 — Wallet Scanner** | Wallet connect, scan endpoint, token-account fetch, basic classification, results + protected/skipped UI | `apps/web`, `apps/api`, `packages/solana` (parsing), `packages/core` (enums/rules), `packages/db` (`wallet_scans`, `token_accounts`) |
|
||
| **2 — Close Empty ATAs** | Build close-account tx, decode/preview, signing, confirmation tracking, receipt page | `packages/solana` (close builder + decoder + simulation), `apps/api` (`/build/close-empty`, `/receipt`), `apps/worker` (confirmation tracking), `packages/db` (`cleanup_receipts`, `close_account_events`) |
|
||
| **3 — Burn Junk** | `INCINERATE_ONLY` classification, burn builder, burn-then-close, stronger confirmations, receipt update | `packages/solana` (burn builder), `apps/api` (`/build/burn`), `packages/db` (`burn_events`) |
|
||
| **4 — Prometheus Generator** | Generation input from receipt, meta mixer, name/ticker/lore + image prompt, safety checks, admin approval UI | `packages/prometheus`, `apps/worker` (AI/safety/collision jobs), `apps/api` (`/prometheus/generate`, admin), `apps/web` (review UI), `packages/db` (`prometheus_generations`, `spawn_candidates`) |
|
||
| **5 — Manual Pump.fun Launch** | Approved package, metadata JSON, operator checklist, record mint/url/tx, public Spawn page | `apps/web` (public Spawn page), `apps/api` (admin record endpoints), `packages/db` (`spawn_records`) |
|
||
| **6 — Essence / Round Prototype** | Swap-candidate detection, route quote preview, net Essence estimate, round dashboard, DB-only contribution record (no claim promises) | `packages/solana` (route eval), `apps/api`, `packages/db` (future `pyre_rounds`, `essence_contributions`, `influence_fields`) |
|
||
| **7 — PYRE Core Program** | Anchor program: create round, contribute Essence, contribution-receipt PDA, lock round, register Spawn, claim, refund, tests | `programs/pyre-core`, future tables `claim_records`, `refund_records` |
|
||
|
||
The first three phases deliver the trust-building burner (v0.1). The Anchor
|
||
program in `programs/pyre-core` is explicitly **not** needed until Phase 7 / v1.0.
|
||
|
||
---
|
||
|
||
> **PYRE returns your rent. The scraps feed the fire.**
|