Files
ProcureLinc-Mobile-App/docs/ARCHITECTURE_AUDIT.md
T
Abin ToppoandClaude Fable 5 1d7646d938
CI / quality (push) Waiting to run
feat: architecture refactor — core layer, spreadsheet engine, envelope bidding, tests
- Add src/core/ (config, errors, events, http, observability, storage, query)
- Add src/Spreadsheet/ engine with native POI backend and Excel bridge
- Add Envelope C spreadsheet flow and tender bidding envelope modules
- Consolidate components into src/components/, remove legacy Component/ and Hooks/
- Add registration state/validation, auth session guard, profile edit modules
- Add unit tests across axios, redux, bidding, registration, encryption, core
- Add CI workflow, docs (architecture, audit, DSC API spec), keystore example
- Remove tracked keystore binary; ignore scripts.zip

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 10:20:22 +05:30

364 lines
37 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ProcureLinc — Principal-Level Architectural Audit & Redesign
**Subject:** ProcureLinc React Native mobile app (e-procurement: auctions + tenders + eKYC + sealed-bid bidding)
**Stack:** React Native 0.79.0, React 19, Redux Toolkit + redux-persist, React Query, STOMP/SockJS, Axios, Formik/Yup, Hermes + New Architecture (Fabric/TurboModules) enabled.
**Scope:** Full-project architectural review — no code was modified. Every claim below is anchored to `file:line` evidence.
**Verdict:** The app *works*, but it is at **prototype maturity carrying production-grade risk**. It is not currently safe to ship to a real user base handling money and Aadhaar/PAN PII, and it is not structured to scale to a large team or user count without significant rework. This document is the redesign blueprint.
---
## 1. Executive Summary
ProcureLinc is a functional e-procurement client with genuinely good *ideas* in places (a clean `AppText` typography primitive, a correct paginated-list template in `LiveTender.tsx`, a working dark-mode theme system, STOMP-based live bidding). But the codebase is in a state common to fast-moving startups: **multiple half-finished architectural attempts layered on top of each other, none fully adopted, with security and correctness debt that compounds as it grows.**
The five findings that matter most, in priority order:
| # | Finding | Severity | Where |
|---|---------|----------|-------|
| 1 | **A binary signing keystore (`appKeyStore`) is committed to git**, and **release builds are signed with the public debug keystore** (`storePassword 'android'`). Anyone with repo access can forge signed ProcureLinc APKs. | 🔴 Critical | `android/app/appKeyStore` (git-tracked), `android/app/build.gradle:103` |
| 2 | **A global Axios interceptor logs every request/response — full URLs, JWT headers, login passwords, Aadhaar/PAN/OTP, and sealed-bid amounts — to logcat, ungated by `__DEV__`, in release builds.** | 🔴 Critical | `src/Axios/Axios.ts:47-99` |
| 3 | **The data layer is four competing API stacks across three different backend hosts**, and the dominant one (`apiCall`) *swallows every error and returns `{success:false}` cast to the success type* — defeating TypeScript, React Query error/retry/cache, and forcing brittle status-code checks everywhere. | 🔴 Critical (architecture) | `Axios.ts:124-141`, `api/client.ts`, `apis.ts`, `Constant/Variable.tsx` |
| 4 | **App-wide cleartext HTTP is permitted in release, with no SSL pinning**; auth JWT + full login response (incl. PII) is persisted **unencrypted** in AsyncStorage; device binding is client-spoofable and DSC "binding" is UI-only. | 🔴 Critical (security) | `network_security_config.xml`, `Redux/Store.js:20-24`, `DeviceBinding.tsx`, `DscBinding.tsx` |
| 5 | **God-components with mixed concerns and massive duplication** — `TenderBidding.tsx` is 1205 lines (crypto + REST + upload + validation + UI); `StartBidding`/`StartReverseBidding` and their card components are ~90% copy-paste forks. | 🟠 High (maintainability) | `TenderBidding.tsx`, `Bidding/*` |
**Bottom line:** before any new feature work, the project needs (a) a security remediation sprint (secrets, transport, logging, storage), and (b) a data-layer consolidation onto a single typed client + properly-configured React Query. Everything else — modularization, design-system unification, testing, CI/CD — follows from those two.
**On the "AI-powered operating system" vision:** I am challenging the premise directly (see §16). The current codebase contains **zero AI, agent, RAG, voice, or knowledge-graph subsystems**. That is not a criticism — an e-procurement app doesn't need them today. But the audit cannot review AI architecture that doesn't exist; instead §16 lays out what an AI-native evolution would *actually* require and why the current foundation must be fixed first.
---
## 2. Current Architecture Overview
**Shape:** A single-module React Native app. No feature-module boundaries; organized by loose type-folders (`Screens/`, `Component/` **and** `components/`, `Hooks/`, `Services/`, `Redux/`, `socket/`, `theme/` **and** `Constant/Color`, `Styles/`).
**Runtime composition** (`App.tsx:19-41`): `NavigationContainer → QueryClientProvider → GestureHandlerRootView → AlertNotificationRoot → Redux Provider → PersistGate → Routes`. `LogBox.ignoreAllLogs(true)` is set at module load (`App.tsx:15`).
**Navigation** (`Navigation/MainStack.jsx`): one flat native-stack with 23 sibling screens; `Home` hosts a custom animated `BottomTab` (4 tabs). No logged-in/out navigator split — auth gating is a one-shot imperative redirect inside `Splash.tsx`.
**State:** Redux Toolkit, 6 slices (`token`, `auction`, `selectPurchaseAuction`, `selectPurchaseTender`, `tender`, `theme`), only `token` + `theme` persisted. `serializableCheck:false` globally (`Store.js:30`).
**Data:** Four coexisting mechanisms — `apiCall` wrapper (dominant), a second `apiClient` axios instance, raw `axios` calls in `apis.ts`, and raw `axios` in screens — hitting **three** hardcoded hosts: `eproc.godemo.in` (REST), `procurelinc.in` (sockets), `procurelinc.com` (dead `client.ts`).
**Real-time:** STOMP-over-SockJS (`socket/Socket.tsx`) for receiving bid updates; bids are *placed* over REST, not STOMP.
**Crypto:** JS-side AES (ECB mode, SHA-256 key truncated to 128 bits) + RSA (jsencrypt) for sealed-bid envelopes (`Services/Encryption/encrypt.tsx`).
**Native:** No custom Java/Kotlin — stock RN host. Hermes on, New Architecture on. No env abstraction, no tests, no CI/CD.
Total: ~26k LOC of TS/JS across 138 source files.
---
## 3. Architectural Strengths (worth preserving)
An honest audit names what's good so it isn't lost in a refactor:
- **`components/AppText.tsx`** — a proper typographic primitive: variant + theme color + Android-safe font-family merging. This is the seed of a real design system. Keep and expand.
- **`useTheme` + `ThemeSlice`** — a working light/dark/system theme with a user switcher (`CustomDrawer.tsx`). Solid foundation; the problem is only that half the app bypasses it.
- **`LiveTender.tsx`** — the *correct* FlatList pattern: memoized `renderItem`/`renderFooter`, `keyExtractor`, infinite-scroll pagination via React Query `fetchNextPage`/`hasNextPage`, and tuned perf props (`initialNumToRender`, `windowSize`, `removeClippedSubviews`). **This should be the template every list screen follows.**
- **`socket/Socket.tsx`** — a reasonable `useReducer`-based STOMP abstraction with a pending-subscription queue and reconnect/heartbeat config. The core is sound; the consumers misuse it.
- **Modern baseline** — RN 0.79, Hermes, New Architecture, React Query, Redux Toolkit, Formik/Yup are all present. The *ingredients* of a good architecture exist; they are simply half-wired.
- **`android:allowBackup="false"`** is correctly set — limits `adb backup` extraction of the (unfortunately plaintext) token.
---
## 4. Architectural Weaknesses
Grouped by subsystem. Severity: 🔴 Critical, 🟠 High, 🟡 Medium.
### 4.1 Data / API layer
- 🔴 **Four parallel API stacks, three hosts, two auth-header names.** `apiCall` sends `Authentication: Bearer` (note: not `Authorization`) reading `token.token` (`Axios.ts:118`); `apiClient` sends `Authorization` reading `token.token.token` (`client.ts:17`); screens send raw axios with `Authentication: Bearer ${Token.token}` (`BiddingCard.tsx:82`). There is no single place to add retry, refresh, tracing, or error handling.
- 🔴 **`apiCall` swallows errors and casts `{success:false}` to `R`** (`Axios.ts:124-141`). This is the single most damaging decision in the codebase — it defeats the type system, makes React Query believe every call succeeded (`isError` never fires, retries never happen, bad responses get cached as valid), and forces defensive `res?.statusCode===200` checks in every consumer. `try/catch` blocks around `apiCall` (e.g. `apis.ts:76-82`) are dead code because it never throws.
- 🟠 **React Query is present but mis-configured.** `new QueryClient()` with no defaults (`App.tsx:20`) ⇒ `staleTime:0`, refetch-on-mount/focus ⇒ refetch storms. It's also instantiated *inside the component body*, so it's recreated on every re-render. Broken query keys: `useAucitonItemDetail` uses `queryKey:[]` (all instances collide), `useGetAuciton` uses static `['getAuction']` ignoring the id. Missing `enabled` guards fire `?tenderId=undefined` calls. No `invalidateQueries` anywhere — lists go stale after writes.
- 🟡 **Dead/broken config.** `Constant/AllUrls.tsx` builds ~30 URLs from `${Variable.Main_Base}` — but `Main_Base` doesn't exist, so every URL is `undefined/...`. This whole file is leftover from a different (e-commerce) app. The generic hooks (`useFetch/useUpdate/useDelete`) are imported nowhere.
### 4.2 State management
- 🟠 **The token slice lies about its type.** Declared `{token:''}` (`Token.tsx:3`) but at runtime holds a full object (`Login.tsx:212`). No `RootState`/`AppDispatch` types exist; every `useSelector` is `(state:any)`. Zero compile-time safety on auth state.
- 🟡 **Two inconsistent logout paths** — `removeToken()` (sets `''`) vs `setToken({})` (leaves a truthy empty object) vs direct `AsyncStorage.removeItem('persist:root')`. These can desync in-memory state from disk and confuse the splash auth check.
- 🟡 **`serializableCheck:false` globally** hides genuine non-serializable leaks, and the slices store whole API response objects.
### 4.3 UI / screens
- 🟠 **God-components:** `TenderBidding.tsx` (1205), `ProfileTab.tsx` (1031), `Login.tsx` (792), `Ragistration.tsx` (725, ~25 `useState`), `Emd.tsx` (608). Networking + crypto + validation + sub-components + rendering all in one file.
- 🟠 **Copy-paste forks:** `StartBidding`≈`StartReverseBidding` (~90%), `BiddingCard`≈`ReverseBiddingCard` (identical StyleSheets) — diverging subtly (one card is `memo`'d, the other isn't; wrong color import in reverse). Every fix must be made twice, and they *will* drift.
- 🟠 **Split-brain design system:** `src/Component/` (old) vs `src/components/` (new); two `MyInput`, two `PrimaryButton`, three side-drawers (one dead), two color palettes with **two different brand oranges** (`Constant/Color:#F97316` vs `theme/colors:#e39546ff`).
### 4.4 Navigation
- 🟠 **No route type safety** (`createNavigationContainerRef<any>()`, `useNavigation<any>()`, `useRoute<any>()` everywhere), **no route guards** (every screen reachable), **no deep linking** (`NavigationContainer` has no `linking`).
### 4.5 Real-time
- 🟠 **`subscribe()` called in the render body** (`StartBidding.tsx:137`, `StartReverseBidding.tsx:171`), re-registering the STOMP callback every render; **no `disconnect()`/`unsubscribe()` on unmount** ⇒ leaked connections that keep reconnecting (5s) and draining battery/data. Bids split across REST *and* STOMP for the same feature.
---
## 5. Design Smells
- **Misspelled, leaked-through names as public API:** folder `Ragistration/`, components `Serach/SeacrhInput`, props `isVarify`/`maxlength`, constant `FORARD_AUCTION_ITEM`, string `'Failed load cities'`. Names are the cheapest documentation; these signal no review gate.
- **Duplicate constants pointing at the same endpoint** (`SELECTED_AUCTION_ITEM` == `EMD_CHECK`).
- **`useState` used as a value holder**: `const tenderId = useState(route.params?.tenderId)` captures the `[state,setState]` tuple and never uses it (`TenderBidding.tsx:71`).
- **Sub-components declared inside a parent's render body** (`InfoRow`, `EnvelopeButton`, `renderEnvelopeItem` in `TenderBidding.tsx:113-309`) — recreated every render, defeating memoization and remounting subtrees.
- **A test/debug screen wired into production** (`TestScreen/DecryptionScreen.tsx`) with a hardcoded real AES key copyable to clipboard.
- **`LogBox.ignoreAllLogs(true)`** — silencing all warnings is hiding the smoke detector.
## 6. Anti-Patterns
- **Error-swallowing service layer** (`apiCall` returning `{success:false} as R`) — the "exceptions considered harmful, return sentinels typed as success" anti-pattern.
- **Effect logic in render** (`subscribe()` outside `useEffect`).
- **Fat controller / god object** (the 1205-line screen).
- **Shotgun surgery by construction** (forward/reverse duplication guarantees every change touches ≥2 files).
- **Config-by-comment** (base URLs selected by commenting/uncommenting lines in `ApiUrls.tsx`).
- **Boolean-flip refetch** (`setAllBoolean(reload:!prev.reload)` to trigger a parent `useEffect` instead of query invalidation).
- **Primitive obsession in state** (~25 discrete boolean `useState`s in `Ragistration.tsx` instead of a reducer/state machine).
## 7. Bottlenecks
- **Refetch storms** from `staleTime:0` + refetch-on-focus on dashboards/lists.
- **Whole-list re-renders** on every live bid tick (inline `renderItem`, unmemoized reverse card) — worsens as bidder lists grow.
- **Per-second `setState` timers** (`CountdownTimer` *and* the parallel `AuctionTimer`) re-rendering timer subtrees every second; two implementations doing it.
- **Nested non-virtualized `FlatList scrollEnabled={false}` inside `ScrollView`** (`TenderBidding.tsx:1056,1099`) renders all rows eagerly.
- **Synchronous blocking API call on the Splash screen** (registration-state fetch gates app entry for tender users, `Splash.tsx:45-107`).
- **Startup polyfills** (`Buffer`, `TextEncoder`) set synchronously; `QueryClient` recreated per render.
## 8. Tight Coupling
- **Transport layer knows about UI and navigation:** `apiCall` dispatches `setToken({})` on 401 (`Axios.ts:129`); `Socket.placeBid` calls `showSuccessAlert`/`showErrorAlert` from inside the service (`Socket.tsx:200-207`). Data/transport should emit events, not drive UI.
- **Screens coupled directly to the crypto implementation** (`TenderBidding` calls `encrypt`/`encryptWithRSA` inline with hardcoded ECB assumptions).
- **Redux token shape coupled across ≥3 readers** with different nesting (`token.token` vs `token.token.token`) — change one, break the others silently.
- **Components coupled to two different color modules**, so a rebrand touches both.
## 9. Hidden Dependencies
- **`jsencrypt` is in `devDependencies`** (`package.json:91`) but imported by runtime code (`encrypt.tsx`). A production install with `--production` (or a clean CI install) **breaks the sealed-bid crypto path**. This is a latent production outage.
- **`global.Buffer` / `global.TextEncoder`** monkey-patched at startup (`index.js:9-10`, `App.tsx:22`) — any code depending on them has an invisible ordering dependency on app bootstrap.
- **Socket token captured at hook-init** (`StartBidding.tsx:55`) — if the token refreshes mid-session, the socket keeps the stale one with no re-read.
- **`text-encoding`, `react-native-get-random-values`** are crypto polyfills whose absence would silently weaken/break encryption.
## 10. Single Points of Failure
- **The one signing key** — committed to git, so its secrecy is already lost; simultaneously the *only* thing standing between the app and forged updates.
- **`apiCall` / the default axios instance** — every feature funnels through it; its error-swallowing behavior means one backend contract change produces silent `undefined`s app-wide rather than a visible failure.
- **The single flat navigator + imperative Splash gate** — if Splash's registration-state call fails, the recovery path *wipes persisted state and forces re-login* (`Splash.tsx:103`); a backend hiccup logs everyone out.
- **`Constant/Variable.tsx` socket host** — hardcoded to a single origin; no failover.
---
## 11. Security Risks
> Full evidence in the security appendix; the highest-priority items:
| Severity | Risk | Evidence |
|----------|------|----------|
| 🔴 Critical | **Signing keystore committed to git** (`.gitignore` `*.keystore` missed it — no extension) + **release signed with debug key** (`storePassword 'android'`). Forgeable/ tamperable APKs. | `android/app/appKeyStore` (git-tracked, verified), `build.gradle:88-104` |
| 🔴 Critical | **Global request/response logger dumps JWTs, passwords, Aadhaar/PAN/OTP, and sealed-bid values to logcat in release** (no `__DEV__` gate). 378 `console.*` calls across 34 files, none stripped (no `transform-remove-console`). | `Axios/Axios.ts:47-99`, `Login.tsx:213-216`, `encrypt.tsx:30-155`, `TenderBidding.tsx:474,561` |
| 🔴 Critical | **Committed SonarQube auth token.** | `sonar-project.properties:13` (verified git-tracked) |
| 🟠 High | **App-wide cleartext HTTP permitted in release + no SSL pinning.** A hardcoded `http://` base with attached Bearer token also lives in `client.ts`. | `network_security_config.xml`, `AndroidManifest.xml:19`, `api/client.ts:6` |
| 🟠 High | **JWT + full login response persisted unencrypted in AsyncStorage.** Readable on rooted/backed-up devices. No Keychain/Keystore/EncryptedStorage. | `Redux/Store.js:20-24`, `Login.tsx:212` |
| 🟠 High | **Device binding is client-spoofable** (`getUniqueId()`/`getIpAddress()`, no Play Integrity attestation) and **silently falls back to fabricated GPS coords** on permission denial. This is the *fraud control* for bidding. | `DeviceBinding.tsx:47-86` |
| 🟠 High | **Weak sealed-bid crypto:** AES-**ECB** mode, SHA-256 key **truncated to 128 bits**, and the symmetric key is **echoed back to the server in plaintext alongside the ciphertext** (`randomKeyGenerated`) — adding no real confidentiality. | `encrypt.tsx:9-71`, `TenderBidding.tsx:546` |
| 🟡 Medium | **WebView KYC success detected by page-title string match** (spoofable); loads **raw server-supplied HTML** with JS enabled; logs Aadhaar callback URLs. | `KycWebViewScreen.tsx:85-143` |
| 🟡 Medium | **No root/tamper/Frida detection, no `FLAG_SECURE`** (sealed bids & Aadhaar OTP are screenshot-/screen-recordable and appear in the app-switcher thumbnail), **no ProGuard/R8** minification. | grep-confirmed absent; `build.gradle:60,104` |
| 🟡 Medium | **`loginWithoutDecryption` endpoints** + **`ResetPassword` sends new password as a URL query param** (`?newPassword=...`) — lands in server/proxy logs. | `apis.ts:29-46`, `ResetPassword.tsx:220` |
| 🟡 Medium | **Repo-committed bulk-user script with default password `P@ssword123`** targeting the live backend. | `scripts/create-users.js:10` |
## 12. Privacy Risks
- **Regulated PII logged in the clear.** Full Aadhaar number, PAN, GST, DOB are `console.log`-ed (`Ragistration.tsx:363`) and the **Aadhaar OTP is displayed in an `Alert` dialog** (`Ragistration.tsx:422`). Under UIDAI/Aadhaar Act and India's DPDP Act, logging full Aadhaar is a compliance exposure, not just a bug.
- **PII travels over a transport that permits cleartext** (§11) and through the global logger (§11) on every request.
- **No data-minimization or masking layer** anywhere — the app has no concept of "sensitive field."
- **Unjustified permissions:** `READ_MEDIA_AUDIO` (no audio feature) and always-on FINE+COARSE location whose only use is a device-binding geo-tag that fabricates coordinates anyway.
- **Cached profile PII** (from `getcontractorfulldetails`) may persist unencrypted via React Query + any persisted slice.
## 13. Scalability Risks
- **Team scalability** is the nearer-term problem than user scalability: no module boundaries, god-components, and duplicated screens mean two developers *cannot* work on bidding without colliding, and every change risks the forward/reverse drift.
- **No env/config abstraction** ⇒ dev/staging/prod require source edits and recompiles; you cannot run a QA build against staging without a code change. This blocks a release pipeline.
- **The error-swallowing data layer** means production incidents are invisible (no error surfaced, bad data cached as good) — you scale the user base but lose the ability to see failures.
- **No observability** (no crash reporting, no analytics, no structured logging) — at 10k+ users you'd be flying blind.
- **Backend coupling by string concatenation** (`${BASE_URL}path?query=`) with no client SDK/contract means backend changes break the app silently.
## 14. Performance & Battery Risks
- Refetch storms (§7), per-second timer re-renders (§7), whole-list re-renders on bid ticks (§7).
- **Leaked STOMP connections** (no unmount disconnect) keep reconnecting every 5s with 1s heartbeats — a measurable battery/data drain when a user leaves a bidding screen without killing the app.
- **No `getItemLayout`** on any list — every list pays layout measurement cost.
- **No release minification/shrinkResources** — larger APK, and no ABI splits/App Bundle so users download all architectures.
- **Duplicate heavy libs shipped:** `exceljs` **and** `xlsx`; `react-native-fs` **and** `react-native-blob-util`; two icon systems — dead weight in the bundle.
## 15. Maintainability Risks
- **~26k LOC, zero tests, no CI, lint/format enforced only by developer discipline** (no husky/lint-staged). There is no automated safety net for any refactor.
- **`.jsx`/`.js` files bypass TypeScript** entirely (`Store.js`, `MainStack.jsx`, `Routes.jsx`, `ImagePath.jsx`).
- **Duplication tax:** every bidding change is a 2-file (often 4-file) edit; the two color palettes and two component roots triple the surface for any visual change.
- **`any` everywhere** (navigation, selectors, socket callbacks, `apiCall` generics) — the compiler cannot help.
- **Dead code** (`AllUrls.tsx`, `DrawerStack`, `CustomDrawer`, generic hooks, `useLogin`, `userLogin`) increases cognitive load and hides which path is live.
---
## 16. AI System Evaluation
**Finding: there is no AI subsystem in this codebase.** No LLM/model integration, no agent/orchestration layer, no intent recognition, no task planning, no RAG/retrieval, no knowledge graph, no vector/embedding index, no voice pipeline, no local or cloud inference. Grep confirms none of the associated libraries or patterns are present.
I'm flagging this plainly because the brief framed the review around an "AI-powered operating system," and a Principal Engineer's job is to challenge the premise rather than invent findings. **An e-procurement bidding app does not need any of that to succeed, and bolting an "AI OS" onto the current foundation would be a strategic mistake** — you'd be building a second story on a cracked foundation (committed keys, cleartext transport, error-swallowing data layer).
If AI capabilities *are* a genuine roadmap goal, here is the honest prerequisite ordering and what each would require:
1. **Fix the foundation first** (§17–19). An AI feature that calls a cloud LLM over the current cleartext-permitted transport, logging prompts (which would contain tender/bid PII) through the global logger, is a data-leak generator.
2. **Realistic, high-value AI features for *this* domain** (in priority order):
- **Tender discovery & ranking** — semantic search over live tenders (embeddings + a vector index server-side; the app just calls a ranked-search endpoint). Low risk, high user value.
- **Document intelligence** — extract/summarize tender documents and envelope requirements (server-side LLM + RAG over the tender corpus; app renders results).
- **Bid-assist / anomaly detection** — flag unusual bid patterns or missing mandatory documents before submission (server-side).
- **Conversational support agent** — scoped, tool-using assistant answering "what tenders match my class?" via typed tool calls to existing endpoints.
3. **Architectural requirement:** all of the above should be **server-mediated** (model keys never on-device; PII never sent to a third-party model without consent + DPA). The mobile app's role is a thin, typed client to an AI *gateway* service — which is exactly the clean data-layer boundary §17 prescribes. **Build the clean API/domain layer, and AI features become just more endpoints behind it.**
**Verdict:** AI is a *future* concern gated on foundation work. Do not start it now.
---
## 17. Android System Evaluation
- **Build config is not release-ready.** No release keystore (debug-signed release), no minify/shrink/ProGuard, no ABI splits or App Bundle, hardcoded `versionCode 1`/`versionName 1.0` with no bump automation.
- **New Architecture + Hermes are on** (good, modern), and confirmed in native host code (`MainApplication.kt`). No custom native modules — everything is JS + autolinked libs, which keeps the native surface small but means device-binding/DSC/crypto get *no* native security guarantees (no Keystore-backed keys, no attestation).
- **Manifest:** app-wide cleartext, no orientation lock, single exported `MainActivity` with only MAIN/LAUNCHER (no deep links). `allowBackup=false` is correct.
- **Cross-platform:** iOS project exists and is set up (`ios/` with Podfile, AppDelegate.swift, PrivacyInfo.xcprivacy) — this is *not* Android-only, so all remediation must be applied per-platform (e.g. ATS on iOS mirrors the network-security-config fix).
- **Tooling gaps:** no tests, no CI/CD (`.github` contains only a Java-upgrade tooling artifact), stock ESLint/Prettier with no enforcement, committed Sonar token pointing at `localhost:9000` scanning `node_modules`.
## 18. Data Architecture Review
**Current:** string-concatenated endpoints off a hardcoded base, four transport styles, error-swallowing wrapper, mis-configured React Query, plaintext persisted auth, three backend hosts.
**Target (see §20):** one typed API client → a **domain/repository layer** (feature-scoped: `auth`, `tenders`, `auctions`, `bidding`, `kyc`, `profile`) → React Query hooks with correct keys/`staleTime`/invalidation → screens. Errors *thrown* and normalized once. Secrets in secure storage. Env-driven base URLs. This is the highest-leverage change in the whole document.
## 19. UX Architecture Review
- **Strong primitives, inconsistent application.** `AppText` + `useTheme` are good, but per-element inline styles + `color=` overrides dilute the tokens, and half the app uses the *other* palette/components.
- **No accessibility** (zero `accessibilityLabel`/`Role`/`Hint` — icon-only touchables and steppers are unusable with TalkBack), **no i18n** (hardcoded English + inline `₹`), **no responsive scaling** (fixed pixels), **no error boundary** (an unhandled render error crashes the app; warnings are globally silenced).
- **Inconsistent loading/empty/error states** — no shared wrapper; main lists lack `ListEmptyComponent`; most `catch` blocks only `console.log`, so React Query `isError` never reaches the user.
- **Forms are backwards:** the *simplest* screens (Login, SignUp) use Formik+Yup; the *hardest* (multi-step registration, envelope bidding) use hand-rolled `useState` state machines.
---
## 20. Recommended Target Architecture
**Principle:** feature-modular, layered, with dependencies pointing inward (Clean/Hexagonal). The mobile app is a *thin typed client* over well-defined domain boundaries.
```
app/
├─ core/ # cross-cutting, no feature knowledge
│ ├─ config/ env.ts # ← react-native-config; NO hardcoded hosts
│ ├─ http/ client.ts # ONE axios instance; throws on error
│ │ interceptors # auth (single token read), tracing, refresh
│ ├─ storage/ secure.ts # react-native-keychain for token; MMKV/AsyncStorage for prefs
│ ├─ crypto/ envelope.ts # AES-GCM (not ECB), key never echoed to server
│ ├─ realtime/ stomp.ts # subscribe in useEffect; disconnect on unmount
│ ├─ errors/ AppError.ts # normalized error type + ErrorBoundary
│ ├─ observability/ # crash reporting + structured, PII-scrubbed logging (DEV-gated)
│ └─ ui/ # ONE design system: AppText, Button, Input, Modal, List, tokens
├─ features/
│ ├─ auth/ { api, hooks, screens, state }
│ ├─ tenders/ { api, hooks, screens }
│ ├─ auctions/ { api, hooks, screens }
│ ├─ bidding/ { api, hooks, screens } # forward+reverse UNIFIED via config/props
│ ├─ kyc/ { api, hooks, screens }
│ └─ profile/ { api, hooks, screens }
├─ navigation/ RootNavigator # typed ParamList; Auth vs App stack split; deep linking
└─ App.tsx # QueryClient (module-const, tuned defaults) + ErrorBoundary + providers
```
**Key decisions & justifications:**
| Decision | Replaces | Principle |
|----------|----------|-----------|
| **One `apiClient` that throws typed `AppError`** | 4 stacks + error-swallowing | Single Responsibility; lets React Query do retry/error/cache; restores TypeScript safety |
| **`react-native-config` env files** (dev/staging/prod) | Hardcoded/commented hosts | 12-factor config; enables CI + QA builds without code edits |
| **`react-native-keychain` for token**, MMKV for prefs | Plaintext AsyncStorage | Defense in depth for auth/PII |
| **Typed navigation** (`RootStackParamList`) + **Auth/App stack split** | `any` + imperative Splash gate | Compile-time route safety; declarative auth gating; enables deep links |
| **Feature modules** (`api/hooks/screens` per domain) | Type-folders + god-components | High cohesion, low coupling; two devs work without collision |
| **Unify forward/reverse bidding** behind one config-driven screen | 90% duplicated forks | DRY; one place to fix bugs |
| **One design system** (`core/ui`), delete `src/Component` + one palette | Split-brain components/colors | Single source of visual truth |
| **AES-GCM, key wrapped by server RSA public key, never echoed back** | AES-ECB + echoed key | Actual confidentiality for sealed bids |
| **`transform-remove-console` + PII-scrubbing logger, DEV-gated** | 378 raw console calls | Privacy/compliance |
| **Server-mediated integrity** (Play Integrity / SafetyNet) for device binding | Client-spoofable IDs | Real anti-fraud |
| **ErrorBoundary + shared Loading/Empty/Error wrappers** | Per-screen ad-hoc + crashes | Consistent, resilient UX |
---
## 21. Migration Roadmap
Sequenced so each phase is shippable and de-risks the next. **Phase 0 is non-negotiable and blocks release.**
### Phase 0 — Security stop-the-bleed (days, do first)
1. **Rotate and remove signing keys from git.** Purge `appKeyStore` + `debug.keystore` from history (`git filter-repo`), rotate on Play Console if `appKeyStore` was the release key, move signing to `keystore.properties` (gitignored) / CI secrets. Create a real release signing config.
2. **Rotate the leaked Sonar token**, remove it from `sonar-project.properties`, fix `sonar.sources` to exclude `node_modules`.
3. **Gate/remove the global logger** behind `__DEV__` + add `babel-plugin-transform-remove-console` for release; scrub PII. Remove `TestScreen/DecryptionScreen` and the hardcoded key.
4. **Disable cleartext** (`usesCleartextTraffic=false`, restrictive network-security-config for release; iOS ATS) once backends are HTTPS. Fix/remove the `http://` `client.ts`.
5. **Move the token to Keychain**; stop persisting the full login response.
6. **Move `jsencrypt` to `dependencies`** (latent prod outage).
### Phase 1 — Data-layer consolidation (1–2 sprints)
7. Introduce `core/config` env + a **single throwing `apiClient`**; migrate feature by feature off `apiCall`/raw axios/`client.ts`. Delete `AllUrls.tsx` and the dead generic hooks.
8. Tune `QueryClient` defaults (`staleTime`, `retry`), fix query keys, add `enabled` guards and `invalidateQueries` on mutations. Move `QueryClient` to a module const.
9. Add typed `RootState`/`AppDispatch`; fix the token slice type; unify logout on `persistor.purge()` + Keychain clear + socket disconnect + query cache clear.
### Phase 2 — Structure & correctness (2–3 sprints)
10. Typed navigation + **Auth/App stack split**; move Splash gating into declarative navigator logic.
11. **Unify forward/reverse bidding** and the two card components into one config-driven module; move `subscribe` into `useEffect` with cleanup; disconnect on unmount.
12. Break up `TenderBidding`/`ProfileTab`/`Ragistration`: extract crypto, API, and validation into `core`/feature modules; convert registration to a reducer/state machine (or Formik).
13. Fix crypto (AES-GCM, no echoed key); add `FLAG_SECURE` to bidding/KYC; add Play Integrity to device binding.
### Phase 3 — Consistency & platform (2 sprints)
14. Collapse to **one design system + one palette**; delete `src/Component` and `Constant/Color` after migration. Add ErrorBoundary + shared state wrappers.
15. Add responsive scaling + accessibility labels; introduce i18n scaffolding (even if only `en` at first).
16. Remove duplicate deps (`xlsx` or `exceljs`; `fs` or `blob-util`; one icon lib); enable release minify/shrink + App Bundle + ABI splits; automate version bumps.
### Phase 4 — Quality gates (ongoing, start in Phase 1)
17. CI (GitHub Actions): install → typecheck → lint → test → build. Add husky + lint-staged.
18. Testing: unit tests for `core/http`, crypto, and domain hooks; component tests for the design system; a Detox smoke test for login→bid. Add crash reporting + analytics.
## 22. Prioritized Refactoring Plan (single ranked backlog)
| Rank | Item | Effort | Risk if skipped |
|------|------|--------|-----------------|
| 1 | Purge & rotate committed keys; real release signing | S | Critical — forgeable app |
| 2 | DEV-gate/strip logging; scrub PII | S | Critical — credential/PII leak |
| 3 | Rotate Sonar token | XS | Critical — exposed secret |
| 4 | HTTPS-only + kill cleartext + fix `client.ts` | S | High — MITM |
| 5 | Token → Keychain; stop persisting login response | S | High — PII at rest |
| 6 | `jsencrypt` → dependencies | XS | High — prod crypto outage |
| 7 | Single throwing `apiClient` + env config | M | Critical — invisible failures, no envs |
| 8 | React Query defaults + keys + invalidation | M | High — stale data, refetch storms |
| 9 | Typed navigation + Auth/App split | M | High — no auth enforcement |
| 10 | Unify bidding forward/reverse + socket cleanup | M | High — drift + leaks |
| 11 | Break up god-components | L | High — unmaintainable |
| 12 | Crypto (AES-GCM) + FLAG_SECURE + Play Integrity | M | High — weak sealed bids/fraud |
| 13 | One design system + one palette | L | Medium — visual drift |
| 14 | ErrorBoundary + shared states + a11y + i18n | M | Medium — UX/compliance |
| 15 | Dedup deps + release optimization + CI/CD + tests | M | Medium — quality/velocity |
## 23. Risk Assessment (residual, if nothing is done)
- **Compliance/legal:** logging full Aadhaar + storing PII in plaintext over cleartext-capable transport is a DPDP/UIDAI exposure — **the highest business risk**, not merely technical.
- **Financial fraud:** spoofable device binding + UI-only DSC + weak sealed-bid crypto undermine the integrity guarantees a tendering platform legally depends on.
- **Supply-chain/integrity:** committed + debug signing key means a malicious actor with repo access (or the public debug key) can distribute a trojaned "ProcureLinc."
- **Operational:** error-swallowing + no observability means production incidents are silent; you learn about outages from users, not dashboards.
- **Velocity:** duplication + god-components + no tests means feature velocity *decreases* as the app grows, and every bidding change risks a regression in the un-fixed twin.
## 24. Future Vision
Once the foundation is fixed, ProcureLinc is well-positioned:
- **Short term (foundation solid):** a secure, observable, typed, feature-modular app where two teams can build tenders and auctions in parallel; QA runs against staging without code edits; releases are automated and signed correctly.
- **Medium term:** offline-first for tender/auction browsing (React Query persistence + optimistic bid queueing over a resilient socket), full accessibility + i18n for a national user base, and a real design system that supports white-labeling for multiple procurement authorities.
- **Long term (only after the above):** the AI features in §16 — semantic tender discovery, document intelligence, bid-assist — added as *endpoints behind the clean data layer*, with all model access server-mediated and PII-governed. The "AI-powered OS" framing is better realized as **an intelligent procurement platform with a thin, secure mobile client**, not an AI runtime on the device.
**The single most important sentence in this report:** *Fix the security foundation and consolidate the data layer first; every other improvement — modularity, design system, testing, and eventually AI — compounds cleanly on top of those two, and none of them is safe or durable without them.*
---
### Appendix A — Evidence index (key files)
- Data/transport: `src/Axios/Axios.ts`, `src/api/client.ts`, `src/Services/ApiCall/{apis.ts,ApiUrls.tsx,Hooks.ts}`, `src/Hooks/use{Fetch,Create,Update,Delete}.ts`, `src/Constant/{Variable.tsx,AllUrls.tsx}`
- State: `src/Redux/Store.js`, `src/Redux/Slices/*`
- Realtime/crypto: `src/socket/Socket.tsx`, `src/Services/Encryption/encrypt.tsx`
- UI: `src/Screens/Tender/tenderBidding/TenderBidding.tsx`, `src/Screens/Bidding/{StartBidding,StartReverseBidding,Emd}.tsx` + `Component/{BiddingCard,ReverseBiddingCard}.tsx`, `src/Screens/Ragistration/Ragistration.tsx`, `src/Screens/Tender/LiveTender.tsx` (good template), `src/components/AppText.tsx`
- Navigation: `src/Navigation/{MainStack.jsx,Bottom.tsx,NavigationService.ts}`, `src/Screens/Splash/Splash.tsx`
- Android/build: `android/app/build.gradle`, `android/gradle.properties`, `android/app/src/main/{AndroidManifest.xml,res/xml/network_security_config.xml}`, `android/app/appKeyStore` (remove), `sonar-project.properties` (remove token)
- Security-sensitive: `src/Screens/Home/KycWebViewScreen.tsx`, `src/Screens/Tender/{DeviceBinding,DscBinding}.tsx`, `src/Screens/TestScreen/DecryptionScreen.tsx` (remove), `scripts/create-users.js`