Scope: This document defines all functional and non-functional requirements for Phase 2 of the Sawtak platform. Phase 1 (v3-Req) established the core complaint submission system on Hedera HCS. Phase 2 delivers: (1) full migration to a custom Cosmos SDK PoA chain, (2) a basic Capacitor-wrapped Next.js mobile app with custom splash/home screens, (3) a simple video evidence player, (4) IPFS-first evidence strategy via Pinata, (5) the existing 184-test suite, (6) complete blockchain network (testnet) & full-stack Compose orchestration, and (7) updated frontend documentation with architecture diagrams.
| Area | Phase 1 (v3) | Phase 2 (v4) |
|---|---|---|
| Blockchain | Hedera HCS (managed public ledger) | Custom Cosmos SDK PoA AppChain |
| Consensus | Hedera managed PoS | Proof of Authority — vetted validators only |
| Blockchain SDK | @hashgraph/sdk / @hiero-ledger/sdk |
@cosmjs/stargate, @cosmjs/proto-signing |
| On-chain module | HCS Topics 1 & 2 | Custom sawtak module (MsgCreateAnonymousComplaint, MsgUpdateComplaintStatus, etc.) |
| Indexer | HederaIndexerService polling HCS Mirror Node |
CosmosIndexerService subscribing via Tendermint WebSocket |
| DB hash column | hcs_hash |
cosmos_hash (migrated) |
| IPFS | Web3.Storage (deprecated free tier) | Pinata (JWT-authenticated, production-ready) |
| Mobile | Web only | Capacitor wrapping the existing Next.js app (custom mobile home & splash screen) |
| Evidence playback | Images only inline | Simple video player + direct file access for PDFs |
| Frontend docs | Inline README | Separate docs/ with architecture diagrams |
| Test suite | a few unit tests | Route-layer + integration + chain-level (184 total tests) |
| Compose stack | Partial (no blockchain nodes) | Full stack: 3 Cosmos nodes + Postgres + Redis + Backend + Proxy + Frontend + Haweya + Monitoring |
The network trust-zone model from Phase 1 is preserved and extended with the custom Cosmos chain layer, and notably includes the Privacy Proxy Layer established in Phase 2 to ensure absolute anonymity before requests hit the backend:
PUBLIC INTERNET
┌────────────────────────────────────────────────────────────────────────────┐
│ Browser (Web) │ Mobile (Capacitor/iOS/Android) │ Admin │ Haweya │
└──────────────────────────────────┬─────────────────────────────────────────┘
▼
┌────────────────────────────────────────────────────────────────────────────┐
│ DMZ — PRIVACY PROXY :4000 │
│ Anonymous session mgmt · Header stripping · Rate limiting (Redis) │
└──────────────────────────────────┬─────────────────────────────────────────┘
│ internal network (proxy-auth required)
▼
┌────────────────────────────────────────────────────────────────────────────┐
│ BACKEND :8000 (Bun / Elysia.js) │
│ Auth · Submission services · AI validation · Cosmos + Pinata clients │
│ CosmosIndexerService (Tendermint WebSocket → Postgres) │
└───────────┬────────────────────────────────────┬───────────────────────────┘
│ gRPC / REST │ HTTPS
▼ ▼
┌──────────────────────────┐ ┌─────────────────────────────────────────┐
│ Sawtak PoA Testnet │ │ External Integrations │
│ Node-1 :26657 :9090 │ │ Pinata IPFS (anon evidence) │
│ Node-2 :26658 :9091 │ │ Cloudflare R2 (identified evidence) │
│ Node-3 :26659 :9092 │ │ Gemini AI (spam filter) │
│ Consensus: PoA │ │ Haweya OAuth (national ID) │
│ Google OAuth │
└──────────────────────────┘ └─────────────────────────────────────────┘
Frontend:
├── Web: Next.js 16, TypeScript, React Query (TanStack), Zustand
├── Mobile: Capacitor 6 wrapping the Next.js app (custom home/splash)
└── Style: TailwindCSS, shadcn/ui
Backend:
├── Runtime: Bun
├── Framework: Elysia.js
├── Language: TypeScript
└── Jobs: Bull Queue (Redis-backed)
Database:
├── Primary: PostgreSQL 16
├── Cache: Redis 7 (sessions, rate limiting, job queue)
└── Search: PostgreSQL Full-Text Search (tsvector)
Blockchain:
├── Network: Sawtak custom AppChain (Cosmos SDK)
├── Consensus: Proof of Authority (custom `poa` module)
├── Client: @cosmjs/stargate, @cosmjs/proto-signing
└── Indexer: Tendermint WebSocket (CosmosIndexerService)
File Storage:
├── Anonymous evidence: IPFS via Pinata (JWT-authenticated)
└── Identified evidence: Cloudflare R2 (private, no chain)
Authentication:
├── JWT (access 1h + refresh 7d, Redis-backed)
├── Google OAuth 2.0
├── Haweya OAuth (national ID provider)
└── bcrypt (cost: 12)
Infrastructure:
├── Proxy: Elysia.js privacy proxy (separate service)
├── Monitoring: Prometheus + Grafana + Alertmanager
└── Compose: Podman / Docker Compose (full stack + testnet)
sawtak/
├── backend/
│ ├── src/
│ │ ├── routes/
│ │ │ ├── auth.routes.ts
│ │ │ ├── complaint.routes.ts # anonymous + identified
│ │ │ ├── admin.routes.ts
│ │ │ ├── feed.routes.ts
│ │ │ ├── upload-evidence.routes.ts # Pinata IPFS uploads
│ │ │ ├── vote.routes.ts
│ │ │ ├── tracking.routes.ts
│ │ │ └── indexer.routes.ts # /api/indexer/status|start|reindex
│ │ ├── services/
│ │ │ ├── cosmos/
│ │ │ │ ├── sawtak-cosmos.service.ts # active blockchain service
│ │ │ │ └── cosmos-indexer.service.ts
│ │ │ ├── ipfs/
│ │ │ │ └── pinata.service.ts
│ │ │ ├── anonymous-submission.service.ts
│ │ │ ├── identified-submission.service.ts
│ │ │ ├── complaint-status.service.ts
│ │ │ └── ai-validation.service.ts
│ │ ├── middleware/
│ │ │ ├── proxy-auth.middleware.ts
│ │ │ ├── auth.middleware.ts
│ │ │ └── rate-limit.middleware.ts
│ │ ├── config/
│ │ │ └── cosmos.config.ts # COSMOS_* env validation
│ │ └── server.ts
│ ├── prisma/
│ │ └── schema.prisma # cosmos_hash, updated schema
│ └── tests/
│ ├── Anonymous-submission.test.ts
│ ├── cosmos-indexer.test.ts
│ ├── upload-evidence.test.ts
│ ├── admin.routes.test.ts
│ └── integration/
│ └── proxy-pipeline.test.ts
│
├── privacy-proxy/
│ ├── src/
│ │ ├── policies/
│ │ ├── middleware/
│ │ └── services/
│ └── tests/
│
├── Front-end/
│ ├── app/ # Next.js app router
│ ├── components/
│ │ ├── evidence/
│ │ │ ├── VideoPlayer.tsx # NEW — simple video playback
│ │ │ └── ImageGallery.tsx # Inline images
│ │ └── ui/
│ ├── docs/ # NEW — docs site source
│ │ ├── architecture/
│ │ │ ├── overview.mdx
│ │ │ ├── trust-zones.mdx
│ │ │ └── blockchain.mdx
│ └── capacitor.config.ts # NEW — Capacitor config
│
├── network/
│ └── Sawtak/ # Custom Cosmos SDK chain
│ ├── Dockerfile
│ ├── entrypoint.sh
│ └── tests/ # chain-level consensus tests
│
├── docker/
│ ├── docker-compose.prod-testnet.local.yml # Full stack + 3 Cosmos nodes
│ └── docker-compose.monitoring.yml
│
├── monitoring/
│ ├── prometheus.yml
│ ├── grafana/
│ └── alertmanager.yml
Epic: Backend Migration — Cosmos SDK Integration (SAW-148)
User Story: As a backend developer, I want to replace HederaService with SawtakCosmosService so that complaint submissions work with the Cosmos SDK blockchain.
SawtakCosmosService that satisfies the existing IBlockchainService interface — no callers need to know the underlying chain changed.