Repo layout
Bun workspaces monorepo. Apps split into backend/ and
frontend/; packages split into business/, infra/, clients/,
and frontend/.
Top-level shape
Section titled “Top-level shape”Directoryapps/
Directorybackend/
Directoryapi/ tRPC + Elysia HTTP server. BullMQ producer.
- …
Directoryworker/ BullMQ consumer. Every async job runs here.
- …
Directorydata-provider/ Centralised outbound 3rd-party calls.
- …
Directoryfrontend/
Directoryapp/ React + Vite SPA.
- …
Directorydocs/ This Starlight docs site.
- …
Directorypackages/
Directorybusiness/ Domain logic + the wire contract.
Directorydomain/ Services, repositories, use cases.
- …
Directoryjobs/ Async-job catalog: descriptors, schedules.
- …
Directoryshared/ Frontend-safe contract (zod DTOs, Decimal).
- …
Directoryinfra/ System concerns. No business knowledge.
Directorydb/ Drizzle schema, migrations, BaseRepository.
- …
Directoryqueue/ BullMQ framework (queue, worker, scheduler).
- …
Directoryemail/ Fastmail JMAP / SMTP.
- …
Directorylogging/ Structured logging (pino).
- …
Directorysecurity/ AES-256-GCM credential encryption.
- …
Directorystorage/ S3-compatible object storage.
- …
Directoryrealtime/ SSE / Redis pub-sub.
- …
Directoryrate-limiter/ Per-provider rate limit + circuit breakers.
- …
Directoryconfig/ Env-validation primitives.
- …
Directoryclients/ Outbound network adapters.
Directoryproviders/ Unified 3rd-party integration package.
- …
Directorycloud-client/ Typed tRPC client for the data-provider.
- …
Directoryfrontend/
Directoryui/ Design system + shared client plumbing (@scani/ui).
- …
Directoryscripts/ Workspace scripts (sync-env, …).
- …
- docker-compose.yml Local dev stack.
- docker-compose.prod.yml Production-flavour compose.
- .env.example Annotated env vars.
- CLAUDE.md Canonical engineering spec.
- CONTRIBUTING.md Contributor on-ramp.
Backend apps
Section titled “Backend apps”apps/backend/api
Section titled “apps/backend/api”tRPC over Elysia. Owns per-user credentialed integrations (exchange API keys, brokerage tokens). The api decrypts and uses those credentials on its own machine; they never leave the tenant boundary. Acts as the BullMQ producer — every user action that needs async work enqueues into Postgres from here.
Routes live in apps/backend/api/src/presentation/routers/.
apps/backend/worker
Section titled “apps/backend/worker”The BullMQ consumer. Every scheduled job (pricing, balance syncs, historical backfills, transfer linking, APY payouts, …) and every user-initiated job (screenshot parse, exchange import, wallet import, file import, …) runs here in one binary.
Processors live in apps/backend/worker/src/processors/. The
cron-lock helper is in apps/backend/worker/src/lib/cron-lock.ts.
apps/backend/data-provider
Section titled “apps/backend/data-provider”tRPC service fronting the Scani-owned third-party calls that a
hosted deployment can serve on your behalf: object storage, the
email transport, Open Graph metadata and token search. This is
the seam between tiers — in Tier 1
it’s a sidecar on localhost:8082; in Tier 2/3 it’s a hosted
endpoint.
It also exposes pricing.*, ai.* and chains.* routers, but the
api and worker do not call them: all three services boot the
provider registry in direct mode and reach CoinGecko, Finnhub,
DeFiLlama, OpenAI, Etherscan and Helius themselves, with keys from
their own environment.
Routers live in apps/backend/data-provider/src/presentation/.
Frontend apps
Section titled “Frontend apps”apps/frontend/app
Section titled “apps/frontend/app”The main React + Vite SPA. End-to-end type-safe with the api via
tRPC. Source under src/v2/. Locales under src/i18n/locales/.
apps/frontend/docs
Section titled “apps/frontend/docs”This site. Astro + Starlight. Pages under
src/content/docs/. Custom CSS in src/styles/custom.css.
scripts/generate-llms-txt.ts runs before astro build to emit
public/llms.txt and public/llms-full.txt.
Packages — business/
Section titled “Packages — business/”Things that change when the product changes.
@scani/domain (packages/business/domain)
Section titled “@scani/domain (packages/business/domain)”The bulk of business logic — services, repositories, use cases.
Class-field DI (see DI pattern).
Tests in mirrored tests/ directories.
@scani/jobs (packages/business/jobs)
Section titled “@scani/jobs (packages/business/jobs)”Per-job descriptors (payload schemas, retry policies, jobId
strategies, summarisers), repeatable schedules, and the
@scani/queue mirror/lock impls. The wire-name catalogue is
job-names.ts.
@scani/shared (packages/business/shared)
Section titled “@scani/shared (packages/business/shared)”Frontend-safe contract: zod DTOs (the tRPC wire), the
project-configured Decimal.js instance, UI helpers
(formatCurrency, formatRelative, emailSchema, …). Strict
rule: no Node-only APIs reachable from the barrel. The frontend
must be able to import any of it.
Packages — infra/
Section titled “Packages — infra/”Pure system concerns. No business knowledge; reusable in any TypeScript backend.
Packages — clients/
Section titled “Packages — clients/”Outbound network adapters.
@scani/providers (packages/clients/providers)
Section titled “@scani/providers (packages/clients/providers)”The unified 3rd-party integration package. One directory per
provider under src/providers/ (CoinGecko, DeFiLlama, Kraken,
Binance, IBKR, Wise, OpenAI, Solana, …). Capability-based
interfaces (PriceProvider, BalanceProvider, TransactionProvider,
TokenIdentityProvider, AIProvider).
See Adding a provider and Provider matrix.
@scani/cloud-client (packages/clients/cloud-client)
Section titled “@scani/cloud-client (packages/clients/cloud-client)”Typed tRPC client for the data-provider. The api + worker call the data-provider through this rather than reaching for HTTP directly.
Packages — frontend/
Section titled “Packages — frontend/”@scani/ui (packages/frontend/ui)
Section titled “@scani/ui (packages/frontend/ui)”Design system + shared client plumbing for the SPA. Tailwind
preset + CSS tokens, the full shadcn primitive set,
ThemeContext, ErrorBoundary, UpdateBanner, MagicCodeInput,
the useAppUpdate hook, PWA helpers, createScaniAuthClient /
createTrpcProvider factories.
apps/frontend/app is the canonical source of truth for
shared primitives — when promoting a new primitive into the
library, copy from there.