# Libretto

[![Go Version](https://img.shields.io/badge/Go-1.26-00ADD8?style=flat&logo=go)](https://go.dev/)
[![Built with templ](https://img.shields.io/badge/built%20with-templ-7C3AED)](https://templ.guide/)
[![Powered by Datastar](https://img.shields.io/badge/powered%20by-Datastar-3B82F6)](https://data-star.dev/)

A Total Cost of Ownership (TCO) calculator for tracking, analyzing, and
visualizing car expenses over time – Swiss-focused (LIK/CPI, MFK, cantons), with
a German/French/Italian/English UI. Go backend, server-rendered
[templ](https://templ.guide/) templates, and a reactive UI over Server-Sent
Events via [Datastar](https://data-star.dev/) – no SPA, minimal client JS. Ships
as a single self-hosted binary with embedded assets and an optional Windows
system tray; the same codebase compiles to a multi-tenant SaaS build.

## ✨ Features

- **Cost tracking & analysis**
  - Multi-car cost journals over a full category taxonomy (fuel, workshop,
    insurance, tax, vignette, parking, …), recurring charges, capital buy/sell events
  - Per-km/day costs **inflation-adjusted** (Swiss LIK CPI); EV-aware depreciation
    & residual value, imputed capital costs (TCS method), accident Wertminderung
  - Ranking, side-by-side comparison, per-car LLM cost narrative
- **Energy & fuel** – ICE/EV/PHEV dual-stream model (litres + kWh), self-calibrating
  consumption, managed charge sources for Lademix analytics
- **Maintenance, reminders & health**
  - Urgency-ranked service / MFK / Abgas / charge reminders with a driving-style slider
  - ~33-item maintenance catalog and EV battery State-of-Health tracking
- **Vehicle identity & documents** – Stammdaten (VIN/Neupreis/plate), per-car photos
  with body-style silhouettes, a car-spanning documents hub with offline glovebox
- **Charts (self-hosted ECharts)** – cost breakdown/timeline/comparison, efficiency
  & depreciation curves, sold-car retrospective, and a collector lens (value/equity,
  treemap, provenance, ownership Gantt); light/dark themed
- **Import & OCR** – invoice OCR (image/PDF → confirm modal → entry, line-item
  classified), Fahrzeugausweis + Serviceheft OCR, LLM-assisted CSV/Tankbuch import,
  CSV import/export
- **Fahrtenbuch** – odometer/GPS/manual trip log over SSE, work-hours
  auto-classification, per-client km (Spesenabrechnung) PDFs
- **Profiles, sharing & community**
  - Cookie-based local profiles; viewer/editor car sharing enforced server-side
  - Public garage showcase (`/g/<slug>`) with cost redaction + collector vanity
    subdomain; social graph (`/u/<handle>`, follow, `/feed`)
  - Shareable CHF/km story cards; ownership-milestone achievements
- **Internationalization** – DE/FR/IT/EN via go-i18n, Swiss vs. ISO formatting
- **Live & reactive** – CQRS + in-process event bus + SSE; the client never computes
  anything, minimal client JS
- **Design system "Editorial × Instrument"** – Playfair/Inter/IBM Plex Mono,
  self-hosted fonts + Lucide sprite + Datastar/ECharts (no CDNs), light/dark via CSS
  `light-dark()`, mobile bottom-tab bar + capture FAB
- **Operations & deployment**
  - Single self-contained binary; **local** (no-auth) or **SaaS** build at compile
    time; Windows tray; public `--demo` mode
  - Backup/restore + Litestream DR; data-rights export/erasure; opt-in
    password/passkey auth
  - SaaS adds per-tenant SQLite, an operator console, Wallee billing,
    activation-funnel + Grafana metrics

## 🏗️ Tech Stack

- **Backend**: Go 1.26 with Chi v5 router
- **Database**: SQLite (WAL, single pinned connection) via `modernc.org/sqlite`
  (pure Go, CGO-free); append-only numbered migrations, auto-applied on startup
- **Templates**: templ v0.3 (type-safe compiled Go templates)
- **Frontend**: Datastar – self-hosted browser bundle + `datastar-go` v1.2.2
  server SDK (all assets vendored under `/static`, no CDN)
- **Charts**: Apache ECharts, self-hosted as an ESM module and wrapped in a
  TypeScript web component
- **Server-side rendering**: `goldmark` (markdown), `tdewolff/canvas` (PNG share
  cards + PDF statements/reports)
- **i18n**: go-i18n (DE/FR/IT/EN)
- **OCR / LLM import**: Mistral (invoice OCR + CSV column detection), optional /
  per-profile BYOK
- **Build**: esbuild (TypeScript) + Task runner + air (hot reload)
- **Architecture**: CQRS + in-process pub/sub event bus, SSE reactive loop
- **Desktop**: Windows system-tray build (`fyne.io/systray`); single
  self-contained binary with embedded assets
- **SaaS build** (`saas` tag): per-tenant SQLite + control plane, embedded
  Litestream (S3) replication, Wallee billing

## 🚀 Quick Start

```bash
# Clone and build
git clone <repo-url>
cd libretto
task build

# Run server
./bin/libretto

# Or with sample data
task seed

# Or use hot reload for development
task live
```

Server starts on `http://localhost:8088` (opens automatically).
With `--seed`: 11 sample vehicles pre-loaded (classic + modern, e.g. Peugeot 306
Cabriolet, Saab 900 Turbo, BMW E30 325i Cabriolet, Alpina B3 Biturbo Touring).

> Note: the build has a mandatory codegen step. Always go through `task`, or
> replicate its order (`build-js` → `generate` → `go build`).

See [USAGE.md](USAGE.md) for detailed instructions.

## 💻 Development

### Prerequisites

```bash
# Go 1.26+
# Node.js 22+ (for TypeScript compilation)
# Task runner (optional but recommended)
go install github.com/go-task/task/v3/cmd/task@latest
```

### Available Tasks

```bash
task --list                  # Show all available tasks

task install                 # Install npm dependencies
task build-js                # Bundle web/resources/_main.ts (+ imports) -> static/main.js
task generate                # Generate *_templ.go from *.templ
task build                   # Build full application (JS + templ + Go)
task run                     # Build and run
task live                    # Hot reload development mode (air)
task test                    # Run tests
task ci                      # Full CI pipeline locally (run before push/release)
task prod                    # Build with version from `git describe`
task release-local           # Cross-compile all platforms into dist/
task clean                   # Clean build artifacts
```

Generated files must not be hand-edited: `*_templ.go` (from `.templ`) and
`web/resources/static/main.js{,.map}` (esbuild-bundled from the `web/resources/*.ts`
sources via the `_main.ts` entry). Edit the source, then run `task generate` /
`task build-js`.

### Development Workflow

**Hot Reload Mode** (recommended):
```bash
task live
```

This starts air with automatic rebuilds when `.go`, `.templ`, or `.ts` files change.

**Manual Build**:
```bash
task build
./bin/libretto --port 8088
```

### Project Structure

```
cmd/web/           # Application entry point (+ Windows fatal-dialog variant)
config/            # Config loading + build-tagged port/mode (local / saas / windows)
internal/
  auth/            # Profile auth: Auth interface, LocalAuth, middleware
  db/              # Database layer, migrations, seed + demo sample data
  eventbus/        # In-process pub/sub event bus
  i18n/            # go-i18n messages (DE/FR/IT/EN) + formatting
  model/           # Domain models (entry-kind / cost-category taxonomy, catalog)
  settings/        # Per-profile key/value settings store
  assets/          # User asset storage (blob / S3 store)
  billing/         # Wallee billing & subscriptions (saas)
  community/       # Social graph projector + derived community.db (saas)
  backup/          # Embedded Litestream replication + restore
  tenant/          # Per-tenant DB pool, merge/import engine (saas)
  tray/            # Windows system tray (no-op on other platforms)
  ui/              # Shared chrome (layout, nav, Icon sprite helper)
features/
  tco/             # TCO feature module
    analysis.go    # TCO calculation engine (inflation-adjusted)
    queries.go     # Read-side operations & car ranking
    commands.go    # Write-side operations (CRUD)
    handlers.go    # HTTP handlers, SSE loop, TCO wizard
    ocr_handler.go # Invoice OCR via Mistral
    llm_import.go  # LLM-assisted CSV / Tankbuch column detection
    import.go      # CSV/TSV import (+ export.go for CSV export)
    inflation.go   # Swiss LIK CPI table
    routes.go      # Route registration
    components/    # templ fragments (modals, forms, tables)
    pages/         # templ full pages + view helpers
  settings/        # Settings UI feature (pages + handlers)
  showcase/        # Public garage, per-car detail, social feed (saas)
router/            # Router setup & static file serving
web/               # Embedded static assets (//go:embed)
  resources/*.ts           # Client TS (chart, passkeys, intake, dialogs) -> _main.ts entry
  resources/static/vendor/ # Vendored Datastar + ECharts (no CDN)
```

### Architecture

**CQRS Pattern**:
- Commands: write operations return `204 No Content` and publish domain events
- Queries: read operations with TCO analysis (pure functions, no DB)
- Events: in-process pub/sub distributes changes to all subscribers

**SSE Updates**:
- A long-lived SSE connection per tab subscribes to the event bus
- On domain events it re-runs the relevant queries/analysis, renders templ
  fragments, and patches the DOM via the Datastar SSE helper
- `ui.*` events drive modals/forms; domain events (`car.*`, `entry.*`) are
  buffered briefly so a reconnecting tab doesn't miss updates

### Routing

Routes are registered per feature. Rather than duplicate a list that drifts, see
the source of truth: [router/router.go](router/router.go) wires profile/static
routes and the `/api/sse` live stream, then delegates to
[features/tco/routes.go](features/tco/routes.go),
[features/settings/routes.go](features/settings/routes.go), and
[features/showcase/routes.go](features/showcase/routes.go) (public garage +
social graph, saas).

### Database

SQLite with WAL mode. Schema auto-migrates on startup via an append-only numbered
migration slice (never edit or reorder existing entries).

**Seed Data** (`task seed`): 11 sample vehicles. `--demo` runs a public instance
that periodically resets to this sample set.

## Roadmap

For code-grounded implementation status (and the longer-term strategy) see
[ROADMAP.md](ROADMAP.md) – the single source of truth for what is built vs.
queued.

### ✅ Shipped (highlights)
- CQRS + event bus + SSE reactive UI; SQLite (WAL) with append-only migrations
- TCO engine with running metrics, inflation-adjusted (Swiss LIK CPI); depreciation/
  residual value, imputed capital costs, accident Wertminderung, cost narrative
- Cost-category taxonomy + recurring charges; ICE/EV/PHEV energy model; reminders,
  maintenance catalog, EV battery health
- Cockpit dashboard, vehicle identity & imagery, documents hub, tabbed car detail,
  mobile UX overhaul, i18n (DE/FR/IT/EN)
- Charts + collector-lens visualization (value/equity, retrospective, treemap,
  provenance, ownership Gantt)
- Invoice / Ausweis / Serviceheft OCR (image/PDF, line-item classification); LLM-assisted
  CSV / Tankbuch import; CSV import/export
- Fahrtenbuch trip log + per-client mileage statements; share cards; achievements
- Local profiles + opt-in password/passkey auth; car sharing; MFK/Abgas tracking; settings UI
- Backup/restore + Litestream DR; data-rights export & erasure; per-car photos
- Windows system tray + browser auto-open; public demo mode (`--demo`)
- SaaS build: per-tenant SQLite + control plane, operator console, Wallee billing,
  public garage showcase + social graph, activation-funnel + Grafana metrics

### 🚧 Next up
- §36 verify the Wallee merchant-initiated (MIT) renewal charge live · §68/§69
  activation polish (free welcome scan, OCR-cap upgrade modal)
- §72 PDF export (report + appraisal dossier) · §42 RBAC (owner/staff seats) for
  Dealer/Fleet · §19 connected vehicle data
