# Fieldbook

A working ledger for projects, tasks, notes, reminders, whiteboards and
flowcharts. React 19 + TanStack Start (SSR) + Tailwind 4, built by Vite,
served by Nitro as a plain Node process.

Live at **https://fieldbook.nillnode.com**, behind Apache basic auth.

## Layout

```
src/routes/       pages (file-based routing; routeTree.gen.ts is generated)
src/components/   UI
src/lib/store.ts  the whole app state (zustand) - this is the data model
src/lib/seed.ts   the sample book a brand-new user starts with
server/middleware/book-api.ts   the storage API, /api/book
```

## Where the data lives

Postgres, one JSON document per user, in the `fieldbook` database. The browser
keeps a `localStorage` copy as an offline cache, but the server is the record
of truth.

This matters, because it used to be the other way round. The book lived only
in `localStorage`, and since the store's initial value *is* `SEED`, a failed
read left the store on the sample book and the next write persisted that over
the user's real data — losing everything on a page load, with no action by the
user. Two things prevent that now: `src/lib/store.ts` refuses to write to
`localStorage` until a read has demonstrably succeeded, and `book-sync.ts`
only arms saving after it has read the server copy.

Saves are a compare-and-swap on a `revision` counter, so a stale tab gets a
409 and the newer version back rather than overwriting it. Every version is
kept for 90 days in `book_history`.

Connection string: `FIELDBOOK_DATABASE_URL`, from `/etc/fieldbook.env`.
Deliberately not `DATABASE_URL` — nothing reads that name any more, but the
distinct name keeps the app's storage separate from anything generic.

Recovery, rollback and backups are documented in `/var/www/html/README.md`.

## Development

```bash
npm install
npm run dev
```

Dev runs on <http://127.0.0.1:5173>. Without `FIELDBOOK_DATABASE_URL` in the
environment the storage API returns 503 and the app says "This browser only"
in the header — it still works, it just won't sync. To develop against the
real database:

```bash
set -a && . /etc/fieldbook.env && set +a && npm run dev
```

Note that Nitro (and therefore `server/middleware/`) only runs for builds and
`npm run preview`, not `npm run dev` — see `vite.config.ts`.

## Deploying

The server runs the built output directly; there is no watch mode in
production. After any change:

```bash
./selfhost-build.sh && sudo systemctl restart fieldbook
```

`selfhost-build.sh` builds, works around a CSS quirk described in its
comments, and then verifies that the rendered page's assets all resolve —
a build can succeed and still serve an unstyled or blank page, so the
verification is the point. It exits non-zero if anything is broken.

The systemd unit is `/etc/systemd/system/fieldbook.service`; it runs
`.output/server/index.mjs` on `127.0.0.1:8478`. Apache terminates TLS,
supplies basic auth, serves `/assets` off disk and proxies the rest.

```bash
journalctl -u fieldbook -f
```

## Checks

```bash
npm run typecheck
npm run lint
```
