ConvBI / Projas — Ylogx intern BI, from this checkout

Adarsh Vishwakarma. Amazon SDE I AUTA APJ Job 10454435 still has no public named live-round question. Unnamed stays unnamed. R1 13 Aug 2026, R2 18 Aug 2026, R3 done. This book is leftover-hour / Round 4 rehearsal.

Live Code is Java. This intern product is TypeScript NestJS + React and Python FastAPI LangChain SQL RAG. Say that split once.

Resume product is Ylogx (Nov 2024–Oct 2025). Folders named Conv-BI / Projas / warpflow-adjacent GitHub are that intern, not a second intern. Percents only from the Aug 2026 PDF. If they ask how a percent was measured: name the mechanism, say the PDF number, then “I do not have the study protocol on the resume.”

Companions: interview-book/ (r4 cards, 125-Q handbook, live-path, why-cards). Do not paste secrets from .aws/task-definition.json.

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

TopicWhat a doc saysWhat this tree doesWhat you say
JWT transportCOOKIE_AUTH_IMPLEMENTATION_COMPLETE.md claims HttpOnly cookies, cookie-parser, req.cookies.accessTokenjwt.middleware.ts reads Authorization: Bearer only. auth.controller.ts signin returns tokens in JSON. Frontend localStorageIf they open the middleware, Bearer + localStorage. Cookie MD is a migration write-up, not the running path in this checkout.
FinishTransactionarchitecture-docs/08: COMMIT on OK, ROLLBACK on errorfinish-transaction.middleware.ts always tx.commit() on res.on('finish'), rollback only if commit throwsHTTP finish commits the RLS transaction. Docs lag the file.
CIbackend-docs ch.7 diagram: Git → development → AWS CodeBuild.github/workflows/convbi-prod.yml push main; convbi-qa.yml push qa; ECR then ECSThis 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 portarchitecture-docs/08: Nest port 3000main.ts PORT || 5000, Dockerfile EXPOSE 5000, ECS containerPort 50005000.
RedisResume −35% bot DBNo 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.
DashboardsResume 30 KPI dashboards9 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.
ChartsResume React + RechartsReport canvas / chatbot charts: Chart.js via ElementRenderer. recharts is also a frontend dependencyResume names Recharts. If they open report builder, Chart.js.
Chatbot modelResume silent on model idextractor.py ChatOpenAI(model="gpt-4.1", temperature=0). Frontend also lists @google/generative-aiIf 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 RedisSequelize and REST.
Password hash—auth.service.ts bcrypt gensalt 10. argon2 is in package.json unused in srcbcrypt. Argon2 is listed, not the signin path.
Secrets—task-definition JSON currently inlines envWould-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

PathRoleEntry
convbi-backend/Nest 11 API, Sequelize, JWT+RLS, dashboards, queryDB, chat, reports, fileUpload, healthsrc/main.ts, src/app.module.ts
convbi-frontend/React 19 Vite SPA (npm name projas-frontend)src/App.tsx
convbi-ai/FastAPI SQL-RAG sidecarmain.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, chatchapters 01–11 / 01–09
COOKIE_AUTH_IMPLEMENTATION_COMPLETE.mdCookie migration notesNot matching jwt.middleware.ts
LOGGING_GUIDE.md / LOGGING_IMPLEMENTATION_SUMMARY.mdInterceptor, SQL log, slow-query warnMatches logging.interceptor.ts
convbi-backend/.github/workflows/prod on main (3-min approval Arjun-ylogx), qa on qaECR convbi-backend, cluster convbi-cluster, service convbi-backend-service, region ap-south-1
convbi-backend/.aws/task-definition.jsonECS task: cpu 1024, memory 3072, port 5000Do 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

StepWhereLogic
Signupauth.service.ts signupEmail must include ORGANISATION_EMAIL domain. Hash bcrypt gensalt 10. User PENDING. SMTP OTP via nodemailer. 6-digit OTP Math.floor(100000 + random*900000).
ActivatePOST auth/activateVerify OTP + expiry. Status ACTIVE.
SigninPOST 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.
Expiryjwt.util.tsAccess: 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.
RefreshPOST auth/refresh-token excludedBody refresh token. Frontend useTokenRefresh + authService.initTokenRefresh default 15 min (VITE_TOKEN_REFRESH_INTERVAL). Single-flight refreshPromise. Failure → logout.
SPA gateApp.tsxlocalStorage.token. Public: /login /signup /forgot-password /reset-password /activateAccount/:token /forgetPassword/:token. Protected layout + ProtectedRoute per feature route. Admin nest under /dashboard/admin.
Roles enumuserRole.enum.tsSUPER_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:

  1. If no req.user, next().
  2. const transaction = await this.sequelize.transaction() — one connection held until HTTP finish.
  3. SUPER_ADMIN: RESET ROLE plus set_config('app.is_SUPER_ADMIN','true', true) (third arg true = transaction-local). organizationId GUC set empty. Full access. Store tx, next.
  4. 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.
  5. USER: load all non-deleted roles with RolePolicy→Policy. If a db_role_name exists, SET that first one only (“PostgreSQL can only have one active role”). Collect policy.policyRole, allowedModules, load ModuleRegistry.tables → req.user.allowedTables, roleModuleMapping. Else rls_no_access and noDataAccess=true.
  6. 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.

EndpointAlgorithm
GET tablesList tables the user may see (policy / SUPER_ADMIN). System tables gated.
GET …/columns-with-typesinformation_schema + hasTableAccess.
GET dataset/:t/fieldsMap 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/valuesDistinct values with slicer filters. Skip text/json/jsonb/array for “all distincts” (LIMIT 1000 per column).
POST dataset/:t/queryFiltered dataset for charts.
GET …/dataSELECT * FROM "table" on RLS tx. No LIMIT — failure class vs execute-query cap.
POST execute-queryReport-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-sqlSame executeRawSelectQuery as chatbot.
POST chat-querySee §7.

hasTableAccess

  1. Chat tables → true for authenticated.
  2. SUPER_ADMIN → all non-system tables.
  3. ADMIN → tables from current policy assignment helper.
  4. System tables → false for normal users.
  5. Else table must appear in getTables() for that user.

executeRawSelectQuery

  1. Trim, lower, must startsWith('select') — WITH at the front is rejected here; extractor wraps WITH as SELECT * FROM (… ) q first.
  2. Regex (?:from|join)\s+… collect table names (schema stripped, quotes stripped).
  3. hasTableAccess each table.
  4. If org filter needed, inject WHERE (alias."organizationId" = '…') AND before GROUP/ORDER/LIMIT/HAVING/UNION or append.
  5. 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):

IDPagesAPI
overview1GET /dashboards/overview
sales6GET /dashboards/sales
receivables5GET /dashboards/receivables (page, pageSize default 12 max 100)
payables3GET /dashboards/payables
stock2GET /dashboards/stocks
collections3GET /dashboards/collections
profitLoss1GET /dashboards/profit-loss
balanceSheet1GET /dashboards/balance-sheet
cashflow2GET /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)

  1. GUEST → 403. SUPER_ADMIN → no app clientFilter.
  2. If user.dbRoleNames length > 0, treat RLS as the row filter and skip application clientFilter; else getClientFilter(user).
  3. Parse month/year lists; default current month/year.
  4. Date predicates: EXTRACT(MONTH FROM col) IN (…) and year IN. Balance-sheet “as of” uses max year/month.
  5. 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)

  1. Data source: Utilitybar → GET queryDB/tables (RLS-visible).
  2. Joins + columns: INNER/LEFT/RIGHT/FULL OUTER, GET columns-with-types.
  3. Fetch: single table getTableData or POST execute-query (join SQL, LIMIT 1000–5000).
  4. Chart building: react-dnd DraggableColumn → ChartDropZone x/y → ElementRenderer → Chart.js (bar/line/pie/area/doughnut).
  5. 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

  1. POST /file-upload/tally multipart. Multer memoryStorage, max 2 files, 200MB each, .zip name + mime.
  2. validateFile: extension, size, mime, ZIP magic bytes PK (0x50 0x4b 03 04 / empty / spanned), AdmZip integrity, not empty.
  3. S3 Upload to tally-uploads/${Date.now()}-sanitizedName, URI s3://bucket/key. Region default ap-south-1.
  4. 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

PieceFileJob
Routersrc/App.tsxPublic auth + Sidebar outlet
Dashboardmodules/dashboard/App.tsx + predefined/*9 types, multi-page
Chatbotmodules/chatbot/App.tsx, api/chatbot.ts, api/chat.tsNL query + ChatContext CRUD
Report buildermodules/reportBuilder/*dnd + rnd + ElementRenderer
Uploadmodules/upload/TallyUploadPage.tsxTally zips
Adminmodules/admin/*, components/admin-page/*RBAC UI
FiltersDashboardFiltersContextmonths/years/currency
HTTPaxiosInterceptor 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

ServiceDockerfileRuntime
Backendnode:18.18.2-alpine3.18, npm --force, nest build, USER node, CMD node dist/main, EXPOSE 5000, curl for probesECS task cpu 1024 / memory 3072 / port 5000
FrontendVite build then nginx:stable-alpine :80Static SPA
AIpython:3.12-slim, gunicorn 8 workers, :80, timeout 180Sidecar; 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)

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)

ClassWhat in this treeWhat you say
Wrong-org JOINexecute-query can JOIN two tables; org AND only if organizationId column exists; RLS still on SET ROLEApp WHERE is extra. Isolation is the role. Missing org column + weak policy is the leak class.
Admin with no SYSTEM roleOld comment RESET ROLE; live rls_no_accessFail closed after a leak class.
LLM on tilesDashboards are SQL filesI would not put ChatOpenAI on overview. Bot is the NL path.
Hallucinated SQLPrefix SELECT, table regex, hasTableAccess, RLS, extractor WITH wrapModel proposes; Nest executes. Failed SQL returns message, empty chart.
executeWithoutRLS misuseChat metadata onlyWarehouse stays on req.dbTransaction.
Commit on HTTP finishFinishTransaction always commitDocs say rollback-on-error; file commits on finish. Would-do: inspect statusCode.
Pool leftovertx until finish, pool 20Slow handler holds a connection and a SET ROLE.
Cache without tenantfieldsCache keyed by table name onlyMetadata, 5 min, in-process. Resume Redis −35% is a different hop.
Sick ECS task/health pings Postgres; /health/simple does notALB should use the Terminus route.
FastAPI CORS *main.pySidecar should not be the public browser origin; Nest is the API.
SELECT * no limitgetTableDataReport path caps 5000; this GET does not.
axios 10sdashboard/adminChat uses fetch without that timeout; AI gunicorn timeout 180.
DNS / chatbot downBAD_GATEWAY if chatbot HTTP not okTiles still work; bot degrades.

19. Honest 10×

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 askYou answer (short)
1What did you build?Multi-tenant BI: Nest API + React SPA + FastAPI SQL RAG. Ylogx intern.
2Where is the entrypoint?backend src/main.ts; frontend src/App.tsx; AI main.py.
3What middleware order?CORS, 50mb body, ValidationPipe, FinishTransaction hook, Jwt, RLS, controller.
4What is on the JWT?guid, role, email, client_id, organizationId (plus isActive, passwordUpdatedAt at sign).
5Where is SET ROLE?rls.middleware.ts live use().
6Why rls_no_access?Fail closed; old RESET ROLE leaked.
7Why first db_role_name?Postgres one SET ROLE at a time.
8What is set_config third arg?true = local to transaction.
9SUPER_ADMIN path?RESET ROLE + flags; empty org GUC.
10ADMIN without SYSTEM role?rls_no_access.
11How allowedTables get filled?Policy.allowedModules → ModuleRegistry.tables.
12Is JWT org_id RLS?No. Role + GUC + policies.
13executeWithoutRLS?New tx, RESET ROLE, chat_* only.
14Chatbot URL?CHATBOT_API_URL or https://chatbot.projas.in/chatbot.
15Does FastAPI get the JWT?No.
16Model?If they open extractor: gpt-4.1 temp 0. Resume silent.
17Allow-listed tables?Ten names in extractor.py.
18WITH CTE?Extractor wraps as SELECT * FROM (with…) q so Nest startsWith select passes.
19How tables extracted from SQL?FROM/JOIN regex, strip schema/quotes.
20Org inject logic?If tableHasOrganizationId, splice WHERE.
21Join types?INNER LEFT RIGHT FULL JOIN enum.
22Row cap?execute-query 1000 default 5000 max. getTableData uncapped.
23Fields cache?Map 5 min, key fields:table, SHA1 etag.
24Is that Redis?No. Resume −35% is Redis on bot path.
25Dashboard LLM?No. Fixed SQL.
26How many dashboard types?9 in controller. Resume 30 tiles.
27Date filter algo?EXTRACT MONTH/YEAR IN lists; default now.
28When is clientFilter skipped?SUPER_ADMIN or dbRoleNames present (RLS mode).
29Receivables paging?page default 1, pageSize default 12 max 100.
30Chart.js vs Recharts?Canvas/chat: Chart.js ElementRenderer. Recharts on resume + dependency.
31Report save?POST /reports JSON blob, createdBy userId.
3250mb body?main.ts report payloads.
33react-dnd vs react-rnd?Columns dnd; canvas rnd 15px grid.
34Feature vs RLS?Feature = route in SPA. RLS = rows in Postgres.
35ADMIN all routes?No. FeatureAccess requires assignment except SUPER_ADMIN.
36Signup domain?email must include ORGANISATION_EMAIL.
37Hash?bcrypt 10. argon2 unused in src.
38Token storage?localStorage; signin prefixes Bearer.
39Cookie MD?Not jwt.middleware.ts in this checkout.
40Refresh interval?15 min default + on window focus.
41axios 401?Toast, clear token, /login.
42Chat fetch Authorization?Raw localStorage; already has Bearer from signin.
43Tally max size?Multer 200MB, 2 files; service also MAX_FILE_SIZE_MB default 500.
44Zip check?Magic bytes + AdmZip entries.
45S3 key?tally-uploads/timestamp-sanitized.
46Gateway timeout?30s axios.
47Pool size?max 20.
48synchronize?true in sequelize.module — would-do off in prod.
49SSL?POSTGRES_SSL true → rejectUnauthorized false.
50Health vs simple?DB ping vs process-up.
51Swagger?/api Bearer.
52Prod workflow trigger?push main.
53QA trigger?push qa, tag :qa, no approval.
54Who approves prod?Arjun-ylogx, 3 minute window.
55ECS names?convbi-cluster, convbi-backend-service, ap-south-1.
56Task cpu/mem?1024 / 3072 from task-def (no secrets).
57Backend USER?Dockerfile USER node.
58AI workers?gunicorn 8, timeout 180.
59Frontend nginx?SPA try_files, gzip, /health.
60CodeBuild?Latex only. Actions is the workflow file.
61Port 3000?Latex. Code 5000.
62Prisma?No. Sequelize.
63GraphQL?No. REST.
64Kafka?Not this intern.
65EKS?Not on the resume hop. ECS.
66Datadog?Not in this repo.
67LangSmith here?IQVIA.
68Gemini bot?Package exists; extractor is ChatOpenAI.
69Redux?Dependency; resume bullet is React+Recharts.
70FinishTransaction rollback?Only if commit throws.
71N+1 dashboards?Many sequential queries per type — would-do batch; no invented p95.
72GET data SELECT *?Yes, no limit.
73SQL injection org id?Interpolated; dates bound on execute-query.
74SET ROLE injection?Identifier interpolated; trust admin-created names.
75CORS * + credentials?Both Nest default and FastAPI.
76Logging redaction?Interceptor sanitizes passwords/tokens.
77Slow SQL?CustomLogger warn >1s when SQL logging on.
78Guest?Enum GUEST; blocked on dashboards/query/reports create.
79systemAccessibleTables?App tables list in applicationTables.ts.
80Warehouse in Sequelize?No models for sales/bs; raw SQL.
81schema validator?AI config vs live DB; auto_sync true; log and continue on ValueError.
82Pydantic fields?eventQuery, chatMessage, chartType, axes, series, dataPeriod, showChart, has_explicit_limit.
83temperature?0.
84Retry parser?BaseExtractor has retry parser; SQL extractor overrides to prompt|llm|parser.
85ChatContext?chats, currentChatId, messages, CRUD wrappers.
86normalizeChartType?architecture-docs/05 table.
87last-updated?GET /dashboards/last-updated?dashboard=
88User hierarchy?UserHierarchyModule — org/manager graph for admin.
89Module registry?Maps module guid → tables for policies.
90Overview admin?OverviewModule stats, not the KPI overview dashboard.
91Policy expression builder?Admin UI policy-form + expression-builder.
92DOMPurify?Frontend sanitizer dep — speak if they open package.json, not as RLS.
9310× first move?Pool/indexes/SELECT *; not shard; not LLM on tiles.
94What you did not own?Tally gateway internals, RDS patching, CloudFront distro JSON (resume edge, not these YAML files).
95Conv-BI GitHub name?Same intern product. Resume row is Ylogx.
96projas-frontend?npm name of the SPA.
97If they open IQVIA after this?Switch intern: Apr 2026 Hybrid RAG. Do not mix LangGraph into this answer.
98Default LP if unnamed?Dive Deep Redis+RLS (resume Redis + this RLS file), then Deliver Horizon 17th if they already heard Ylogx.
99How do you know +65%?Mechanism SQL RAG; PDF number; no protocol on resume.
100Would 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 / nameLives on
40%, 99.9%, +65%, 3 tiers, Redis −35%, 30 dashboards +60%, sub-210 ms, CloudFront/ALB/ECSYlogx intern
LangGraph, LangSmith, Azure AI Search, 200+ sites/BRDsIQVIA current
17th/80+, GStreamer 60 FPS, ZED, costmap −55%Horizon ERC
73→89% mAP, 24 FPS, 20+ camerasArgus
sub-300 ms, 99.5%, 3–10 min doubtsGiftedBooks
−30% iteration, 50+ models, Gemini RAG botsStratifyLabs — 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
JavaLive 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.