# Splint — Contributor's Guide for AI Agents

## What Splint is

A Tricount/Splitwise clone. Users create "ledgers" (shared expense groups), add expenses and transfers, and the app computes the minimum number of settlement payments to balance everyone.

## Key architecture decisions

- **No user accounts.** Ledgers accessed via secret-protected invite links (pastebin model). No sign-up, no auth.
- **E2EE.** All data encrypted client-side (AES-256-GCM). Server only sees ciphertext. Encryption key shared via URL fragment.
- **Offline-first.** Local storage via Dexie.js (IndexedDB). Works without internet, syncs when online.
- **Min-cash-flow algorithm.** Greedy settlement: match largest creditor with largest debtor until all settled.

## Tech stack

- **Language:** TypeScript (strict mode)
- **Runtime:** Bun
- **Database:** Postgres (Drizzle ORM)
- **Server:** Hono
- **Web:** React SPA (Vite + React Router + shadcn/ui components)
- **Graph viz:** graphology + sigma.js
- **Lint/format:** Biome
- **Container:** Podman Compose

## Project structure

```
├── packages/
│   ├── shared/          # Types, E2EE crypto, settlement algorithm
│   ├── server/          # Bun + Hono API + Drizzle ORM
│   └── web/             # React SPA (Vite)
├── compose/             # Podman Compose (dev + prod) + Containerfile
├── scripts/             # Build scripts
├── docs/                # Plans and docs
└── Justfile             # Task runner (build, test, lint, deploy, backup)
```

## Conventions

### Component architecture (web/)

- **LedgerView** is a thin orchestrator. Sub-components own their own form state.
- **Data flow:** LedgerView loads + decrypts → passes data to sub-components via props.
- **Form state** lives as close to the consumer as possible. Modals use key-remount to reset.
- **Styling:** Tailwind CSS v4 utility classes + shadcn/ui components. Dark theme only. Use `cn()` from `lib/utils.ts` for class merging.

### Git workflow

- Use conventional commits: `feat:`, `fix:`, `chore:`, `docs:`

### Code quality

- TypeScript strict mode across all packages
- Biome for linting and formatting (`biome check .`)
- Prefer explicit types over `any`
- Error handling: use `SplintError` class with `ErrorKind` enum for classified errors

## Development setup

```bash
# Prerequisites: Bun, Podman + podman-compose

# Install dependencies
bun install

-# Create environment config
-cp env/.env.dev.example env/.env   # Edit with your values

-# Start everything (Postgres, migrations, server, Vite)
-just run

-# Run database migrations manually (optional)
-just migrate

-# Stop everything
-just stop

# Run tests
bun test

# Lint and format
biome check .
biome format --write .
```

## Common tasks

```bash
just run          # Start everything (Postgres, migrations, server, Vite)
just stop         # Stop all dev services and processes
just migrate      # Run database migrations manually
just build        # Build for current ENV_MODE
just test         # Run all tests
just typecheck    # TypeScript type checking
just lint         # Biome lint + format
just backup       # Dump database backup
```
