Sawtak — Phase 2 Product Requirements Document

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.


1. SYSTEM OVERVIEW

1.1 What Changes in Phase 2

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

1.2 Core Architecture (Phase 2)

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                            │
└──────────────────────────┘       └─────────────────────────────────────────┘

1.3 Technology Stack (Phase 2 Full)

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)

1.4 Updated Repository Structure

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

2. BLOCKCHAIN MIGRATION — HEDERA → COSMOS SDK

Epic: Backend Migration — Cosmos SDK Integration (SAW-148)

2.1 SawtakCosmosService — Blockchain Service Replacement

User Story: As a backend developer, I want to replace HederaService with SawtakCosmosService so that complaint submissions work with the Cosmos SDK blockchain.

Functional Requirements