diff --git a/GITHUB_ISSUES.md b/GITHUB_ISSUES.md deleted file mode 100644 index 0175aa9..0000000 --- a/GITHUB_ISSUES.md +++ /dev/null @@ -1,362 +0,0 @@ -# stellar-notify — GitHub Issues & Project Pipeline - -## How to create the GitHub Project board first - -1. In your `stellar-notify` org repo, click **Projects** tab → **New project** -2. Select **Board** view (Kanban) -3. Name it: `stellar-notify v0.1 Roadmap` -4. Add these columns (click **+ Add column** for each): - - `📋 Backlog` - - `🔍 Ready` - - `🚧 In Progress` - - `👀 In Review` - - `✅ Done` -5. Click **Settings** → **Workflows** → enable: - - "Item added to project" → set to `Backlog` - - "Pull request merged" → set to `Done` -6. Go back to the board. Now create the 10 issues below and they will auto-appear in Backlog. - ---- - -## Issue 1 — Core: Horizon payment streaming - -**Title:** `feat: implement Horizon SSE payment streaming with cursor persistence` - -**Labels:** `enhancement`, `core`, `good first issue` - -**Milestone:** `v0.1.0` - -**Body:** -``` -## Summary -The stream manager should open a Horizon SSE connection per active `payment` subscription and -persist the paging cursor to SQLite so streams resume correctly after a restart. - -## Acceptance criteria -- [ ] `StreamManager._streamPayments()` opens an SSE stream via `@stellar/stellar-sdk` -- [ ] Cursor saved to `stream_cursors` table after each event -- [ ] On restart, stream resumes from saved cursor (not "now") -- [ ] Filter by `asset_code` and `min_amount` applied before queuing delivery -- [ ] Unit test: mock Horizon SSE, assert delivery is queued for matching events -- [ ] Unit test: assert filtered events (wrong asset) do NOT queue a delivery - -## Notes -Use `server.payments().forAccount(addr).cursor(saved).stream({ onmessage })`. -Cursor must be saved *per account* key, not globally. -``` - -**Project column:** `🔍 Ready` - ---- - -## Issue 2 — Core: Soroban contract event polling - -**Title:** `feat: poll Soroban RPC getEvents for contract_event subscriptions` - -**Labels:** `enhancement`, `core`, `soroban` - -**Milestone:** `v0.1.0` - -**Body:** -``` -## Summary -Contract events are not available over Horizon SSE. We poll the Soroban JSON-RPC -`getEvents` method on a configurable interval. - -## Acceptance criteria -- [ ] `StreamManager._streamContractEvents()` polls `getEvents` using `fetch` -- [ ] `filter.poll_interval_ms` controls interval (default 5000ms) -- [ ] `filter.topics` passed through to the RPC filter array -- [ ] Tracks last seen ledger to avoid duplicate deliveries -- [ ] Gracefully handles RPC errors (logs, does not crash) -- [ ] Integration test against Soroban testnet RPC (or mocked response) - -## Notes -Soroban RPC: https://developers.stellar.org/docs/data/rpc/api-reference/methods/getEvents -Default testnet RPC: https://soroban-testnet.stellar.org -``` - -**Project column:** `📋 Backlog` - ---- - -## Issue 3 — Delivery: Webhook channel with HMAC signing - -**Title:** `feat: webhook delivery channel with HMAC-SHA256 request signing` - -**Labels:** `enhancement`, `delivery` - -**Milestone:** `v0.1.0` - -**Body:** -``` -## Summary -Webhook delivery must POST a JSON payload to the configured URL and optionally sign the -request body with HMAC-SHA256 so receivers can verify authenticity. - -## Acceptance criteria -- [ ] `POST` to `channel_config.url` with `Content-Type: application/json` -- [ ] Header `X-StellarNotify-Event: ` present on every request -- [ ] Header `X-StellarNotify-Signature: sha256=` present when `channel_config.secret` is set -- [ ] 10-second request timeout -- [ ] Non-2xx responses throw an error so the retry loop is triggered -- [ ] Unit test: assert signature header is correct for known payload + secret -- [ ] Unit test: assert delivery succeeds on 200 and fails on 500 - -## Security note -The HMAC is computed over the raw JSON body string before parsing. -Document the verification algorithm in README. -``` - -**Project column:** `🔍 Ready` - ---- - -## Issue 4 — Delivery: Exponential back-off retry engine - -**Title:** `feat: retry engine with exponential back-off for failed deliveries` - -**Labels:** `enhancement`, `delivery`, `reliability` - -**Milestone:** `v0.1.0` - -**Body:** -``` -## Summary -Failed deliveries must be automatically retried with exponential back-off so transient -outages (receiver down, SMTP blip) do not cause permanent notification loss. - -## Acceptance criteria -- [ ] Max retries configurable via `WEBHOOK_MAX_RETRIES` (default 5) -- [ ] Base delay configurable via `WEBHOOK_RETRY_BASE_MS` (default 1000ms) -- [ ] Delay formula: `BASE * 2^(attempt - 1)` ms -- [ ] After max retries, delivery status set to `failed` in DB -- [ ] `POST /api/deliveries/:id/retry` resets `attempts = 0` and `status = pending` -- [ ] Unit test: assert 3 failures → 3 delays with correct multipliers -- [ ] Unit test: assert 5 failures → status = `failed` - -## Notes -Back-off runs inside `DeliveryService.dispatch()` — no separate cron job needed for v0.1. -``` - -**Project column:** `🔍 Ready` - ---- - -## Issue 5 — API: Subscription CRUD endpoints - -**Title:** `feat: REST API for subscription lifecycle (create, list, update, delete)` - -**Labels:** `enhancement`, `api` - -**Milestone:** `v0.1.0` - -**Body:** -``` -## Summary -Developers need a clean REST API to manage subscriptions from their own apps or CI pipelines, -without needing to open the dashboard. - -## Acceptance criteria -- [ ] `GET /api/subscriptions` — list all subs for authenticated key -- [ ] `POST /api/subscriptions` — create, returns 201 with full object -- [ ] `GET /api/subscriptions/:id` — get single sub (403 if wrong key) -- [ ] `PATCH /api/subscriptions/:id` — toggle `active`, update `label` or `channel_config` -- [ ] `DELETE /api/subscriptions/:id` — removes from DB and closes stream -- [ ] Hot-reload: stream opens/closes immediately without restart -- [ ] Integration tests using supertest for all five endpoints - -## Request/response shape -See README for examples. Filter and channel_config are JSON objects — store as serialised -strings in SQLite, deserialise on read. -``` - -**Project column:** `🔍 Ready` - ---- - -## Issue 6 — Dashboard: Subscription management UI - -**Title:** `feat: Next.js dashboard — subscription list with create/pause/delete` - -**Labels:** `enhancement`, `dashboard`, `frontend` - -**Milestone:** `v0.1.0` - -**Body:** -``` -## Summary -The dashboard is the face of stellar-notify for non-API users. It must provide a clean UI -for full subscription management. - -## Acceptance criteria -- [ ] API-key entry screen with sessionStorage persistence -- [ ] Subscription list shows: type badge, channel, label, active status, account filter -- [ ] "New Subscription" modal with fields for all types and channels -- [ ] Toggle active / paused without page reload (optimistic update) -- [ ] Delete with confirmation dialog -- [ ] Stats bar: active count, total, delivered, pending, failed -- [ ] Mobile-responsive layout -- [ ] Dark space theme using CSS custom properties - -## Design notes -Font: IBM Plex Mono + DM Sans. -Color accent: #2ab9b9. -Background: #040b14 with subtle star-field pseudo-element. -``` - -**Project column:** `📋 Backlog` - ---- - -## Issue 7 — Dashboard: Delivery log with retry - -**Title:** `feat: delivery log tab with status filter and manual retry` - -**Labels:** `enhancement`, `dashboard`, `frontend` - -**Milestone:** `v0.1.0` - -**Body:** -``` -## Summary -Developers need visibility into what happened to each notification: was it delivered? -How many retries? What was the last error? Can they trigger a retry manually? - -## Acceptance criteria -- [ ] Delivery log table: status badge, event type, subscription ID, attempt count, timestamp -- [ ] Filter bar: All | Delivered | Pending | Failed -- [ ] "Retry" button visible on `failed` rows — calls `POST /api/deliveries/:id/retry` -- [ ] Auto-refreshes every 10 seconds via SWR -- [ ] Empty state with helpful message -- [ ] Pagination or limit to last 100 entries - -## Notes -Use the `useSWR` hook with `refreshInterval: 10_000`. -Subscription ID in table should truncate to 8 chars + ellipsis. -``` - -**Project column:** `📋 Backlog` - ---- - -## Issue 8 — Infrastructure: Docker & docker-compose setup - -**Title:** `chore: production-ready Dockerfile and docker-compose for engine + dashboard` - -**Labels:** `infrastructure`, `devops` - -**Milestone:** `v0.1.0` - -**Body:** -``` -## Summary -stellar-notify must be self-hostable with a single `docker-compose up -d`. The Docker setup -should follow security best practices (non-root user, minimal image). - -## Acceptance criteria -- [ ] `Dockerfile` — multi-stage build (deps → runtime), non-root user `stellar` -- [ ] Persistent volume mounted at `/app/data` for SQLite -- [ ] `HEALTHCHECK` using `wget` against `/api/health` -- [ ] `dashboard/Dockerfile` — Next.js standalone output -- [ ] `docker-compose.yml` — engine + dashboard services, shared volume, `env_file` -- [ ] `docker-compose up -d` starts cleanly with only `.env` configured -- [ ] README "Quick start" section documents the two-command flow - -## Notes -Use `node:20-alpine` base image. -`better-sqlite3` requires native build — ensure `python3` and `make` are available in build stage. -``` - -**Project column:** `📋 Backlog` - ---- - -## Issue 9 — Testing: Integration test suite for stream + delivery pipeline - -**Title:** `test: integration tests for full stream → delivery pipeline` - -**Labels:** `testing`, `reliability` - -**Milestone:** `v0.2.0` - -**Body:** -``` -## Summary -Unit tests cover individual functions, but we need integration tests that simulate a real -Stellar testnet event flowing through the full pipeline to a webhook receiver. - -## Acceptance criteria -- [ ] Test harness spins up in-memory SQLite and the Express app -- [ ] Mock Horizon SSE endpoint using `nock` or `msw` -- [ ] Assert that a mocked `payment` event creates a `delivery` row with `status = delivered` -- [ ] Assert webhook receiver (local http server in test) receives correct payload -- [ ] Assert HMAC signature validates against shared secret -- [ ] Assert retry fires after simulated 500 response from receiver -- [ ] CI runs these tests via `npm test` without network access - -## Tech -Jest + supertest + nock. -Tests live in `src/__tests__/integration/`. -``` - -**Project column:** `📋 Backlog` - ---- - -## Issue 10 — Feature: PostgreSQL support via connection URL - -**Title:** `feat: PostgreSQL support via DATABASE_URL env variable` - -**Labels:** `enhancement`, `database`, `scalability` - -**Milestone:** `v0.2.0` - -**Body:** -``` -## Summary -SQLite is perfect for self-hosted single-instance deployments, but teams deploying to -Railway/Render/Fly.io with horizontal scaling need PostgreSQL. - -## Acceptance criteria -- [ ] If `DATABASE_URL` env var is set, use `pg` (node-postgres) instead of better-sqlite3 -- [ ] DB abstraction layer in `src/db/database.js` detects which driver to use -- [ ] Migrations run on both SQLite and Postgres (use raw SQL, not an ORM) -- [ ] All queries work unchanged on both backends -- [ ] `docker-compose.yml` gains an optional `postgres` service profile -- [ ] README documents `DATABASE_URL` format - -## Notes -Keep `better-sqlite3` as the default — SQLite requires zero config. -Consider a lightweight adapter interface: `{ prepare, exec }` for SQLite vs `{ query }` for Postgres. -Only add `pg` as a dependency (not `pg-native`). -``` - -**Project column:** `📋 Backlog` - ---- - -## Pipeline setup summary - -Once all 10 issues are created: - -1. Open your **Project board** (`stellar-notify v0.1 Roadmap`) -2. Move issues 1, 3, 4, 5 to **🔍 Ready** (they are dependencies of everything else) -3. Leave issues 2, 6, 7, 8 in **📋 Backlog** -4. Issues 9 and 10 are `v0.2.0` — keep in Backlog, update milestone when ready -5. Assign **yourself** to issues 1, 3, 4, 5 to signal active development to the Drips Wave reviewers - -**Labels to create in the repo** (Settings → Labels): -| Label | Colour | -|---|---| -| `core` | `#0e7c7c` | -| `delivery` | `#2ab9b9` | -| `dashboard` | `#6366f1` | -| `frontend` | `#8b5cf6` | -| `soroban` | `#0891b2` | -| `infrastructure` | `#64748b` | -| `devops` | `#475569` | -| `testing` | `#16a34a` | -| `reliability` | `#15803d` | -| `database` | `#b45309` | -| `scalability` | `#92400e` |