Files
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

37 KiB
Raw Permalink Blame History

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 useStates 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 undefineds 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.

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)

  1. 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.
  2. Tune QueryClient defaults (staleTime, retry), fix query keys, add enabled guards and invalidateQueries on mutations. Move QueryClient to a module const.
  3. 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)

  1. Typed navigation + Auth/App stack split; move Splash gating into declarative navigator logic.
  2. Unify forward/reverse bidding and the two card components into one config-driven module; move subscribe into useEffect with cleanup; disconnect on unmount.
  3. Break up TenderBidding/ProfileTab/Ragistration: extract crypto, API, and validation into core/feature modules; convert registration to a reducer/state machine (or Formik).
  4. 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)

  1. Collapse to one design system + one palette; delete src/Component and Constant/Color after migration. Add ErrorBoundary + shared state wrappers.
  2. Add responsive scaling + accessibility labels; introduce i18n scaffolding (even if only en at first).
  3. 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)

  1. CI (GitHub Actions): install → typecheck → lint → test → build. Add husky + lint-staged.
  2. 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