Skip to content

What is Scani

Scani is a self-hostable, open-source portfolio tracker for crypto and traditional assets. One database, one ledger, one dashboard across:

  • Exchanges — Binance, Kraken, Bybit, OKX, Coinbase, KuCoin, Gate.io, HTX (Huobi), Bitstamp, Bitget, Gemini, MEXC.
  • Brokerages & banks — Interactive Brokers (via Flex Web Service), Wise.
  • On-chain wallets — Ethereum and every EVM chain Etherscan V2 supports (Polygon, Arbitrum, Optimism, Base, …), Solana (via Helius), Bitcoin, Tron, TON, plus ENS resolution.
  • Pricing — CoinGecko, Finnhub, DeFiLlama, Frankfurter (FX), Yahoo Finance, and Google Sheets for manual-asset prices.
  • AI-assisted import — OpenAI for screenshot parsing; Perplexity and DeepSeek for token-identity backfill.
  • Anything else — manual holdings with manual prices. Home equity, private-company shares, an off-grid commodity position, a friend’s IOU.

The whole stack is a few Bun services and a Postgres database. The default local stack — bun run dev:stack — boots Postgres, Redis, MinIO, Mailpit, and every Scani service in Docker. No external account, no API key, no credit card is required to log in and start using it.

Provider API keys (CoinGecko, OpenAI, Etherscan, …) unlock specific integrations when you want them. Without them, manual holdings still work, FX pricing still works (the FX provider has no key requirement), and the screenshot store still works (local MinIO container).

Scani is structured around a small set of domain concepts that map cleanly to finance:

  • A holding is a single (account, token) position with a balance.
  • An account is a container for holdings at one institution — your Kraken account, your Metamask wallet, your IBKR brokerage account, your “manual” pseudo-account.
  • An institution is the financial entity behind the account: an exchange, a bank, a brokerage, a blockchain, or a synthetic “manual” institution Scani creates for offline data.
  • A token is a tradeable asset: a fiat currency, a cryptocurrency, an equity, or a private company.
  • A transaction is one immutable event in an append-only ledger — a buy, a sell, a deposit, a withdrawal, a transfer, a swap, a fee, a reward, an interest payout, an airdrop.
  • A balance observation is a snapshot of a holding’s balance at a moment in time — what a sync captured, what a screenshot said, what you typed in.
  • A vault is a savings goal you allocate fractions of holdings against.
  • A group is a user-defined tag for organising holdings and accounts.

These concepts are the same whether you’re looking at a Binance position or a private-equity stake. That symmetry is the design — see Mental model for the one-pager.

  • Not an exchange. Scani has no order book, no custody, no settlement. Read-only credentials and read-only blockchain RPCs are how it reads your portfolio.
  • Not a tax engine. Cost basis, realised PnL, and FIFO/LIFO lot selection are tracked (see Portfolio value rollup), but Scani does not file returns or render jurisdiction-specific tax reports.
  • Not a market-data terminal. Pricing is for portfolio valuation, not high-frequency trading. Intra-day prices land on the minute scale, not the millisecond.
  • Not a SaaS-first product. A managed tier exists (Tier 3), but the design centre is single-tenant self-hosted. Everything else follows from that.

A four-service Bun monorepo plus a database:

ServiceWhat it does
apps/backend/apitRPC + Elysia HTTP server. Owns per-user credentialed integrations (exchange keys, brokerage tokens) so user creds never leave the tenant boundary. Acts as the BullMQ producer.
apps/backend/workerBullMQ consumer. Every scheduled job (pricing, balance syncs, historical backfill, transfer linking) and every user-initiated job (screenshot parse, import, delete) runs here.
apps/backend/data-providertRPC service that centralises every outbound third-party call (CoinGecko, OpenAI, Etherscan, …). The api and worker call it over tRPC rather than talking to upstream APIs directly. The seam between Tier 1 and Tier 2/3 lives here.
apps/frontend/appReact + Vite SPA. End-to-end type-safe with the api via tRPC.
Postgres + Redis + S3Postgres for everything durable. Redis for BullMQ, rate-limiter buckets, realtime fan-out. S3 (or compatible) for binary uploads.

See Repo layout for the package-by-package map.

Most portfolio trackers are SaaS-only and crypto-only (or stocks-only). Scani exists because a useful portfolio tool has to:

  1. Cover every venue you actually use, custodial or not, crypto or not, with or without an API.
  2. Run on your hardware, with your keys, against your database. The ledger of every trade you’ve ever made is sensitive — sending it to a third party should be a choice, not a precondition.
  3. Survive incomplete data. Exchange CSVs start at export date. Blockchain indexers prune. Wallets get imported mid-life. The model has to make the headline number reconcile with the chart even when parts of the history are missing — see Observations & coverage.

The trade-off is more setup than a SaaS sign-up. The docs you’re reading exist to make that setup tractable.

  • Quickstart — the one-command local stack.
  • Mental model — the one-pager for the domain model.
  • Tier model — choosing how much you want to host yourself.
  • Glossary — every term used in these docs.