Anthony Carbonaro

Current State

# Current State

Last verified against code: 2026-04-18

Deployment: live at [llatria.carbonaromedia.com](https://llatria.carbonaromedia.com)

This document describes what Llatria actually is today — not what the original design docs said it would be. It is written to be honest about limitations so readers have an accurate mental model.

---

## What's live

A working prediction-market browsing and trading application, running in alpha on a single Hetzner VM in Germany.

- [llatria.carbonaromedia.com](https://llatria.carbonaromedia.com) — frontend

- `/api/v1/*` — backend

- Magic-link authentication via Resend email

- TLS via Caddy with automatic Let's Encrypt certificates

Active resources as of this writing:

| | Count |

|---|---|

| Llatria-native markets (all `ACTIVE` state) | 13 |

| Kalshi markets cached for browsing | ~30,000 |

| Curve trades executed | 258 |

| Registered users | 7 |

All Llatria markets are still in the `ACTIVE` state — no market has reached the $10k graduation threshold, so no market has transitioned to `GRADUATING` or `GRADUATED`. The graduation path exists in code but has not run against production data.

---

## What works end-to-end

### Browse

Homepage shows two sections: "New on Llatria" (creator-launched bonding-curve markets) and "Graduated / On the books" (Kalshi-catalog markets). Each section is organized into categories (Politics, Sports, Finance, Culture, Climate & Science, World, Local, Other). Category tiles support pagination and sort by volume or close time.

Cards on the homepage show YES price, 24-hour volume, close time, and a 24-hour sparkline. Sparklines for Kalshi markets come from a 15-minute background sync worker; Llatria sparklines are generated from the actual curve-trade history via a separate 15-minute refresh loop.

### Search

Full-text search across both Llatria-native markets and Kalshi's catalog, with autocomplete typeahead in the header and a full results page at `/search`. Uses Postgres `tsvector` with GIN indexes; no external search service.

### Auth

Magic-link login only. Flow: user submits email → server generates token, sends link via Resend → user clicks link → server sets HMAC-signed session cookie with 14-day expiry. No passwords, no OAuth, no KYC at this stage.

### Market creation

Any authenticated user can create a market from `/markets/new`. The form collects question, settlement rules (free-text description), category, resolution source, close time (up to 2 years out), initial probability (1–99%), and seed amount ($10–$50). As of commit `8f58b20`, the form shows a live preview of the resulting pool depth and warns when extreme probabilities would make the market effectively un-tradeable.

On submit, the creator's Mock balance is debited, a row is inserted into `markets`, a pool is initialized in `curve_pools`, and a `market.created` event is appended to the `event_log` — all in one transaction.

### Trading

`TradePanel` on the market detail page supports:

- Buying either side by dollar amount (fees shown in advance)

- Selling a full position (partial sells not yet supported)

- Real-time quote fetch (debounced 250ms) as the user adjusts inputs

- Idempotency-safe execution — a single `idempotency_key` per "trade intent" is held stable across button clicks, so double-submits deduplicate server-side

- Server returns the updated pool, which is optimistically merged into the UI

The CPMM math runs server-side in `math/big` — all swap calculations and collateralization invariants are exact integer arithmetic. Every trade is recorded in `curve_trades` with a full JSONB snapshot of the pool before and after, for forensic audit.

### Real-time updates

When a trade executes, the market service publishes a JSON event through an in-process pub/sub broker to all Server-Sent Event subscribers watching that market. Open tabs on the market detail page receive the push within milliseconds — no polling. Heartbeat every 30 seconds keeps proxies from closing idle streams. EventSource auto-reconnects on disconnect and pulls a fresh snapshot when the connection is re-established.

### Charts

The market detail page has a hand-rolled SVG line chart with five range options (1h / 24h / 7d / 30d / all). Points come from a server-side endpoint that buckets `curve_trades` rows by time window and returns closing price per bucket. Hover shows a vertical guide and tooltip; screen-reader ARIA summary describes price trajectory.

### Dashboard

`/me` shows the signed-in user their created markets and their open positions (both YES and NO, joined with market metadata).

### Docs

`/about` is a four-tab documentation page (About, How It Works, Architecture, API Reference) audited against the actual code. Useful for curious visitors and for anchoring external-facing explanations.

---

## What is demo / simulated

Everything that touches custody of real money is mocked.

### Kalshi custody — Mock only

In production, the backend uses an in-memory `kalshi.Mock` for balances, debits, and credits. Unknown users get auto-seeded with $500 of play-money balance on first contact. There is a `kalshi.RealClient` implementation that signs requests with a Kalshi-issued key, but:

- It is not enabled in production (`KALSHI_API_KEY_ID` is unset)

- The market-creation and position-grant methods return `ErrOperationNotSupported` because Kalshi has not published the partner APIs they would need

This is an intentional alpha choice. Every UI element that looks like a real trade is backed by Mock bookkeeping, not by money movement.

### Graduation — coded, untested against real money

The `graduation-worker` service polls for markets whose cumulative notional has crossed $10,000 and drives them through `BeginGraduation → CreateMarket (Kalshi) → GrantPositions (Kalshi) → CompleteGraduation`. The `Mock` client implements all of these with synthetic ticker generation and idempotency tracking.

Because no market has reached $10k yet — and because the Kalshi-side partner API doesn't exist — the graduation flow has never run in production. It's fully tested against the Mock in the `internal/graduation` test suite, but the real-world case is gated behind the Kalshi conversation.

### Resolution — manual only today

The `resolution-worker` service scans for markets past their close time and dispatches to registered adapters. Three adapters exist:

- `manual` — a placeholder that never resolves (markets have to be resolved by the creator via the UI)

- `fake` — returns a fixed outcome, used in tests

- `espn_nfl` — an actual working adapter that queries ESPN's public scores endpoint for NFL games

Only `manual` is wired up to the `Demo (manual resolution)` source in production today. The `espn_nfl` adapter is registered but no market has used it yet.

### No payment rails

There is no ACH, no card processing, no bank integration. "Funding your account" doesn't exist as a concept. Every user gets $500 of synthetic balance and trades with that. Real-money funding is gated behind the Kalshi partnership (Phase 1) or an eventual self-custody / DCM path (Phase 2).

### No KYC

No identity verification. Anyone with an email address can sign up. Under the Phase 1 plan with Kalshi, KYC runs on Kalshi's side; the Llatria-user-to-Kalshi-account link would carry the KYC guarantee. Under a Phase 2 / own-DCM plan, Llatria would need to stand up KYC itself (Persona, Alloy, etc.) — that work is not started.

---

## Architecture at a glance

Monorepo with three deployable surfaces.

### Backend — Go 1.25

```

cmd/

  api/                   HTTP API server

  migrate/               goose-based migration runner

  kalshi-sync/           polls Kalshi public endpoints, upserts catalog cache

  resolution-worker/     polls for due markets, dispatches to adapters

  graduation-worker/     polls for markets past $10k, drives migration

internal/

  cpmm/                  constant-product bonding curve (math/big, pure)

  market/                trade-execution service (transactions + events)

  storage/               8 repositories + query helpers (pgx)

  kalshi/                adapter interface + Mock + RealClient

  catalog/               browse feed, search, on-demand sparkline fetch

  auth/                  magic-link + HMAC session cookies

  pubsub/                in-memory fan-out broker

  api/                   HTTP handlers + middleware + SSE

  db/                    pgxpool setup + migration runner

  resolution/            engine + adapter interface

  graduation/            worker

```

### Frontend — Next.js 16 + React 19 + TypeScript

```

web/src/

  app/                   App Router pages

    page.tsx             homepage

    markets/[id]/        market detail

    markets/new/         create market form

    c/[slug]/            category detail

    events/k/[ticker]/   Kalshi event detail

    markets/k/[ticker]/  Kalshi market detail

    search/              search results

    me/                  user dashboard

    about/               docs page (4 tabs)

    login/               magic-link entry

    auth/callback/       token exchange

    error.tsx            global error boundary

  components/            15 components (TradePanel, PriceChart, CatalogCard, ...)

  lib/                   API client + user helpers + formatters

```

### Database — PostgreSQL 16

8 goose-managed migrations, covering:

- `users`, `markets`, `curve_pools`, `curve_trades`, `positions`, `fee_ledger`, `resolutions`, `event_log` (0001)

- `login_tokens` (0002)

- `kalshi_series`, `kalshi_events`, `kalshi_markets` catalog cache (0003, 0004)

- Full-text search indexes (0005)

- `kalshi_candles` sparkline cache (0006)

- `market.description` field (0007)

- `llatria_candles` Llatria-native sparkline cache (0008)

### Infrastructure

Single VM (Hetzner CX23, 2 vCPU / 4GB RAM, Germany region):

- PostgreSQL, Go API, Next.js web, three workers, Caddy reverse proxy — all on one box

- Systemd unit per service, `EnvironmentFile=/etc/llatria/env`

- Caddy auto-issues Let's Encrypt certs; SSE route has `flush_interval -1` for streaming

---

## Test coverage

- 76+ Go tests across 10 packages. Per-package test databases (`llatria_test_market`, `_api`, `_storage`, ...) enable parallel execution (~5 seconds vs ~30s serial).

- 58 Vitest unit + component tests covering formatters, sparkline, catalog card, search bar, price chart, recent trades card.

- 5 Playwright E2E tests covering homepage, search typeahead, search page, about tabs, login demo explainer.

CI is not yet automated — tests run locally via `make test` and `npm run build && npx vitest run`. Adding a GitHub Actions pipeline is an open item.

---

## Code review status

A full four-phase code review was completed on 2026-04-18, producing 94 findings (1 CRITICAL, 11 HIGH, 40 MEDIUM, 40 LOW/NIT) documented in [`Docs/25 Code Review Findings.md`](../Docs/25%20Code%20Review%20Findings.md). 33 of the findings were fixed and shipped to production. The CRITICAL item — an infinite `/v1/auth/me` fetch loop in the UserChip component — is resolved.

Remaining items are split into:

- Blocked on Kalshi partnership decisions (e.g. GrantPositions idempotency semantics, outbox pattern for failed credits)

- Launch prerequisites (CSRF middleware, Sentry integration, stuck-graduation recovery)

- Polish (accessibility, a11y on chart, onboarding UX)

---

## What's deliberately missing

Things that don't exist today, with the reason each is absent:

| Missing | Reason |

|---|---|

| Real money movement | Requires Kalshi partnership (Path A) or own DCM (Path B) |

| KYC flow | Kalshi handles this in Phase 1; Llatria would add Persona/Alloy in Phase 2 |

| Email notifications (beyond login) | Not yet built; Resend integration exists but only sends magic links |

| CSRF middleware | Open item in code review; cookies use `SameSite=Strict` as partial defense |

| Observability (Sentry, Prometheus) | Open item; no production visibility beyond stdout logs |

| Multi-AZ / DR | Single VM; acceptable for alpha, replace before launch |

| Mobile native apps | Web-only; mobile browser UX is adequate but not optimized |

| Payout / withdrawal UX | Not relevant until real money exists |

---

## Summary

What's built: a functioning prediction-market application with real-time trading, search, creator economics, resolution infrastructure, and a graduation pipeline. 4,000+ lines of Go, 4,000+ lines of TypeScript, 76+ backend tests, 58 frontend tests, deployed and running.

What's honest to say: the product works, the math is correct, and the architecture is intentional. But no real money has touched Llatria, and the path to real money runs through regulated infrastructure that Llatria does not yet own — either Kalshi's, or its own DCM. That gap is the subject of [`03 Strategic Paths.md`](./03%20Strategic%20Paths.md).

5 views

Add a comment

Replies

Be the first to comment