0. Lock, labels, docs vs this tree
Every claim below is tagged. FACT = this checkout or a matching architecture/README section. RESUME = Aug 2026 PDF only. DOCS≠CODE = latex/markdown disagrees with the live file — speak the file they opened. MISSING = not in this Nest tree. STD CS = textbook follow-up, not a resume number.
Resume numbers that stay on Ylogx
40% faster reports · 99.9% uptime · SQL RAG +65% · RLS+RBAC 3 tiers · Redis −35% bot DB · 30 KPI dashboards +60% ops · sub-210 ms · CloudFront → ALB → ECS + Docker + CI/CD · Route 53. LangChain SQL RAG, not LangGraph. Ylogx LP primaries only: Ownership, Frugality, Dive Deep.
Docs vs live code — speak this table if they open GitHub
| Topic | What a doc says | What this tree does | What you say |
|---|---|---|---|
| JWT transport | COOKIE_AUTH_IMPLEMENTATION_COMPLETE.md claims HttpOnly cookies, cookie-parser, req.cookies.accessToken | jwt.middleware.ts reads Authorization: Bearer only. auth.controller.ts signin returns tokens in JSON. Frontend localStorage | If they open the middleware, Bearer + localStorage. Cookie MD is a migration write-up, not the running path in this checkout. |
| FinishTransaction | architecture-docs/08: COMMIT on OK, ROLLBACK on error | finish-transaction.middleware.ts always tx.commit() on res.on('finish'), rollback only if commit throws | HTTP finish commits the RLS transaction. Docs lag the file. |
| CI | backend-docs ch.7 diagram: Git → development → AWS CodeBuild | .github/workflows/convbi-prod.yml push main; convbi-qa.yml push qa; ECR then ECS | This checkout ships GitHub Actions → ECR → ECS. Resume named hop is GitHub Actions / ECS. I do not describe CodeBuild as the intern 99.9% path unless they open that latex figure. |
| Backend port | architecture-docs/08: Nest port 3000 | main.ts PORT || 5000, Dockerfile EXPOSE 5000, ECS containerPort 5000 | 5000. |
| Redis | Resume −35% bot DB | No Redis client in Nest package.json. QueryDB fieldsCache is an in-process Map, 5 min TTL, key fields:${tableName} | −35% is resume Redis cache-aside on the bot path. This Nest service caches dataset fields in memory. I do not point at a Redis line in this file. |
| Dashboards | Resume 30 KPI dashboards | 9 types in DashboardsController; architecture-docs/06 lists page counts (sales 6, receivables 5, …) | Resume 30 tiles. If they open the registry: nine types, some multi-page, month/year query params, no LLM. |
| Charts | Resume React + Recharts | Report canvas / chatbot charts: Chart.js via ElementRenderer. recharts is also a frontend dependency | Resume names Recharts. If they open report builder, Chart.js. |
| Chatbot model | Resume silent on model id | extractor.py ChatOpenAI(model="gpt-4.1", temperature=0). Frontend also lists @google/generative-ai | If they open extractor: ChatOpenAI gpt-4.1, temperature 0. Gemini is a frontend package, not the Ylogx SQL-RAG hop. LangGraph/LangSmith are IQVIA. |
| ORM / API | — | Sequelize + REST. No Prisma, no GraphQL, no Kafka, no Nest Redis | Sequelize and REST. |
| Password hash | — | auth.service.ts bcrypt gensalt 10. argon2 is in package.json unused in src | bcrypt. Argon2 is listed, not the signin path. |
| Secrets | — | task-definition JSON currently inlines env | Would-do: Secrets Manager. I do not recast secrets into the hour. |
1. How it was built
What is this product and why three runtimes?
Situation. Ylogx needed a multi-tenant BI product: org users, KPI tiles, ad-hoc reports, natural-language questions over accounting tables, and Tally zip ingest.
Task. One API that owns identity and isolation, one SPA that owns canvases, one sidecar that only proposes SQL.
Action. (1) NestJS convbi-backend on port 5000: JWT, per-request Sequelize transaction, SET ROLE, dashboards, query engine, reports, Tally→S3. (2) React 19 + Vite SPA projas-frontend: routes, FeatureAccess, Chart.js canvas, chatbot UI. (3) FastAPI convbi-ai: LangChain SQLDatabase allow-list + Pydantic chart metadata. FastAPI never executes the user’s warehouse SQL and never sees the JWT. Nest executeRawSelectQuery runs the proposed SELECT on req.dbTransaction.
Result. Resume family: SQL RAG +65%, 3-tier RLS+RBAC, 30 dashboards +60% ops, 40% faster reports, sub-210 ms, 99.9% on CloudFront/ALB/ECS. Then stop.
IQVIA is Apr 2026–present Hybrid RAG + LangGraph + LangSmith. Horizon is ERC rover software. This tree is Ylogx BI.
Why not one process?
- FACT LLM chain in Python (LangChain, Pydantic parser, schema validator). Nest stays on the request transaction so RLS cannot be skipped by the model.
- FACT Dashboards are hand-written SQL in
dashboards.service.ts— no LLM on the tile hot path. - STD CS Putting ChatOpenAI inside Nest would still need a Python-quality parser or a weaker prompt. The split is “propose vs execute,” not “Python is faster.”
2. Directory map of this checkout
| Path | Role | Entry |
|---|---|---|
convbi-backend/ | Nest 11 API, Sequelize, JWT+RLS, dashboards, queryDB, chat, reports, fileUpload, health | src/main.ts, src/app.module.ts |
convbi-frontend/ | React 19 Vite SPA (npm name projas-frontend) | src/App.tsx |
convbi-ai/ | FastAPI SQL-RAG sidecar | main.py POST /chatbot, GET /health |
architecture-docs/ | TikZ architecture (01–08). Best diagrams. Some lag code (port 3000, FinishTransaction rollback, CodeBuild). | 01_system_overview.tex |
backend-docs/ frontend-docs/ | LaTeX books: modules, API, Docker, security, report builder, chat | chapters 01–11 / 01–09 |
COOKIE_AUTH_IMPLEMENTATION_COMPLETE.md | Cookie migration notes | Not matching jwt.middleware.ts |
LOGGING_GUIDE.md / LOGGING_IMPLEMENTATION_SUMMARY.md | Interceptor, SQL log, slow-query warn | Matches logging.interceptor.ts |
convbi-backend/.github/workflows/ | prod on main (3-min approval Arjun-ylogx), qa on qa | ECR convbi-backend, cluster convbi-cluster, service convbi-backend-service, region ap-south-1 |
convbi-backend/.aws/task-definition.json | ECS task: cpu 1024, memory 3072, port 5000 | Do not dump JWT/SMTP/RDS values |
Nest modules registered in AppModule
Config, Sequelize, Health, Auth, Organization, ModuleRegistry, QueryDB, PolicyManagement, RoleManagement, UserHierarchy, Overview, Reports, FeatureManagement, Chat, Dashboards, FileUpload. JWT+RLS applied to * except health + listed auth POSTs.
Sequelize models (app tables)
From applicationTables.ts and architecture-docs/08: organizations, module_registry, policies, role_policies, roles, user_roles, users, chat_feedbacks, chat_messages, chats, favorite_conversations, favorite_messages, features, feedbacks, reports, user_features. Warehouse fact tables are not Sequelize models — they are queried as SQL under RLS.
Warehouse tables the AI sidecar is allowed to see
extractor.py include_tables: accounting_transaction, bs, cashflow_bank, cashflow_others, inventory_stk_masters, inventory_stk_vouchers, outstanding_payable, outstanding_receivable, pl, sales. Descriptions live in config.py table_descriptions; schema_validator.validate_and_sync_schema(..., auto_sync=True) on extractor init.
3. Request lifecycle (exact files)
Browser React SPA
App.tsx Router + Sidebar layout
axiosInterceptor.ts → Authorization: Bearer (strips/re-adds)
chatbot.ts fetch → Authorization: localStorage token as-is
(signin returns "Bearer " + jwt, so both work)
│ REST JSON
▼
Nest main.ts
AllExceptionsFilter
LoggingInterceptor
CORS origin = CORS_ORIGIN || '*' credentials true
bodyParser 50mb JSON + urlencoded
ValidationPipe whitelist + forbidNonWhitelisted + transform
FinishTransactionMiddleware (registers res.on('finish'))
Swagger /api
│
▼ (excluded: health*, auth signup/signin/otp/activate/forgot/reset/update-password/refresh-token)
JwtMiddleware jwt.verify(JWT_ACCESS_SECRET)
req.user = { userId: guid, role, email, client_id, organizationId }
│
▼
RLSMiddleware sequelize.transaction()
SUPER_ADMIN → RESET ROLE + set_config flags
ADMIN → first SYSTEM role.db_role_name or SET ROLE rls_no_access
USER → first db_role_name (Postgres one active role)
policies → allowedModules → ModuleRegistry.tables → req.user.allowedTables
req.dbTransaction = tx
│
▼
Controller → Service queries use req.dbTransaction
│
▼
PostgreSQL RLS policies on current_setting('app.*') + SET ROLE
│
▼
res finish → FinishTransactionMiddleware commit (see docs≠code)
architecture-docs/03 excluded-route table matches app.module.ts. JWT payload names: guid, role, email, client_id, organizationId — not a Postgres role. Org isolation is SET ROLE + GUC + (for raw SQL) injected organizationId AND, not “JWT org_id is RLS.”
4. Auth algorithm
| Step | Where | Logic |
|---|---|---|
| Signup | auth.service.ts signup | Email must include ORGANISATION_EMAIL domain. Hash bcrypt gensalt 10. User PENDING. SMTP OTP via nodemailer. 6-digit OTP Math.floor(100000 + random*900000). |
| Activate | POST auth/activate | Verify OTP + expiry. Status ACTIVE. |
| Signin | POST auth/signin (excluded from JWT) | Lookup email, require isActive, bcrypt.compare. signToken payload: email, guid, role, isActive, passwordUpdatedAt, client_id, organizationId. Returns accessToken: "Bearer "+jwt and refresh similarly. |
| Expiry | jwt.util.ts | Access: JWT_EXPIRATION_TIME. Refresh: JWT_REFRESH_TOKEN_EXPIRATION_TIME. Task-def in this tree sets those env keys (do not recite secret strings). Cookie MD claims 1h/7d — that is the cookie write-up, not a spoken TTL unless they open the env. |
| Refresh | POST auth/refresh-token excluded | Body refresh token. Frontend useTokenRefresh + authService.initTokenRefresh default 15 min (VITE_TOKEN_REFRESH_INTERVAL). Single-flight refreshPromise. Failure → logout. |
| SPA gate | App.tsx | localStorage.token. Public: /login /signup /forgot-password /reset-password /activateAccount/:token /forgetPassword/:token. Protected layout + ProtectedRoute per feature route. Admin nest under /dashboard/admin. |
| Roles enum | userRole.enum.ts | SUPER_ADMIN, ADMIN, USER, GUEST. GUEST blocked from dashboards/query in several services. |
5. RLS algorithm (live use(), not the commented block)
The file still contains a large commented old use() that SET ROLE’d every role name and fell back to RESET ROLE for admins with no SYSTEM role. Do not describe the comment as production. Live path from line ~189:
- If no
req.user, next(). const transaction = await this.sequelize.transaction()— one connection held until HTTP finish.- SUPER_ADMIN:
RESET ROLEplusset_config('app.is_SUPER_ADMIN','true', true)(third arg true = transaction-local). organizationId GUC set empty. Full access. Store tx, next. - ADMIN: load org; load UserRole whose Role.scope is SYSTEM. Filter those with
db_role_name. If any:SET ROLE "firstName"+ admin GUCs +app.admin_hierarchy_filter. If roles exist but none have db_role_name, or zero SYSTEM roles:SET ROLE "rls_no_access". Comment in code: previous RESET ROLE leaked. - USER: load all non-deleted roles with RolePolicy→Policy. If a
db_role_nameexists, SET that first one only (“PostgreSQL can only have one active role”). Collectpolicy.policyRole,allowedModules, load ModuleRegistry.tables →req.user.allowedTables,roleModuleMapping. Elserls_no_accessandnoDataAccess=true. - On error: rollback that tx, next(error).
FACT Identifier interpolation: SET ROLE "${dbRoleName}" is not a bind parameter. Role names must be trusted admin-created identifiers.
FACT Pool in sequelize.module.ts: max 20, min 0, acquire 30000, idle 10000. backend-docs warn: each request holds a tx until finish, so Postgres max_connections must cover concurrency × open transactions. synchronize: true is on — schema auto-sync; speak as a would-do to turn off in prod if they grind Highest Standards.
FACT App-level org AND in QueryDB is defense in depth. RLS is still the row filter. JWT organizationId is not a Postgres policy by itself.
6. Query engine algorithms
Controller: queryDB.controller.ts. Service: queryDB.service.ts. Every data path takes req.dbTransaction.
| Endpoint | Algorithm |
|---|---|
| GET tables | List tables the user may see (policy / SUPER_ADMIN). System tables gated. |
| GET …/columns-with-types | information_schema + hasTableAccess. |
| GET dataset/:t/fields | Map cache key fields:${tableName}, TTL 5 min, SHA1 etag of JSON fields. Maps PG types → UI number/date/boolean/string. Not Redis. Not per-user key — process-wide by table name. |
| POST dataset/:t/field/:c/values | Distinct values with slicer filters. Skip text/json/jsonb/array for “all distincts” (LIMIT 1000 per column). |
| POST dataset/:t/query | Filtered dataset for charts. |
| GET …/data | SELECT * FROM "table" on RLS tx. No LIMIT — failure class vs execute-query cap. |
| POST execute-query | Report-builder join. Guest blocked. hasTableAccess on main + join. Build SELECT cols FROM main [JOIN type join ON condition]. JoinType enum: INNER/LEFT/RIGHT/FULL JOIN. Date BETWEEN as bind ?. Org filter string-concat if tableHasOrganizationId. LIMIT default 1000, max 5000. |
| POST execute-raw-sql | Same executeRawSelectQuery as chatbot. |
| POST chat-query | See §7. |
hasTableAccess
- Chat tables → true for authenticated.
- SUPER_ADMIN → all non-system tables.
- ADMIN → tables from current policy assignment helper.
- System tables → false for normal users.
- Else table must appear in getTables() for that user.
executeRawSelectQuery
- Trim, lower, must
startsWith('select')— WITH at the front is rejected here; extractor wraps WITH asSELECT * FROM (… ) qfirst. - Regex
(?:from|join)\s+…collect table names (schema stripped, quotes stripped). - hasTableAccess each table.
- If org filter needed, inject
WHERE (alias."organizationId" = '…') ANDbefore GROUP/ORDER/LIMIT/HAVING/UNION or append. sequelize.query(modifiedSql, { type: SELECT, transaction: dbTransaction }).
STD CS Prefix check is not a SQL parser. Comments, stacked queries, or functions can still be a later hardening item. Table regex can miss some FROM shapes. Org id is interpolated, dates in execute-query are bound.
7. SQL RAG — Nest proposes nothing; FastAPI proposes; Nest executes
SPA chatbot.ts fetch POST /queryDB/chat-query { query, options }
QueryDBService.executeChatQuery
fetch CHATBOT_API_URL || https://chatbot.projas.in/chatbot
body { query, options } ← no JWT forwarded
FastAPI main.py chatbot_function
SQLQueryAndDetailsExtractor.extract_data(input=query)
SQLDatabase(engine, include_tables=[10 warehouse tables])
validate_and_sync_schema(auto_sync=True)
prompt | ChatOpenAI(gpt-4.1, temperature=0) | PydanticOutputParser(SQLQueryAndDetails)
If eventQuery starts WITH and not SELECT: wrap SELECT * FROM (sql) q
Strip trailing semicolon, collapse whitespace
Nest executeRawSelectQuery(eventQuery, user, req.dbTransaction)
Return rows + chatMessage + chartType + xAxis/yAxis/series + showChart + dataPeriod
SPA prepareChartData → ElementRenderer (same as report builder) Chart.js
FACT FastAPI CORS allow_origins=["*"] with credentials True. Nest default CORS also * unless CORS_ORIGIN set.
FACT Prompts in prompts.py: SELECT-only, no INSERT/UPDATE/DELETE/DDL, no trailing semicolon, chartType enum, showChart false ⇒ chartType table, series only for stacked/striped/multiLine/barLine.
FACT On SQL failure Nest still returns the chatbot message with showChart: false and error — user sees a sentence, empty data.
FACT AI Dockerfile: python:3.12-slim, gunicorn 8 UvicornWorker, bind 0.0.0.0:80, timeout 180, healthcheck curl /health. FastAPI itself does not authenticate the caller — Nest is the trust boundary.
Chart-type normalize table (architecture-docs/05, copy): stackedcolumn→stackedColumn, line/multiline→line, pie→pie, donut→hollowPie, unknown→bar.
8. Dashboards — 9 types, fixed SQL, no LLM
architecture-docs/06 registry (copy of the table):
| ID | Pages | API |
|---|---|---|
| overview | 1 | GET /dashboards/overview |
| sales | 6 | GET /dashboards/sales |
| receivables | 5 | GET /dashboards/receivables (page, pageSize default 12 max 100) |
| payables | 3 | GET /dashboards/payables |
| stock | 2 | GET /dashboards/stocks |
| collections | 3 | GET /dashboards/collections |
| profitLoss | 1 | GET /dashboards/profit-loss |
| balanceSheet | 1 | GET /dashboards/balance-sheet |
| cashflow | 2 | GET /dashboards/cashflow |
Also GET last-updated and detailed-update-info. Query params: month/months, year/years (comma lists). Filters context: selectedMonths, selectedYears, currencyUnit lakhs|actual, persisted dashboardFiltersState_v2.
Overview algorithm (pattern for the rest)
- GUEST → 403. SUPER_ADMIN → no app clientFilter.
- If
user.dbRoleNameslength > 0, treat RLS as the row filter and skip application clientFilter; elsegetClientFilter(user). - Parse month/year lists; default current month/year.
- Date predicates:
EXTRACT(MONTH FROM col) IN (…)and year IN. Balance-sheet “as of” uses max year/month. - Parallel-ish SQL: SUM sales.total_amount; P&L; accounting_transaction; outstanding_receivable / payable aging to month-end; cashflow_bank; bs assets/liabilities. All on the RLS transaction.
Sales extra query params: topCustomerCount, bottomCustomerCount. Growth queries compare target year vs previous year with EXTRACT.
RESUME 30 KPI dashboards, +60% ops, sub-210 ms. FACT nine controller types. Do not invent p95.
9. Report builder — five phases (architecture-docs/04)
- Data source: Utilitybar → GET queryDB/tables (RLS-visible).
- Joins + columns: INNER/LEFT/RIGHT/FULL OUTER, GET columns-with-types.
- Fetch: single table getTableData or POST execute-query (join SQL, LIMIT 1000–5000).
- Chart building: react-dnd DraggableColumn → ChartDropZone x/y → ElementRenderer → Chart.js (bar/line/pie/area/doughnut).
- Canvas: Canvas.tsx react-rnd, 15px grid, PowerBICustomizationPanel, save JSON via POST /reports { name, data }. Load GET /reports/:id. Body limit 50mb in Nest for large report JSON. Export: frontend-docs/07 (jspdf / file-saver).
Slicers: SlicerPanel + dataset field values API. Saved report is canvas JSON, not a warehouse materialized view. Guest cannot create reports.
10. Chat CRUD vs warehouse
ChatController: create/list/get/update/delete chat; add/update messages; favorites; feedback. ChatService.executeWithoutRLS: new transaction, RESET ROLE, run callback, commit. Used for chats / messages / favorites / feedback so SET ROLE on warehouse policies does not hide app chat rows.
Warehouse facts stay on req.dbTransaction via chat-query. executeWithoutRLS on sales/bs would skip isolation — that is a leak class, not the implemented path.
architecture-docs/05 says executeWithoutRLS is a “new Sequelize instance”; live code is this.sequelize.transaction() + RESET ROLE on the same pool. Speak the file.
11. Tally ingest
- POST /file-upload/tally multipart. Multer memoryStorage, max 2 files, 200MB each, .zip name + mime.
validateFile: extension, size, mime, ZIP magic bytes PK (0x50 0x4b 03 04 / empty / spanned), AdmZip integrity, not empty.- S3 Upload to
tally-uploads/${Date.now()}-sanitizedName, URIs3://bucket/key. Region default ap-south-1. - Build TallyUploadDto: client_id, uploaded_by, period_in_years, credentials current/previous year, inventory flags. Gateway POST
TALLY_GATEWAY_URL(default tallygw.projas.in/instance/create) header x-api-key, timeout 30s.
Zips are not stored as bytea in Postgres. Gateway is a separate service this Nest does not implement.
12. Admin RBAC graph
User ──< UserRole >── Role (scope SYSTEM|ORG, db_role_name)
└──< RolePolicy >── Policy (allowedModules[], policyRole)
└── ModuleRegistry (tables[])
User ──< UserFeature >── Feature (route)
User ── belongs → Organization (client_id)
UserHierarchy module: org tree / manager relations (UserHierarchyModule)
OverviewModule: admin stats
FeatureManagement: assign routes; frontend FeatureAccessProvider
SUPER_ADMIN hasFeatureAccess always true
others: feature.route must be assigned (including ADMIN)
fetch cache 30s; refreshFeatures on login custom event
Admin UI routes: /dashboard/admin, roles, policies, users, tables, features, custom-expressions. Policy form has expression-builder + module-tree.
3-tier resume mapping to speak: (1) app role SUPER_ADMIN/ADMIN/USER/GUEST, (2) Postgres db_role_name + RLS policies, (3) Feature routes in the SPA. Do not invent a fourth tier.
13. Frontend map
| Piece | File | Job |
|---|---|---|
| Router | src/App.tsx | Public auth + Sidebar outlet |
| Dashboard | modules/dashboard/App.tsx + predefined/* | 9 types, multi-page |
| Chatbot | modules/chatbot/App.tsx, api/chatbot.ts, api/chat.ts | NL query + ChatContext CRUD |
| Report builder | modules/reportBuilder/* | dnd + rnd + ElementRenderer |
| Upload | modules/upload/TallyUploadPage.tsx | Tally zips |
| Admin | modules/admin/*, components/admin-page/* | RBAC UI |
| Filters | DashboardFiltersContext | months/years/currency |
| HTTP | axiosInterceptor timeout 10000; chatbot uses fetch (no 10s cap) | 401 clears storage → /login |
UI libraries present: Tailwind/Radix, MUI, Antd, Mantine, Redux Toolkit, TanStack Query, DOMPurify, Chart.js + Recharts. Resume bullet is React + Recharts — do not claim you rewrote tiles in all four UI kits as the intern metric.
Frontend Docker: multi-stage node:20-alpine build → nginx:stable-alpine port 80, gzip, SPA try_files, /health 200, X-Frame-Options SAMEORIGIN. frontend-docs claim ~30–40MB image — that number is the latex book, not a resume metric.
14. How it is deployed
Three images
| Service | Dockerfile | Runtime |
|---|---|---|
| Backend | node:18.18.2-alpine3.18, npm --force, nest build, USER node, CMD node dist/main, EXPOSE 5000, curl for probes | ECS task cpu 1024 / memory 3072 / port 5000 |
| Frontend | Vite build then nginx:stable-alpine :80 | Static SPA |
| AI | python:3.12-slim, gunicorn 8 workers, :80, timeout 180 | Sidecar; default chatbot URL chatbot.projas.in |
GitHub Actions (this repo — speak this, not the CodeBuild latex)
QA (convbi-qa.yml): push branch qa → ubuntu-latest, environment qa, AWS credentials from GitHub secrets, ECR login, docker build+push :sha and :qa, render task def, deploy ECS, wait-for-service-stability: true. No manual approval.
Prod (convbi-prod.yml): push main, environment production, tags :sha and :latest, then trstringer/manual-approval@v1 timeout 3 minutes, approver Arjun-ylogx, minimum-approvals 1, then ECS deploy + wait-for-service-stability.
Shared env in YAML: AWS_REGION ap-south-1, ECR_REPOSITORY convbi-backend, ECS_SERVICE convbi-backend-service, ECS_CLUSTER convbi-cluster, CONTAINER_NAME convbi-backend.
RESUME 99.9% via CloudFront → ALB → ECS + Docker + CI/CD + Route 53. This backend workflow is the Actions→ECR→ECS slice. CloudFront/ALB/Route 53 are resume edge; they are not defined inside these two YAML files. Do not claim you owned EKS. Do not invent desired count or Fargate vs EC2 unless they open the cluster in AWS.
Local compose
convbi-backend/docker-compose.yml service convbi-backend port 5000:5000, env from Postgres/JWT/SMTP/FRONTEND_* (no secrets in this book).
Health
Terminus: GET /health and /healthcheck ping Postgres. GET /health/simple returns process-up JSON without DB. Use /health for “sick task”; /health/simple can stay green if RDS is down — that is the class.
15. Logging
From LOGGING_IMPLEMENTATION_SUMMARY (matches code): global HttpException filter; LoggingInterceptor (method, URL, user, IP, duration, status, redacts passwords/tokens); CustomLogger sql() + slow >1s; sequelize benchmark + SQL_LOGGING or NODE_ENV=development; auth.service example logs. No Datadog / CloudWatch dashboard in this repo — do not invent one. Speak interceptor + Terminus + ECS wait-for-stability.
16. Security slice (what you actually built)
- Bearer JWT verify on almost every route; bcrypt passwords; org-domain signup.
- RLS SET ROLE fail-closed
rls_no_access; transaction-scoped GUCs. - SQL RAG: SELECT prefix + table allow-list + RLS tx + optional org AND. FastAPI allow-listed tables + SELECT-only prompt + Pydantic.
- Tally: magic bytes + zip integrity, not trust MIME alone.
- ValidationPipe whitelist. Swagger Bearer.
- Would-do if they grind: Secrets Manager; CORS not *; parser not prefix; bind org id; FinishTransaction rollback on 5xx; cookie/HttpOnly if they want XSS story — cookie MD is not this file; turn synchronize off; Redis still resume-only in this Nest package.json.
17. One-request walks
A. “Revenue this quarter” in the bot
ChatContext addMessage user → POST /chat/:id/messages (RESET ROLE chat row) → sendChatMessage fetch chat-query → FastAPI gpt-4.1 SELECT on sales → Nest executeRawSelectQuery on SET ROLE tx → ElementRenderer chart → addMessage bot with metadata JSON.
B. Sales dashboard tile
DashboardFilters months/years → GET /dashboards/sales?months=&years= → DashboardsService fixed aggregations on sales table via dbTransaction → KPI cards. No ChatOpenAI.
C. Report with join
Pick tables → POST execute-query JOIN … LIMIT n → drag columns → save POST /reports JSON.
D. Tally year zip
Multipart → S3 URI → gateway create instance. Warehouse rows appear later via gateway/ETL, not via this controller INSERT.
18. Failure classes (no invented tickets)
| Class | What in this tree | What you say |
|---|---|---|
| Wrong-org JOIN | execute-query can JOIN two tables; org AND only if organizationId column exists; RLS still on SET ROLE | App WHERE is extra. Isolation is the role. Missing org column + weak policy is the leak class. |
| Admin with no SYSTEM role | Old comment RESET ROLE; live rls_no_access | Fail closed after a leak class. |
| LLM on tiles | Dashboards are SQL files | I would not put ChatOpenAI on overview. Bot is the NL path. |
| Hallucinated SQL | Prefix SELECT, table regex, hasTableAccess, RLS, extractor WITH wrap | Model proposes; Nest executes. Failed SQL returns message, empty chart. |
| executeWithoutRLS misuse | Chat metadata only | Warehouse stays on req.dbTransaction. |
| Commit on HTTP finish | FinishTransaction always commit | Docs say rollback-on-error; file commits on finish. Would-do: inspect statusCode. |
| Pool leftover | tx until finish, pool 20 | Slow handler holds a connection and a SET ROLE. |
| Cache without tenant | fieldsCache keyed by table name only | Metadata, 5 min, in-process. Resume Redis −35% is a different hop. |
| Sick ECS task | /health pings Postgres; /health/simple does not | ALB should use the Terminus route. |
| FastAPI CORS * | main.py | Sidecar should not be the public browser origin; Nest is the API. |
| SELECT * no limit | getTableData | Report path caps 5000; this GET does not. |
| axios 10s | dashboard/admin | Chat uses fetch without that timeout; AI gunicorn timeout 180. |
| DNS / chatbot down | BAD_GATEWAY if chatbot HTTP not ok | Tiles still work; bot degrades. |
19. Honest 10×
- I did not shard Postgres. First: pool vs max_connections, indexes on filter columns, stop SELECT *.
- Per-request transactions × 10× QPS: shorter handlers or read-only SET ROLE pooling — I did not build PgBouncer in this repo.
- fieldsCache is one process: 10× tasks = 10× maps. Resume Redis is the shared cache story, not this Map.
- ChatOpenAI on every NL question: cost and latency. Tiles stay SQL. I would not “scale” by putting the model on dashboards.
- ECS: this YAML deploys one service name. I do not invent autoscaling numbers.
- AI 8 gunicorn workers is the Dockerfile, not a capacity study.
20. Spoken cards (one project per answer)
Walk me through RLS.
Every authenticated request opens a Sequelize transaction. JwtMiddleware puts guid/role/org on req.user. RLSMiddleware SET ROLE to the first Postgres db_role_name, or rls_no_access if missing. SUPER_ADMIN RESET ROLE. Policies map to ModuleRegistry tables on the request. Queries use that transaction so Postgres RLS sees the role. Finish commits when the HTTP response finishes. Resume: 3-tier RLS+RBAC. I do not have a sample size beyond the PDF.
Next: Why first role only? Postgres one active role. Why not JWT org_id? That’s a claim, not a policy.
How does the chatbot not leak another org?
FastAPI only returns SQL and chart fields. Nest runs SELECT on the same RLS transaction as the user. Tables must pass hasTableAccess. Non-super-admin gets organizationId injected when the table has that column. Resume SQL RAG +65%. Protocol sentence.
Redis −35%?
That number is the Ylogx bot path cache-aside on the resume. This Nest file caches dataset fields in a Map for five minutes. I will not point at ioredis in package.json because it is not there.
99.9% / how did you ship?
Resume hop CloudFront, ALB, ECS, Docker, CI/CD. In this repo: GitHub Actions on qa auto-deploys; main waits three minutes for Arjun-ylogx then ECS wait-for-service-stability. Image node alpine, USER node, Terminus Postgres ping. I do not invent incident IDs.
Why not LangGraph here?
This sidecar is LangChain SQLDatabase + one extractor chain. LangGraph Deep Research is IQVIA Apr 2026–present.
21. Question bank — how / what / where / logic / algo
Resume-derived unless noted. Other candidates’ loops may differ. Not a Job 10454435 list.
| # | They might ask | You answer (short) |
|---|---|---|
| 1 | What did you build? | Multi-tenant BI: Nest API + React SPA + FastAPI SQL RAG. Ylogx intern. |
| 2 | Where is the entrypoint? | backend src/main.ts; frontend src/App.tsx; AI main.py. |
| 3 | What middleware order? | CORS, 50mb body, ValidationPipe, FinishTransaction hook, Jwt, RLS, controller. |
| 4 | What is on the JWT? | guid, role, email, client_id, organizationId (plus isActive, passwordUpdatedAt at sign). |
| 5 | Where is SET ROLE? | rls.middleware.ts live use(). |
| 6 | Why rls_no_access? | Fail closed; old RESET ROLE leaked. |
| 7 | Why first db_role_name? | Postgres one SET ROLE at a time. |
| 8 | What is set_config third arg? | true = local to transaction. |
| 9 | SUPER_ADMIN path? | RESET ROLE + flags; empty org GUC. |
| 10 | ADMIN without SYSTEM role? | rls_no_access. |
| 11 | How allowedTables get filled? | Policy.allowedModules → ModuleRegistry.tables. |
| 12 | Is JWT org_id RLS? | No. Role + GUC + policies. |
| 13 | executeWithoutRLS? | New tx, RESET ROLE, chat_* only. |
| 14 | Chatbot URL? | CHATBOT_API_URL or https://chatbot.projas.in/chatbot. |
| 15 | Does FastAPI get the JWT? | No. |
| 16 | Model? | If they open extractor: gpt-4.1 temp 0. Resume silent. |
| 17 | Allow-listed tables? | Ten names in extractor.py. |
| 18 | WITH CTE? | Extractor wraps as SELECT * FROM (with…) q so Nest startsWith select passes. |
| 19 | How tables extracted from SQL? | FROM/JOIN regex, strip schema/quotes. |
| 20 | Org inject logic? | If tableHasOrganizationId, splice WHERE. |
| 21 | Join types? | INNER LEFT RIGHT FULL JOIN enum. |
| 22 | Row cap? | execute-query 1000 default 5000 max. getTableData uncapped. |
| 23 | Fields cache? | Map 5 min, key fields:table, SHA1 etag. |
| 24 | Is that Redis? | No. Resume −35% is Redis on bot path. |
| 25 | Dashboard LLM? | No. Fixed SQL. |
| 26 | How many dashboard types? | 9 in controller. Resume 30 tiles. |
| 27 | Date filter algo? | EXTRACT MONTH/YEAR IN lists; default now. |
| 28 | When is clientFilter skipped? | SUPER_ADMIN or dbRoleNames present (RLS mode). |
| 29 | Receivables paging? | page default 1, pageSize default 12 max 100. |
| 30 | Chart.js vs Recharts? | Canvas/chat: Chart.js ElementRenderer. Recharts on resume + dependency. |
| 31 | Report save? | POST /reports JSON blob, createdBy userId. |
| 32 | 50mb body? | main.ts report payloads. |
| 33 | react-dnd vs react-rnd? | Columns dnd; canvas rnd 15px grid. |
| 34 | Feature vs RLS? | Feature = route in SPA. RLS = rows in Postgres. |
| 35 | ADMIN all routes? | No. FeatureAccess requires assignment except SUPER_ADMIN. |
| 36 | Signup domain? | email must include ORGANISATION_EMAIL. |
| 37 | Hash? | bcrypt 10. argon2 unused in src. |
| 38 | Token storage? | localStorage; signin prefixes Bearer. |
| 39 | Cookie MD? | Not jwt.middleware.ts in this checkout. |
| 40 | Refresh interval? | 15 min default + on window focus. |
| 41 | axios 401? | Toast, clear token, /login. |
| 42 | Chat fetch Authorization? | Raw localStorage; already has Bearer from signin. |
| 43 | Tally max size? | Multer 200MB, 2 files; service also MAX_FILE_SIZE_MB default 500. |
| 44 | Zip check? | Magic bytes + AdmZip entries. |
| 45 | S3 key? | tally-uploads/timestamp-sanitized. |
| 46 | Gateway timeout? | 30s axios. |
| 47 | Pool size? | max 20. |
| 48 | synchronize? | true in sequelize.module — would-do off in prod. |
| 49 | SSL? | POSTGRES_SSL true → rejectUnauthorized false. |
| 50 | Health vs simple? | DB ping vs process-up. |
| 51 | Swagger? | /api Bearer. |
| 52 | Prod workflow trigger? | push main. |
| 53 | QA trigger? | push qa, tag :qa, no approval. |
| 54 | Who approves prod? | Arjun-ylogx, 3 minute window. |
| 55 | ECS names? | convbi-cluster, convbi-backend-service, ap-south-1. |
| 56 | Task cpu/mem? | 1024 / 3072 from task-def (no secrets). |
| 57 | Backend USER? | Dockerfile USER node. |
| 58 | AI workers? | gunicorn 8, timeout 180. |
| 59 | Frontend nginx? | SPA try_files, gzip, /health. |
| 60 | CodeBuild? | Latex only. Actions is the workflow file. |
| 61 | Port 3000? | Latex. Code 5000. |
| 62 | Prisma? | No. Sequelize. |
| 63 | GraphQL? | No. REST. |
| 64 | Kafka? | Not this intern. |
| 65 | EKS? | Not on the resume hop. ECS. |
| 66 | Datadog? | Not in this repo. |
| 67 | LangSmith here? | IQVIA. |
| 68 | Gemini bot? | Package exists; extractor is ChatOpenAI. |
| 69 | Redux? | Dependency; resume bullet is React+Recharts. |
| 70 | FinishTransaction rollback? | Only if commit throws. |
| 71 | N+1 dashboards? | Many sequential queries per type — would-do batch; no invented p95. |
| 72 | GET data SELECT *? | Yes, no limit. |
| 73 | SQL injection org id? | Interpolated; dates bound on execute-query. |
| 74 | SET ROLE injection? | Identifier interpolated; trust admin-created names. |
| 75 | CORS * + credentials? | Both Nest default and FastAPI. |
| 76 | Logging redaction? | Interceptor sanitizes passwords/tokens. |
| 77 | Slow SQL? | CustomLogger warn >1s when SQL logging on. |
| 78 | Guest? | Enum GUEST; blocked on dashboards/query/reports create. |
| 79 | systemAccessibleTables? | App tables list in applicationTables.ts. |
| 80 | Warehouse in Sequelize? | No models for sales/bs; raw SQL. |
| 81 | schema validator? | AI config vs live DB; auto_sync true; log and continue on ValueError. |
| 82 | Pydantic fields? | eventQuery, chatMessage, chartType, axes, series, dataPeriod, showChart, has_explicit_limit. |
| 83 | temperature? | 0. |
| 84 | Retry parser? | BaseExtractor has retry parser; SQL extractor overrides to prompt|llm|parser. |
| 85 | ChatContext? | chats, currentChatId, messages, CRUD wrappers. |
| 86 | normalizeChartType? | architecture-docs/05 table. |
| 87 | last-updated? | GET /dashboards/last-updated?dashboard= |
| 88 | User hierarchy? | UserHierarchyModule — org/manager graph for admin. |
| 89 | Module registry? | Maps module guid → tables for policies. |
| 90 | Overview admin? | OverviewModule stats, not the KPI overview dashboard. |
| 91 | Policy expression builder? | Admin UI policy-form + expression-builder. |
| 92 | DOMPurify? | Frontend sanitizer dep — speak if they open package.json, not as RLS. |
| 93 | 10× first move? | Pool/indexes/SELECT *; not shard; not LLM on tiles. |
| 94 | What you did not own? | Tally gateway internals, RDS patching, CloudFront distro JSON (resume edge, not these YAML files). |
| 95 | Conv-BI GitHub name? | Same intern product. Resume row is Ylogx. |
| 96 | projas-frontend? | npm name of the SPA. |
| 97 | If they open IQVIA after this? | Switch intern: Apr 2026 Hybrid RAG. Do not mix LangGraph into this answer. |
| 98 | Default LP if unnamed? | Dive Deep Redis+RLS (resume Redis + this RLS file), then Deliver Horizon 17th if they already heard Ylogx. |
| 99 | How do you know +65%? | Mechanism SQL RAG; PDF number; no protocol on resume. |
| 100 | Would you put secrets in task-def? | Would-do Secrets Manager. I will not paste values. |
Deeper spoken STAR + 80 more rehearsal rows: interview-book/r4/12-convbi-codebase.html and Q1–Q125 in 26-convbi-full-codebase.html.
22. Keep straight
| Number / name | Lives on |
|---|---|
| 40%, 99.9%, +65%, 3 tiers, Redis −35%, 30 dashboards +60%, sub-210 ms, CloudFront/ALB/ECS | Ylogx intern |
| LangGraph, LangSmith, Azure AI Search, 200+ sites/BRDs | IQVIA current |
| 17th/80+, GStreamer 60 FPS, ZED, costmap −55% | Horizon ERC |
| 73→89% mAP, 24 FPS, 20+ cameras | Argus |
| sub-300 ms, 99.5%, 3–10 min doubts | GiftedBooks |
| −30% iteration, 50+ models, Gemini RAG bots | StratifyLabs — not this Nest chatbot |
| Sequelize, Bearer, Map 5 min, GH Actions, port 5000, gpt-4.1 in extractor, Chart.js canvas, 9 dashboard types, rls_no_access, executeWithoutRLS on chat_* | This checkout |
| Java | Live Code only |
Tighten: one intern per spoken block. If they already heard Ylogx RLS, backup LP story is Horizon 17th (Deliver) or GiftedBooks 3–10 min (Customer) — not a fourth Ylogx trophy. Do not rank internships. Dates: Horizon Feb–Jun 2024, Ylogx Nov 2024–Oct 2025, IQVIA Apr 2026–present.
Built from architecture-docs 01–08, backend-docs (including Docker ch.7), frontend-docs (Docker ch.9, chatbot, report builder), COOKIE/LOGGING markdown, and the live TypeScript/Python/YAML/Dockerfiles in December25. Latex diagrams copied as English tables where they still match; disagreements called out in §0.