# Lakshmi — Systems Deep Dive

> The `apps/lakshmi/` area: six Nx applications that make up the **service tier
> of the Lakshmi personal-finance-intelligence platform** — an HTTP/WebSocket
> gateway, a BullMQ worker and scheduler pair, two health-serving service shells
> (sync-engine, ai-agents), and a Manifest V3 browser extension.

## What this area is

Lakshmi is Oshun's personal-finance-intelligence product. `apps/lakshmi/`
contains its runnable services — not domain libraries, but the deployable
process boundaries that front, schedule, and process the platform's finance
workloads. The root `apps/lakshmi/README.md` enumerates five services
(`api-gateway`, `sync-engine`, `ai-agents`, `worker`, `scheduler`) plus their
ports; a sixth tracked project, the `browser-extension`, is a client surface
rather than a server.

The honest split across the area is "real infrastructure, deferred domain
logic." Three concerns are fully implemented: the gateway's edge plumbing (auth,
RBAC, OAuth scope enforcement, rate limiting, OpenAPI generation, Kafka
publishing, WebSocket pub/sub), the worker's BullMQ queue/retry/DLQ machinery,
and the scheduler's repeatable-job registration. What is deliberately _not_ yet
implemented is the finance domain work those shells would call: the gateway's
`/v1` route handlers return empty placeholder payloads, and the worker's job
processors log their phases and advance progress but stop short of real
provider/DB/ML calls. Both are explicit in the source — handlers carry comments
like `// Domain service call will be implemented in @lakshmi/accounts library`
and processors carry `// Real implementation: ...` markers. No `@lakshmi/*`
domain library exists in this area yet, so those are forward references, not
present dependencies.

The two remaining services, `sync-engine` and `ai-agents`, are the thinnest:
each is a single-file Node `http` server that answers `/health` and `/ready`
only. `ai-agents` is candid about this in its own module docstring ("the
financial agents themselves ... are NOT yet implemented in this service — this
process currently serves health checks only").

## How the area is shaped

The services are wired together through two shared pieces of infrastructure
rather than through direct imports:

- **Redis + BullMQ** is the job spine. `lakshmi-scheduler` registers five
  repeatable jobs onto five named queues (`lakshmi:account-sync`,
  `lakshmi:transaction-categorize`, `lakshmi:ai-recommendation`,
  `lakshmi:report-generation`, `lakshmi:data-export`); `lakshmi-worker` defines
  those same queues with retry/back-off policies and DLQs and runs a worker per
  queue. The queue-name constants are duplicated in both
  `scheduler/src/schedules.ts` and `worker/src/queues.ts`, which is how the two
  processes meet — the scheduler produces, the worker consumes.
- **Kafka** is the event spine for the gateway. `api-gateway/src/kafka.ts`
  provides a typed, idempotent `LakshmiEventBus` (kafkajs, LZ4 compression,
  per-`userId` partition keys) for domain events, with the gateway running in a
  documented degraded mode if Kafka is unreachable at boot.

The gateway is the only service with a substantial internal structure
(`middleware/`, `public-rest-api.ts`, `websocket.ts`, `kafka.ts`, `events.ts`);
the others are one or two source files each. All six share the same Nx target
shape (`build`/`lint`/`test`/`typecheck`, with `serve` on the servers and `e2e`
on the extension) and the `scope:lakshmi`, `type:app`, `lakshmi:tier:app` tags.

## How it fits the wider system

These are leaf applications: nothing else in the monorepo imports them, and
within the area they depend on each other only indirectly (through Redis queue
names and Kafka topics, never through TypeScript imports). The gateway is the
public front door — it serves the v1 REST surface, an OpenAPI 3.1 document, an
OAuth authorization-server metadata document, and the `/v1/ws` WebSocket
upgrade. The browser extension is a separate client artifact that ships as a
Chrome Manifest V3 bundle. The real external boundaries are downstream of the
code present here: open-banking providers (Plaid/Yodlee/MX/Finicity/Tink),
foundation- model providers (OpenAI/Anthropic/Google), object storage (MinIO
buckets), and the not-yet-created `@lakshmi/*` domain libraries the handlers and
processors are written to delegate into.

## Entity reference

### lakshmi-api-gateway

The Hono HTTP/WebSocket front door (`apps/lakshmi/api-gateway`), and by far the
most complete service in the area. Its edge layer is real and production-grade:
JWT verification via `jose` (HS256) with subscription-tier RBAC
(`free`/`premium`/`family` → permission sets) and per-route OAuth scope
enforcement in `src/middleware/auth.ts`; Redis-backed rate limiting; a
machine-generated OpenAPI 3.1 document and OAuth authorization-server metadata
built from a 44-endpoint catalog in `src/public-rest-api.ts`; a typed idempotent
Kafka producer in `src/kafka.ts`; and a room-based WebSocket manager in
`src/websocket.ts`. The 44 `/v1` route handlers in `src/app.ts`, however, are
intentionally hollow — they validate input with Zod, enforce auth/scopes, then
return empty placeholder payloads (e.g. `c.json({ accounts: [] })`) annotated
with `// Domain service call will be implemented in @lakshmi/<domain> library`.
So: the gateway _is_ a working, secured, observable API surface, but the finance
data behind it is not yet wired.

### lakshmi-sync-engine

A service shell for account synchronization (`apps/lakshmi/sync-engine`). Its
module docstring describes orchestrating open-banking provider polling (Plaid,
Yodlee, MX, Finicity, Tink), transaction ingestion, and balance refresh, but the
implemented code in `src/index.ts` is only a Node `http` server that answers
`/health` and `/ready`. The provider list it reports is a static array inside
`createSyncEngineHealthPayload`, and the sync interval/concurrency are read from
env vars — there is no polling, ingestion, or Kafka publishing in the service
yet. Honest status: a health-serving scaffold awaiting the actual sync
implementation.

### lakshmi-ai-agents

An AI financial-agent service shell (`apps/lakshmi/ai-agents`), and the most
self-honest scaffold in the area — its `src/index.ts` docstring states the
agents (scenario modeling, recommendations, NL query, bill negotiation, tax
advisory) are "NOT yet implemented in this service — this process currently
serves health checks only." What _is_ real is `resolveConfiguredModelProviders`,
which inspects the environment (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, and
Google/Gemini keys) and reports the subset of foundation-model providers
actually configured, rather than asserting connections that may not exist. The
health payload also surfaces the configured embedding model and agent
concurrency. Beyond `/health` and `/ready`, there is no agent logic here.

### lakshmi-worker

The BullMQ background-job processor (`apps/lakshmi/worker`). The queue
infrastructure in `src/queues.ts` is real and substantial: five typed queues
plus five matching dead-letter queues, per-queue retry counts and back-off
strategies (exponential for sync/categorize/AI, fixed for report/export), typed
enqueue helpers with deduplicating job IDs, a `QueueEvents`-based DLQ forwarder
that moves permanently-failed jobs (attempts exhausted) into the DLQ with
failure metadata, and a typed `createWorker` factory wrapping processors in
structured start/complete/fail logging. `src/index.ts` boots one worker per
queue at domain-specific concurrencies with a graceful shutdown that drains
workers, events, and queues. The five job _processors_ (`processAccountSync`,
`processTransactionCategorize`, `processAiRecommendation`,
`processReportGeneration`, `processDataExport`) are the scaffolded part: they
log each phase and call `job.updateProgress(...)` but replace the actual work
with `// Real implementation: ...` comments referencing `@lakshmi/integrations`,
`@lakshmi/db`, embeddings, and MinIO. So the BullMQ runtime is genuine; the
finance work inside the jobs is deferred.

### lakshmi-scheduler

The BullMQ repeatable-job scheduler (`apps/lakshmi/scheduler`), and — alongside
the worker's queue layer — one of the genuinely functional pieces of the area.
`src/schedules.ts` builds five recurring tasks against the same five queue names
the worker consumes, each with a real repeat policy (interval-based for the
account-sync and categorization sweeps, cron-based for daily AI recommendations,
daily report rollups, and hourly export maintenance) and env-overridable timing
via `getSchedulerConfigFromEnv`. `src/index.ts` registers them with
`queue.upsertJobScheduler`, which is a live Redis operation, and exposes a
health endpoint reporting the registered task count, last registration time, and
state. The data each scheduled job carries is system-level seed data (e.g.
`userId: 'system'`, `connectionId: 'due-connections'`) since the worker side
that would fan these out per-user is not yet implemented, but the scheduling
mechanism itself is real and complete.

### lakshmi-browser-extension

A Chrome Manifest V3 browser extension (`apps/lakshmi/browser-extension`) that
shows budget/price/coupon/cashback context while shopping. Several parts are
real: `src/shared/shopping-context.js` is a genuine client-side analysis library
(budget arithmetic, coupon ranking by estimated savings, best-offer and best-
cashback selection, currency formatting, and price-text parsing); the
`background/service-worker.js` handles install-time storage defaults and a
`runtime.onMessage` settings query; and `scripts/build.js` packages `src/` into
`dist/chrome/` while validating that the manifest is MV3 with a popup and
content scripts (`scripts/validate-extension.js` is wired as the `typecheck`
target, and Playwright as `e2e`). The honest caveat is the data and the injected
overlay: `DEFAULT_PROFILE` is sample/seed data (example merchants like
"Threadline"), and the injected `content/content-script.js` overlay uses
hardcoded sample figures (spent 34000, budget 40000, a fixed best price) and
does _not_ yet call the shared `analyzeShoppingContext` analyzer or any live
account data. So the extension is a working MV3 package with a real analysis
module, but its on-page experience is currently a demo wired to placeholder
numbers.
