diff --git a/.gitignore b/.gitignore
index 95050e22..2d430582 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,115 @@
-.qodo
+# ============================================================
+# StellarHunts — root .gitignore
+# Covers: Node.js / NestJS, Next.js, Rust / Cargo, and IDEs
+# ============================================================
+
+# ── Environment & secrets ───────────────────────────────────
+.env
+.env.*
+.env.local
+.env.*.local
+.env.development
+.env.development.local
+.env.test
+.env.test.local
+.env.production
+.env.production.local
+*.pem
+*.key
+*.p12
+secrets/
+
+# ── Node / npm / yarn / pnpm ────────────────────────────────
+node_modules/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+.pnp
+.pnp.js
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+
+# ── Build artefacts ─────────────────────────────────────────
+dist/
+build/
+out/
+.next/
+.nuxt/
+.output/
+.cache/
+*.tsbuildinfo
+
+# ── Rust / Cargo ────────────────────────────────────────────
+target/
+**/*.rs.bk
+.cargo/registry/
+.cargo/git/
+
+# ── Test & coverage ─────────────────────────────────────────
+coverage/
+.nyc_output/
+test_snapshots/
+*.lcov
+*.snap
+
+# ── Logs & diagnostics ──────────────────────────────────────
+logs/
+*.log
+pids/
+*.pid
+*.pid.lock
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# ── OS-generated ────────────────────────────────────────────
.DS_Store
-node_modules
-dist
-.env
\ No newline at end of file
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+Thumbs.db
+ehthumbs.db
+Desktop.ini
+
+# ── IDE — JetBrains (IntelliJ / WebStorm / CLion / …) ───────
+.idea/
+*.iml
+*.iws
+*.ipr
+.project
+.classpath
+.settings/
+*.sublime-workspace
+*.sublime-project
+
+# ── IDE — VS Code ───────────────────────────────────────────
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+
+# ── IDE — Eclipse / generic ─────────────────────────────────
+.c9/
+*.launch
+
+# ── Temporary files ─────────────────────────────────────────
+.temp/
+.tmp/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~
+
+# ── Tooling ─────────────────────────────────────────────────
+.qodo
+.tool-versions
+.turbo/
+.parcel-cache/
+
+# ── Docker (local overrides only — Dockerfiles stay tracked) ─
+docker-compose.override.yml
diff --git a/docs/adr/0001-record-architecture-decisions.md b/docs/adr/0001-record-architecture-decisions.md
new file mode 100644
index 00000000..bfcc39db
--- /dev/null
+++ b/docs/adr/0001-record-architecture-decisions.md
@@ -0,0 +1,66 @@
+# ADR-0001: Record Architecture Decisions
+
+**Date:** 2025-07-24
+**Status:** Accepted
+**Deciders:** StellarHunts core team
+
+---
+
+## Context
+
+Architecture decisions in StellarHunts have historically been made in PR
+descriptions, Discord threads, and informal team discussions. When a new
+contributor joins, there is no single place to find *why* certain choices
+were made — only *what* the current code does. This creates ramp-up
+friction and leads to decisions being revisited unnecessarily.
+
+## Decision
+
+We will use Architecture Decision Records (ADRs) to capture significant
+architectural and design decisions. Each ADR is a short Markdown document
+stored in `docs/adr/` with a sequential four-digit prefix and a
+kebab-case title.
+
+ADRs should be created when:
+- A technology or library is selected over alternatives
+- A structural pattern is established (e.g., module layout, naming)
+- An existing decision is reversed or superseded
+- A design has meaningful trade-offs worth documenting
+
+ADRs are **immutable once accepted**. Superseding an old decision means
+creating a new ADR and updating the old one's status field.
+
+### Template
+
+```
+# ADR-NNNN:
+
+**Date:** YYYY-MM-DD
+**Status:** Proposed | Accepted | Deprecated | Superseded by ADR-XXXX
+**Deciders:**
+
+---
+
+## Context
+
+
+## Decision
+
+
+## Consequences
+### Positive
+
+### Negative / Trade-offs
+
+```
+
+## Consequences
+
+### Positive
+- New contributors can understand system rationale without reading PRs
+- Decisions are revisited deliberately rather than accidentally
+- Lightweight process — one Markdown file per decision
+
+### Negative / Trade-offs
+- Requires discipline to write an ADR *before* merging significant changes
+- ADRs can go stale if not kept up to date with status changes
diff --git a/docs/adr/0002-zustand-alongside-redux-toolkit.md b/docs/adr/0002-zustand-alongside-redux-toolkit.md
new file mode 100644
index 00000000..f2dfee01
--- /dev/null
+++ b/docs/adr/0002-zustand-alongside-redux-toolkit.md
@@ -0,0 +1,62 @@
+# ADR-0002: Use Zustand as the Primary State Manager Alongside Redux Toolkit
+
+**Date:** 2025-07-24
+**Status:** Accepted
+**Deciders:** StellarHunts frontend team
+
+---
+
+## Context
+
+The StellarHunts frontend needs client-side state management for:
+
+1. **Game state** — current puzzle, difficulty level, completed puzzles,
+ score, NFT collection. This state must survive page refreshes
+ (localStorage persistence) and is mutated frequently during gameplay.
+2. **Auth state** — current user, JWT token, wallet address.
+3. **Server state** — leaderboard data, puzzle content, referral stats.
+ This is cached, stale-while-revalidate data fetched from the NestJS API.
+
+Two popular choices were on the table: **Zustand** and
+**Redux Toolkit (RTK)**.
+
+| Criterion | Zustand | Redux Toolkit |
+|-----------|---------|---------------|
+| Bundle size | ~3 kB | ~20 kB |
+| Boilerplate | Minimal (no actions/reducers) | Moderate (slice files) |
+| Middleware / devtools | Optional, plugin-based | First-class |
+| Persistence | `zustand/middleware` `persist` | `redux-persist` |
+| Learning curve | Low | Medium |
+| Ecosystem maturity | Stable, wide adoption | Very mature, huge ecosystem |
+
+`@reduxjs/toolkit` is already listed as a production dependency (version
+`^2.5.1`) because it was planned for a more complex slice-based state
+model. In practice the team converged on Zustand stores for all current
+state needs.
+
+## Decision
+
+- **Zustand** is the **primary** state management library for game state,
+ auth state, and reward state (see `frontend/store/`).
+- **`@reduxjs/toolkit`** remains in `package.json` and should be used if
+ future requirements call for complex middleware chains, time-travel
+ debugging, or shared state slices that benefit from RTK's code
+ generation patterns (e.g., `createEntityAdapter`).
+- **TanStack Query** handles all *server state* — API responses, caching,
+ background refetching — and is not replaced by either of the above.
+
+## Consequences
+
+### Positive
+- Simple, readable store definitions — a store is just a `create()` call
+- `persist` middleware handles localStorage serialization out of the box
+- Low bundle size contribution
+- Devtools integration available via `zustand/middleware` `devtools`
+
+### Negative / Trade-offs
+- RTK's advanced features (immer-backed reducers, RTK Query, entity
+ adapters) are unavailable unless RTK is wired up in the future
+- Two state libraries in `package.json` can confuse new contributors —
+ this ADR resolves that ambiguity
+- No centralized dispatcher pattern; state mutations are co-located in
+ store files, which can scatter business logic
diff --git a/docs/adr/0003-nestjs-modular-monolith.md b/docs/adr/0003-nestjs-modular-monolith.md
new file mode 100644
index 00000000..817dd4ea
--- /dev/null
+++ b/docs/adr/0003-nestjs-modular-monolith.md
@@ -0,0 +1,68 @@
+# ADR-0003: NestJS Modular Monolith Over Microservices
+
+**Date:** 2025-07-24
+**Status:** Accepted
+**Deciders:** StellarHunts backend team
+
+---
+
+## Context
+
+StellarHunts requires a backend that handles authentication, puzzle
+management, NFT claim orchestration, real-time multiplayer matchmaking,
+notifications, referrals, analytics, and more. These are distinct
+domains, so the question arose: should the backend be structured as a
+**microservices** cluster or a **monolith**?
+
+The team evaluated three architectural patterns:
+
+| Pattern | Deployment | Scaling | Complexity | Team size fit |
+|---------|-----------|---------|------------|---------------|
+| Unstructured monolith | Single process | Vertical only | Low initially | Small |
+| Modular monolith | Single process | Vertical + horizontal replicas | Medium | Small–medium |
+| Microservices | Many processes | Per-service horizontal | High | Large |
+
+Key constraints at the time of decision:
+- Team of fewer than 10 engineers
+- Early-stage product — domain boundaries still evolving
+- Single PostgreSQL instance; cross-service transactions would be complex
+- Redis already required for Socket.IO adapter and rate limiting
+- Need to ship quickly and iterate
+
+## Decision
+
+The backend is a **NestJS modular monolith**.
+
+- All domains live under `backend/src/` as NestJS feature modules
+ (`@Module()` decorated classes).
+- Each module owns its controller, service, entities, and DTOs.
+- Cross-domain calls happen through NestJS dependency injection (imported
+ modules), **not** via HTTP or a message bus.
+- The monolith is deployed as a single Docker container / process;
+ horizontal scaling is achieved by running multiple replicas behind a
+ load balancer with Redis as the shared session/socket adapter.
+
+If a specific domain needs independent scaling in the future (e.g.,
+the multiplayer matchmaking gateway), it can be extracted into a
+standalone NestJS microservice using the built-in `@nestjs/microservices`
+transport layer with minimal refactoring because the module boundary
+already exists.
+
+## Consequences
+
+### Positive
+- Single deployment unit — simpler CI/CD and local development
+- No distributed-transaction complexity; TypeORM transactions work across
+ all domains
+- NestJS DI container enforces explicit module boundaries without the
+ operational overhead of separate services
+- Straightforward to extract a module into a microservice later
+
+### Negative / Trade-offs
+- A poorly written module can import anything, eroding boundaries over
+ time — code review must enforce the module contract
+- A crash in one domain crashes the whole process (mitigated by process
+ managers and health checks)
+- Vertical scaling limits apply; the team must monitor whether any single
+ domain (e.g., real-time sockets) becomes a bottleneck before extracting
+ it
diff --git a/docs/adr/0004-soroban-over-evm.md b/docs/adr/0004-soroban-over-evm.md
new file mode 100644
index 00000000..4ec9bb2c
--- /dev/null
+++ b/docs/adr/0004-soroban-over-evm.md
@@ -0,0 +1,71 @@
+# ADR-0004: Use Soroban (Stellar) Over EVM-Compatible Chains
+
+**Date:** 2025-07-24
+**Status:** Accepted
+**Deciders:** StellarHunts core team
+
+---
+
+## Context
+
+StellarHunts awards on-chain NFT badges when players complete puzzle
+levels. The team needed to select a smart-contract platform to host:
+
+1. **Game contract** — question lifecycle, answer validation (SHA-256),
+ player level progression.
+2. **NFT badge contract** — per-level badge minting with role-gated
+ authorization.
+
+The two primary candidates were **Soroban on Stellar** and an
+**EVM-compatible chain** (Ethereum mainnet, Polygon, or Base).
+
+| Criterion | Soroban / Stellar | EVM (Ethereum / Polygon) |
+|-----------|-------------------|--------------------------|
+| Transaction fees | Sub-cent on Stellar | Variable (gwei spikes on mainnet; low on L2s) |
+| Finality | ~5 s (Stellar consensus) | ~12 s ETH / ~2 s Polygon |
+| Smart contract language | Rust (soroban-sdk) | Solidity / Vyper |
+| Tooling maturity | Growing (Stellar CLI, soroban-cli) | Very mature (Hardhat, Foundry, OpenZeppelin) |
+| NFT standards | Custom (no ERC-721 equivalent yet) | ERC-721 / ERC-1155 well established |
+| Wallet ecosystem | Freighter, Lobstr, Albedo | MetaMask, WalletConnect (broad) |
+| Developer community | Smaller, niche | Large, extensive resources |
+| Educational alignment | Matches project's Stellar-learning theme | Generic blockchain knowledge |
+
+The project's **educational mission** is to teach players about
+blockchain technology — specifically the Stellar ecosystem. Using Soroban
+keeps the on-chain layer consistent with the subject matter being taught.
+
+## Decision
+
+StellarHunts uses **Soroban smart contracts on the Stellar network** for
+all on-chain game logic and NFT badge minting.
+
+- Contracts are written in **Rust** using `soroban-sdk 22.x`.
+- The workspace lives in `onchain/` with a Cargo workspace manifest.
+- Two production contracts exist:
+ - `stellar_hunts` — game logic
+ - `stellar_hunts_nft` — badge minting
+- Local development and CI use Stellar Testnet;
+ `STELLAR_MODE=mock` allows the backend to run without a live network.
+- Answer privacy is preserved on-chain via `env.crypto().sha256()` —
+ no plaintext answers are stored in contract state.
+
+## Consequences
+
+### Positive
+- Aligns with the project's educational goal of teaching Stellar/Soroban
+- Very low and predictable transaction fees
+- Fast finality reduces wait time after puzzle completion
+- Rust's type system and Soroban's sandboxed WASM runtime provide strong
+ safety guarantees
+- `STELLAR_MODE=mock` lets the backend be developed and tested without
+ a live network dependency
+
+### Negative / Trade-offs
+- Smaller developer community means fewer tutorials and third-party
+ tooling compared to EVM
+- No standardized NFT interface (ERC-721) — badge ownership queries use
+ a custom `has_level_badge` function
+- Freighter wallet has less browser/mobile coverage than MetaMask's
+ ecosystem
+- Team members with an EVM background need to learn Rust and the Soroban
+ execution model
diff --git a/docs/api.md b/docs/api.md
new file mode 100644
index 00000000..8c5fa698
--- /dev/null
+++ b/docs/api.md
@@ -0,0 +1,407 @@
+# StellarHunts API Reference
+
+Base URL: `http://localhost:3001` (development)
+Interactive docs: `http://localhost:3001/api/docs` (Swagger UI)
+
+**Authentication:** Unless noted as _Public_, all endpoints require a
+`Authorization: Bearer ` header obtained from `POST /auth/login`.
+
+> This document was generated from the NestJS controller source. For the
+> full request/response schemas, open the Swagger UI while the backend is
+> running (`npm run start:dev`).
+
+---
+
+## Table of Contents
+
+- [Auth](#auth)
+- [Users](#users)
+- [Puzzles (Game)](#puzzles-game)
+- [Puzzles (Admin CRUD)](#puzzles-admin-crud)
+- [Puzzle Submission](#puzzle-submission)
+- [Puzzle Categories](#puzzle-categories)
+- [Puzzle Dependencies](#puzzle-dependencies)
+- [Puzzle Translations](#puzzle-translations)
+- [Content](#content)
+- [Rewards](#rewards)
+- [NFT Claim](#nft-claim)
+- [Reward Shop](#reward-shop)
+- [Achievements](#achievements)
+- [Badges](#badges)
+- [Progress](#progress)
+- [Streaks](#streaks)
+- [Time Trial](#time-trial)
+- [In-App Notifications](#in-app-notifications)
+- [Referrals](#referrals)
+- [Challenges](#challenges)
+- [Feedback](#feedback)
+- [Multiplayer Queue](#multiplayer-queue)
+- [User Ranking](#user-ranking)
+- [Activity](#activity)
+- [Wallet](#wallet)
+- [Admin](#admin)
+
+---
+
+## Auth
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/auth/register` | Public | Register a new user account |
+| POST | `/auth/login` | Public | Log in and receive a JWT |
+| GET | `/auth/profile` | JWT | Get the authenticated user's profile |
+| POST | `/auth/validate-token` | JWT | Validate a JWT token |
+
+---
+
+## Users
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/users` | Public | Create a new user |
+| PATCH | `/users/profile` | JWT | Update the authenticated user's profile |
+| POST | `/users/link-wallet` | JWT | Link a Stellar wallet address to the account |
+| GET | `/users/:id` | JWT | Get a user by ID |
+| GET | `/users/:id/rank` | JWT | Get a user's ranking information |
+| GET | `/users/:id/progress` | JWT | Get a user's overall progress |
+
+---
+
+## Puzzles (Game)
+
+Handles active gameplay interactions (submit answers, request hints).
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/puzzles/submit` | JWT | Submit an answer for a puzzle |
+| POST | `/puzzles/hint` | JWT | Request a hint for the current puzzle |
+| GET | `/puzzles/progress` | JWT | Get the authenticated user's puzzle progress |
+| GET | `/puzzles/rate-limit-status` | JWT | Check current rate-limit status for submissions |
+| GET | `/puzzles/active` | Public | List all active puzzles |
+
+---
+
+## Puzzles (Admin CRUD)
+
+Full CRUD for puzzle management. Requires admin role.
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/admin/puzzles` | JWT + Admin | Create a new puzzle |
+| GET | `/admin/puzzles` | JWT + Admin | List all puzzles (admin view) |
+| GET | `/admin/puzzles/:id` | JWT + Admin | Get a puzzle by ID (admin view) |
+| PATCH | `/admin/puzzles/:id` | JWT + Admin | Update a puzzle |
+| DELETE | `/admin/puzzles/:id` | JWT + Admin | Delete a puzzle |
+
+---
+
+## Puzzle Submission
+
+Separate submission endpoint (legacy / alternative path).
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/puzzle-submission` | JWT | Submit a puzzle answer |
+
+---
+
+## Puzzle Categories
+
+Manage puzzle categories and the puzzles belonging to them.
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| GET | `/puzzle-categories/puzzles-by-category` | Public | Get puzzles grouped by category |
+| GET | `/puzzle-categories/categories` | Public | List all categories |
+| GET | `/puzzle-categories/categories/:id` | Public | Get a category by ID |
+| GET | `/puzzle-categories/categories/slug/:slug` | Public | Get a category by slug |
+| POST | `/puzzle-categories/categories` | JWT + Admin | Create a category |
+| PUT | `/puzzle-categories/categories/:id` | JWT + Admin | Update a category |
+| DELETE | `/puzzle-categories/categories/:id` | JWT + Admin | Delete a category |
+| GET | `/puzzle-categories/puzzles` | Public | List all categorised puzzles |
+| GET | `/puzzle-categories/puzzles/:id` | Public | Get a categorised puzzle by ID |
+| POST | `/puzzle-categories/puzzles` | JWT + Admin | Add a puzzle to a category |
+| PUT | `/puzzle-categories/puzzles/:id` | JWT + Admin | Update a puzzle's category assignment |
+| DELETE | `/puzzle-categories/puzzles/:id` | JWT + Admin | Remove a puzzle from a category |
+| GET | `/puzzle-categories/categories/:id/puzzles` | Public | Get all puzzles in a category |
+| GET | `/puzzle-categories/puzzles/difficulty/:difficulty` | Public | Filter puzzles by difficulty |
+| GET | `/puzzle-categories/puzzles/search` | Public | Search puzzles |
+| POST | `/puzzle-categories/seed-categories` | JWT + Admin | Seed default categories |
+
+---
+
+## Puzzle Dependencies
+
+Prerequisite / unlock chain management.
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/puzzle-dependencies` | JWT + Admin | Create a dependency |
+| GET | `/puzzle-dependencies` | JWT | List all dependencies |
+| GET | `/puzzle-dependencies/puzzle/:puzzleId` | JWT | Get dependencies for a puzzle |
+| GET | `/puzzle-dependencies/:id` | JWT | Get a dependency by ID |
+| PATCH | `/puzzle-dependencies/:id` | JWT + Admin | Update a dependency |
+| DELETE | `/puzzle-dependencies/:id` | JWT + Admin | Delete a dependency |
+| DELETE | `/puzzle-dependencies/puzzle/:puzzleId` | JWT + Admin | Remove all dependencies for a puzzle |
+| POST | `/puzzle-dependencies/check-eligibility` | JWT | Check if a user is eligible to attempt a puzzle |
+| POST | `/puzzle-dependencies/mark-completed` | JWT | Mark a dependency as completed |
+| GET | `/puzzle-dependencies/user/:userId/completed` | JWT | List completed dependencies for a user |
+| GET | `/puzzle-dependencies/user/:userId/unlocked` | JWT | List unlocked puzzles for a user |
+| GET | `/puzzle-dependencies/chain/:puzzleId` | JWT | Get the full prerequisite chain for a puzzle |
+| GET | `/puzzle-dependencies/stats/:puzzleId` | JWT | Get dependency stats for a puzzle |
+
+---
+
+## Puzzle Translations
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/puzzle-translations` | JWT + Admin | Create a translation |
+| PUT | `/puzzle-translations/:id` | JWT + Admin | Update a translation |
+| GET | `/puzzle-translations/:puzzleId` | Public | Get all translations for a puzzle |
+| GET | `/puzzle-translations/:puzzleId/lang` | Public | Get a translation for a puzzle in a specific language |
+
+---
+
+## Content
+
+Educational articles and resources.
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| GET | `/content` | Public | List published content |
+| GET | `/content/:id` | Public | Get a content item by ID |
+| POST | `/admin/content` | JWT + Admin | Create a content item |
+| GET | `/admin/content` | JWT + Admin | List all content (admin view) |
+| GET | `/admin/content/:id` | JWT + Admin | Get a content item (admin view) |
+| PATCH | `/admin/content/:id` | JWT + Admin | Update a content item |
+| DELETE | `/admin/content/:id` | JWT + Admin | Delete a content item |
+
+---
+
+## Rewards
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/rewards` | JWT + Admin | Create a reward |
+| GET | `/rewards` | JWT | List all rewards |
+| GET | `/rewards/:id` | JWT | Get a reward by ID |
+| GET | `/rewards/challenge/:challengeId` | JWT | Get rewards for a challenge |
+| POST | `/rewards/claim` | JWT | Claim a reward |
+| GET | `/rewards/user/:userId/claims` | JWT | List all reward claims for a user |
+| GET | `/rewards/claims/:id` | JWT | Get a specific claim |
+| GET | `/rewards/:id/stats` | JWT | Get claim stats for a reward |
+| DELETE | `/rewards/:id` | JWT + Admin | Delete a reward |
+
+---
+
+## NFT Claim
+
+On-chain Soroban NFT badge minting.
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/nft-claim/claim` | JWT | Trigger an NFT badge mint for a completed level |
+
+---
+
+## Reward Shop
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| GET | `/reward-shop` | JWT | List reward shop items (see Swagger for full schema) |
+
+---
+
+## Achievements
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| GET | `/achievements/:playerId` | JWT | Get all achievements for a player |
+
+---
+
+## Badges
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/badges/assign` | JWT + Admin | Assign a badge to a user |
+| GET | `/badges/user/:id` | JWT | Get all badges for a user |
+
+---
+
+## Progress
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| GET | `/users/:id/progress` | JWT | Get a user's progress summary |
+
+---
+
+## Streaks
+
+### Authenticated
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/streaks/activity` | JWT | Record a streak activity event |
+| GET | `/streaks/user/:userId` | JWT | Get streak data for a user |
+| GET | `/streaks/my-streak` | JWT | Get the authenticated user's streak |
+| GET | `/streaks/leaderboard` | JWT | Streak leaderboard |
+| GET | `/streaks/history` | JWT | Authenticated user's streak history |
+| GET | `/streaks/user/:userId/history` | JWT | Streak history for a user |
+| POST | `/streaks/recalculate` | JWT + Admin | Recalculate streaks |
+| POST | `/streaks/reset` | JWT + Admin | Reset streaks |
+| GET | `/streaks/active` | JWT | List users with active streaks |
+
+### Public
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| GET | `/public/streaks/user/:userId` | Public | Get streak data for a user (public) |
+| GET | `/public/streaks/leaderboard` | Public | Public streak leaderboard |
+| GET | `/public/streaks/user/:userId/history` | Public | Public streak history for a user |
+| GET | `/public/streaks/stats` | Public | Global streak statistics |
+
+---
+
+## Time Trial
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/time-trial/start` | JWT | Start a timed puzzle attempt |
+| POST | `/time-trial/submit/:id` | JWT | Submit an answer for a time trial |
+| GET | `/time-trial/results/:userId` | JWT | Get time trial results for a user |
+| GET | `/time-trial/leaderboard/:puzzleId` | Public | Time trial leaderboard for a puzzle |
+
+---
+
+## In-App Notifications
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| GET | `/in-app-notifications` | JWT | List notifications for the authenticated user |
+| GET | `/in-app-notifications/unread-count` | JWT | Get unread notification count |
+| POST | `/in-app-notifications` | JWT | Create a notification |
+| POST | `/in-app-notifications/system` | JWT + Admin | Send a system-wide notification |
+| PATCH | `/in-app-notifications/read` | JWT | Mark a notification as read |
+| PATCH | `/in-app-notifications/read-all` | JWT | Mark all notifications as read |
+| PATCH | `/in-app-notifications/archive` | JWT | Archive a notification |
+| DELETE | `/in-app-notifications/:id` | JWT | Delete a notification |
+
+---
+
+## Referrals
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/referrals/codes` | JWT | Generate a referral code |
+| GET | `/referrals/codes/my` | JWT | Get the authenticated user's referral code |
+| POST | `/referrals/invites` | JWT | Send a referral invite |
+| GET | `/referrals/stats` | JWT | Get referral statistics |
+| GET | `/referrals/history` | JWT | Get referral history |
+| POST | `/referrals/invites/:id/complete` | JWT | Mark a referral invite as completed |
+| POST | `/referrals/register` | Public | Register via referral code |
+
+---
+
+## Challenges
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/challenges` | JWT + Admin | Create a challenge |
+| GET | `/challenges` | JWT | List all challenges |
+| GET | `/challenges/available` | JWT | List challenges available to the user |
+| GET | `/challenges/daily` | JWT | Get today's daily challenge |
+| GET | `/challenges/weekly` | JWT | Get the current weekly challenge |
+| GET | `/challenges/:id` | JWT | Get a challenge by ID |
+| GET | `/challenges/:id/stats` | JWT | Get stats for a challenge |
+| PATCH | `/challenges/:id` | JWT + Admin | Update a challenge |
+| DELETE | `/challenges/:id` | JWT + Admin | Delete a challenge |
+
+---
+
+## Feedback
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/feedback` | JWT | Submit feedback |
+| GET | `/feedback/admin` | JWT + Admin | List all feedback (admin view) |
+| GET | `/feedback/stats` | JWT + Admin | Feedback statistics |
+| GET | `/feedback/target/:targetType` | JWT | Get feedback for a target type |
+| PUT | `/feedback/admin/:id` | JWT + Admin | Update a feedback entry |
+| DELETE | `/feedback/admin/:id` | JWT + Admin | Delete a feedback entry |
+
+---
+
+## Multiplayer Queue
+
+Real-time matchmaking via Socket.IO. See the WebSocket gateway for event names.
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| *(Socket.IO)* | `ws://localhost:3001` | JWT | Connect to the multiplayer matchmaking gateway |
+
+---
+
+## User Ranking
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| GET | `/users/:id/rank` | JWT | Get global ranking for a user |
+
+---
+
+## Activity
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| GET | `/activity` | JWT | Get the social activity feed (see Swagger for full schema) |
+
+---
+
+## Wallet
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/wallet/link` | JWT | Link a Stellar wallet address |
+| POST | `/wallet/verify-signature` | JWT | Verify a wallet signature (POST) |
+| GET | `/wallet/verify-signature` | JWT | Verify a wallet signature (GET) |
+
+---
+
+## Admin
+
+| Method | Path | Auth | Description |
+|--------|------|------|-------------|
+| POST | `/admin/login` | Public | Admin login |
+| GET | `/admin/profile` | JWT + Admin | Get admin profile |
+| GET | `/admin/puzzles` | JWT + Admin | List all puzzles (admin) |
+| GET | `/admin/content` | JWT + Admin | List all content (admin) |
+
+---
+
+## Error Responses
+
+All endpoints return standard HTTP status codes:
+
+| Status | Meaning |
+|--------|---------|
+| 200 | OK |
+| 201 | Created |
+| 400 | Bad Request (validation error) |
+| 401 | Unauthorized (missing or invalid JWT) |
+| 403 | Forbidden (insufficient role) |
+| 404 | Not Found |
+| 429 | Too Many Requests (rate limited) |
+| 500 | Internal Server Error |
+
+Error bodies follow the NestJS default shape:
+
+```json
+{
+ "statusCode": 400,
+ "message": ["field must not be empty"],
+ "error": "Bad Request"
+}
+```
diff --git a/docs/architecture.md b/docs/architecture.md
index 43652943..0dd02bfd 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -27,8 +27,8 @@ StellarHunts is a three-tier gamified blockchain application. The system consist
│ │ Module │ │ Modules │ │ (StellarHandlerSvc) │ │
│ └────────────┘ └────────────┘ └──────────────────────┘ │
│ ┌────────────┐ ┌────────────┐ ┌──────────────────────┐ │
-│ │ Progress │ │ Leaderboard│ │ Multiplayer (Socket) │ │
-│ │ Module │ │ Module │ │ Module │ │
+│ │ Progress │ │ Multiplayer│ │ In-App Notifications │ │
+│ │ Module │ │ (Socket) │ │ Module │ │
│ └────────────┘ └────────────┘ └──────────────────────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
@@ -71,7 +71,6 @@ StellarHunts/
| State (Global) | Zustand | Game state, user session, progress tracking |
| State (Server) | TanStack Query | API caching, optimistic updates |
| Auth | NextAuth.js | OAuth, wallet linking, JWT sessions |
-| Forms | Formik + Yup | Form state management and validation |
| Blockchain | `@stellar/stellar-sdk` + `@stellar/freighter-api` | Wallet connection, contract invocation |
| HTTP | Axios | API client with interceptors |
| UI Components | Radix UI + shadcn/ui | Accessible primitives, design system |
@@ -81,17 +80,11 @@ StellarHunts/
```
/ Homepage
/game Puzzle game interface
-/leaderboard Global rankings
/puzzles/roadmap Puzzle progression timeline
/invite-friends Referral program
/ref/[referralId] Referral landing page
/admin/puzzle-review Admin puzzle management
/admin/puzzle-submission Admin puzzle creation
-/faq Frequently asked questions
-/terms Terms of service
-/privacy Privacy policy
-/about About page
-/profile User profile
/api/auth/[...nextauth] Auth API routes
/api/referrals/* Referral API routes
```
@@ -100,14 +93,16 @@ StellarHunts/
Application state is split across two concerns:
-**Zustand (useGameStore)** — Persisted to localStorage for game-specific state:
+**Zustand** — Persisted to localStorage for game-specific state:
- User authentication status
- Current puzzle difficulty and progress
- Completed puzzles and difficulty levels
- Score tracking and NFT collection
+> `@reduxjs/toolkit` is listed as a dependency but is not currently wired up.
+> See [ADR-0002](adr/0002-zustand-alongside-redux-toolkit.md) for the rationale.
+
**TanStack Query** — Server state caching for:
-- Leaderboard data
- Puzzle content
- Referral statistics
- API-driven data with automatic invalidation
@@ -144,78 +139,66 @@ Application state is split across two concerns:
NestJS modules are organized by domain concern. Each module encapsulates its controller, service, entities, DTOs, and tests.
+The table below lists modules that are **actually registered** in `app.module.ts`. Modules present in `backend/src/` but not yet wired into AppModule are noted separately.
+
**Core / Infrastructure**
- `ConfigModule` — Environment configuration loading
- `TypeOrmModule` — Database connection and entity registration
-- `RateLimiterModule` — Request throttling with Redis-backed guards
- `AnalyticsModule` — Event tracking and usage metrics
-- `MaintenanceModeModule` — Service availability control
-- `MigrationModule` — Database migration orchestration
-- `AuditLogModule` — System audit logging
-- `TokenVerificationModule` — Token validation utilities
**Authentication & Users**
- `AuthModule` — JWT authentication, registration, login, wallet linking
-- `UserModule` — User CRUD and profile management
-- `UserSettingsModule` — User preferences
-- `UserActivityLogModule` — Audit trail for user actions
- `UserReportCardModule` — Per-user performance summaries
-- `UserInventoryModule` — NFT and badge ownership tracking
+- `UserActivityLogModule` — Audit trail for user actions
- `UserRankingModule` — Ranking calculations
-- `WalletModule` — Stellar wallet address management
+- `UserInventoryModule` — NFT and badge ownership tracking
**Puzzle & Content**
- `PuzzleModule` — Core puzzle CRUD and game logic
-- `PuzzleCategoryModule` — Puzzle categorization and grouping
- `PuzzleSubmissionModule` — Answer submission handling
- `PuzzleDependencyModule` — Prerequisite puzzle management
-- `PuzzleDraftModule` — Puzzle authoring workflow
-- `PuzzleVersioningModule` — Puzzle revision history
-- `PuzzleReviewModule` — Admin review workflow
- `PuzzleTranslationModule` — Multi-language support
-- `PuzzleCommentModule` — User discussion on puzzles
-- `PuzzleAccessLogModule` — Access tracking
-- `PuzzleTestCaseModule` — Test case management
-- `PuzzleForkModule` — Puzzle forking and remixing
- `ContentModule` — Educational articles and resources
- `ContentRatingModule` — User content ratings
-- `QuizModule` — Quiz-style challenges
**Gamification & Rewards**
- `RewardsModule` — Reward distribution and claim tracking
- `RewardShopModule` — Reward marketplace
- `NFTClaimModule` — On-chain Soroban NFT minting orchestration (StellarHandlerService)
-- `NFTMarketplaceStubModule` — Mock marketplace for testing
-- `AchievementsModule` — Achievement definitions and tracking
-- `BadgeModule` — Badge management
-- `MilestoneModule` — Milestone progression
-- `StreakModule` — Daily/consecutive activity tracking
-- `DailyRewardModule` — Login bonus system
- `TimeTrialModule` — Timed challenge mode
-- `PromoCodeModule` — Promotional code redemption
**Social & Multiplayer**
- `MultiplayerQueueModule` — Socket.IO matchmaking
-- `ReferralModule` — Referral program tracking
- `ReportsModule` — User reporting and moderation
-- `FeedbackModule` — User feedback collection
- `InAppNotificationsModule` — Notification delivery
- `ActivityModule` — Social activity feed
- `UserReactionModule` — Emoji/like reactions
-- `GeostatsModule` — Geographic player statistics
-**Progress & Analytics**
+**Progress & Integrations**
- `ProgressModule` — User progression tracking
-- `SessionModule` — Session lifecycle
-- `HintModule` — Puzzle hint management
- `ApiKeyModule` — API key management for integrations
-- `AdminModule` — Admin dashboard backend
+
+**Modules present in `backend/src/` but not yet registered in AppModule**
+
+The following directories exist and may be under active development:
+`AuditLogModule`, `BadgeModule`, `DailyRewardModule`, `FeedbackModule`,
+`GeostatsModule`, `HintModule`, `MaintenanceModeModule`, `MigrationModule`,
+`MilestoneModule`, `NFTMarketplaceStubModule`, `PromoCodeModule`,
+`PuzzleAccessLogModule`, `PuzzleCategoryModule`, `PuzzleCommentModule`,
+`PuzzleDraftModule`, `PuzzleForkModule`, `PuzzleReviewModule`,
+`PuzzleTestCaseModule`, `PuzzleVersioningModule`, `QuizModule`,
+`ReferralModule`, `SessionModule`, `StreakModule`,
+`TokenVerificationModule`, `UserModule`, `UserSettingsModule`,
+`UserTokenHistoryModule`, `WalletModule`, `AdminModule`.
+
+> There is **no** `LeaderboardModule` in the source tree. The leaderboard
+> endpoint lives inside `StreakModule` (`GET /streaks/leaderboard`).
### Database
Primary database: **PostgreSQL** managed through TypeORM with code-first entity definitions.
-Key entities: `User`, `Puzzle`, `Category`, `Reward`, `RewardClaim`, `TimeTrial`, `Session`, `Progress`, `Hint`, `Achievement`, `Badge`, `Streak`, `Referral`, `Notification`, `UserActivityLog`.
+Key entities: `User`, `Puzzle`, `Category`, `Reward`, `RewardClaim`, `TimeTrial`, `Progress`.
Configuration via environment variables:
@@ -224,17 +207,19 @@ DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_NAME=stellarshunts
DATABASE_SYNC=true # Auto-sync entities (dev only)
-DATABASE_LOAD=true # Auto-load entities
+DATABASE_LOAD=true # Auto-load entities
```
### API Design
- **RESTful** endpoints organized by resource (no global prefix — e.g., `/puzzle-categories`, `/rewards`, `/auth`)
-- **Authentication** via JWT tokens (Bearer header) or session cookies
+- **Authentication** via JWT tokens (Bearer header)
- **Swagger** documentation at `http://localhost:3001/api/docs`
- **Rate limiting** applied to auth and claim endpoints
- **WebSocket** connections for multiplayer queue via Socket.IO
+See [`docs/api.md`](api.md) for the full endpoint reference table.
+
## Onchain Architecture
### Stack