A warm, private food diary with editable vision-model estimates.
Scranbook is a mobile-first Next.js PWA exported as static files and deployed with Cloudflare Workers Static Assets. Meal entries and processed photos use IndexedDB as the working copy on the user's device. There are no Scranbook accounts, Worker code, or server-side diary APIs. Scranbook sends a deliberately small set of cookieless, anonymous page and feature events to PostHog, never diary content, photos, prompts, credentials, or model responses. Users can stop future analytics in Settings. They can optionally copy accepted entries and processed photos directly from the browser to a visible folder in their own Google Drive.
When the user explicitly chooses to analyse a photo, the browser sends it directly to their
configured OpenAI-compatible endpoint. LM Studio with google/gemma-4-e4b is the default local
development profile, but the provider is configurable.
- Camera or gallery capture with browser-side resize and metadata removal.
- Manual diary entry that works without an AI model.
- Recoverable local drafts, including an unfinished processed photo.
- Diary text search, date/meal/image filters, and a purpose-built Log again flow.
- Post-meal check-ins for how a meal felt, including optional symptoms, severity, timing, and notes.
- On-device possible-pattern summaries that compare checked meals with and without an ingredient, and never treat an unchecked meal as symptom-free.
- Editable dish, portion, ingredient, confidence, and uncertainty fields.
- Editable calorie and macro estimates calculated locally from bundled official food-composition data.
- A dedicated nutrition-label scanner with model-assisted transcription, fully manual entry, reviewable printed values, and local scaling by grams, millilitres, or servings.
- Reviewable nutrition matches with local-record selection and ingredient exclusion.
- Local IndexedDB persistence with export, import, deletion, storage visibility, and gentle archive reminders. Version 3 archives preserve meal check-ins and reviewed label provenance, and remain able to import version 1 and 2 backups.
- Provider-neutral backup sharing through the operating-system share sheet where supported, with direct archive download as the fallback.
- Optional browser-direct Google Drive backup while the app is open, including pending/offline state, explicit reconnection, conflict protection, and validated restore on another device.
- Guided LM Studio and custom OpenAI-compatible setup with endpoint privacy cues, discovered-model selection, and advanced connection controls.
- Mobile diary/add/settings navigation and a two-column desktop journal.
- Installable offline PWA shell with local diary access.
- No external nutrition API and no medical, allergy, or food-safety claims.
- Node.js 22.13+
- pnpm 11.9+
- Optional: an OpenAI-compatible vision endpoint such as LM Studio
- Optional: a Google OAuth web client ID with the non-sensitive
drive.filescope
pnpm install
pnpm devOpen http://localhost:3000. Model configuration is stored through the Settings screen, not an
environment file.
Google Drive backup is disabled when no client ID is present. To enable the connection surface in
a build, set NEXT_PUBLIC_GOOGLE_DRIVE_CLIENT_ID to a Web OAuth client ID whose authorized origin
matches the app. This is a public identifier; never add a Google client secret to the frontend.
For local live testing, put the ID in the ignored .env.local file and run pnpm preview:drive.
Use a dedicated browser profile and test Google account so live Drive data remains separate from
normal browsing.
Production analytics uses the public PostHog project key and EU ingest host in .env.production.
The SDK runs only on scranbook.labs.tau.gr; local development and browser tests do not send
events. Copy .env.example when configuring another deployment. NEXT_PUBLIC_POSTHOG_KEY is a
public browser identifier—never put a PostHog personal API key in a frontend environment file.
The project-local PostHog CLI can inspect the connected project after browser authorization:
pnpm exec posthog-cli login
pnpm exec posthog-cli api project-get '{}'For the verified LM Studio profile, start the loopback-only server with browser access enabled:
lms server start --port 1234 --bind 127.0.0.1 --corsThen use:
Base URL: http://127.0.0.1:1234/v1
Model: google/gemma-4-e4b
Response mode: Strict JSON schema
The endpoint must allow browser requests. A deployed HTTPS app may encounter browser-specific local-network restrictions, especially in Safari; running Scranbook locally is the most dependable way to use a plain-HTTP local endpoint.
pnpm dev
pnpm build
pnpm build:drive:mock
pnpm start
pnpm preview:drive
pnpm test
pnpm typecheck
pnpm lint
pnpm format
pnpm test:e2e
pnpm test:e2e:mock
pnpm test:live -- --image /path/to/meal.jpg
pnpm nutrition:data
pnpm cloudflare:preview
pnpm cloudflare:deploytest:e2e and test:e2e:mock build with a dummy OAuth client ID and intercept Google Identity
Services and Drive requests with local mocks. They do not access a real Google account or Drive. If
.env.local exists, the command restores the normal local build after the mocked suite finishes.
build:drive:mock is available for explicitly producing the same credential-free test build.
test:live is opt-in and never runs in CI. Pass a local image with --image or set
SCRANBOOK_TEST_IMAGE; the command resizes it before inference and prints validated output plus
latency without logging image bytes or credentials.
nutrition:data reproducibly downloads the pinned USDA FoodData Central and UK CoFID releases and
rebuilds the committed browser index. Production never downloads those upstream datasets or calls a
nutrition API at runtime. See docs/nutrition-data.md and
THIRD_PARTY_NOTICES.md.
Scranbook has no diary backend. Cloudflare serves the application files, while IndexedDB remains the working copy for entries, meal check-ins, and photos. Possible-pattern analysis runs entirely in the browser. Model settings and credentials stay in browser storage. A photo leaves the device when the user chooses to analyse a meal photo or scan a label, and then travels directly to the configured endpoint. If the user explicitly enables Google Drive backup, accepted entries and processed photos are also copied directly from the browser to their Drive; model credentials, custom headers, tokens, settings, and unfinished drafts are excluded.
See the in-app /privacy page and SECURITY.md.
Production is served from https://scranbook.labs.tau.gr using Cloudflare Workers Static Assets.
Static asset requests are served without invoking Worker code. The production build contains no AI
key or AI proxy; a model is configured locally by each browser.
Cloudflare Workers Builds should connect to the taugr/scranbook repository:
- Production branch:
main - Root directory:
/ - Build command:
pnpm test && pnpm typecheck && pnpm lint && pnpm format - Deploy command:
pnpm cloudflare:deploy - Build output:
out
src/app/ Next.js routes, metadata, privacy page, and styling
src/components/ Diary, capture, review, and settings interface
src/lib/ schemas, IndexedDB, images, nutrition, archives, and model provider
tests/ Vitest coverage
tests/e2e/ Playwright mobile, desktop, PWA, and accessibility coverage
scripts/ setup, dataset generation, and opt-in live-model evaluation
public/ PWA assets and the bundled local nutrition index
docs/ product specification, implementation plans, and technical notes
pnpm test
pnpm typecheck
pnpm lint
pnpm format
pnpm build
pnpm test:e2e
pnpm cloudflare:preview