# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview eng-pad-server is a Go service that receives engineering notebook data from the eng-pad Android app via gRPC, stores it in SQLite, and serves read-only views through a web UI. It supports password + FIDO2/U2F authentication and shareable links. ## Build Commands ```bash make all # vet → lint → test → build make eng-pad-server # build binary with version injection make build # compile all packages make test # run tests make vet # go vet make lint # golangci-lint make proto # regenerate gRPC code make proto-lint # buf lint + breaking changes make clean # remove binary # Run a single test: go test -run TestFunctionName ./internal/... ``` ## Architecture - **Go 1.25+**, pure-Go dependencies, `CGO_ENABLED=0` - **gRPC API**: receives notebook sync from the Android app (password auth per-request over TLS) - **REST API**: JSON over HTTPS for web viewing, auth endpoints - **Web UI**: Go `html/template` + htmx, SVG page rendering - **SQLite**: via `modernc.org/sqlite`, WAL mode, foreign keys - **Auth**: Argon2id passwords + FIDO2/U2F via `go-webauthn/webauthn` - **Router**: chi (lightweight, stdlib-compatible) ## Project Documents - **ARCHITECTURE.md** — full system specification - **PROJECT_PLAN.md** — implementation steps with checkboxes - **PROGRESS.md** — completion tracking and decisions **Keep PROJECT_PLAN.md and PROGRESS.md in sync.** ## Source Tree ``` eng-pad-server/ ├── cmd/ │ └── eng-pad-server/ CLI entry point (cobra) │ ├── main.go │ ├── server.go server subcommand │ ├── init.go init subcommand │ └── passwd.go password reset subcommand ├── internal/ │ ├── auth/ │ │ ├── argon2.go Password hashing │ │ ├── tokens.go Bearer token generation/validation │ │ └── webauthn.go FIDO2/U2F integration │ ├── config/ │ │ └── config.go TOML configuration │ ├── db/ │ │ ├── db.go Database setup, pragmas │ │ └── migrations.go Schema migrations │ ├── grpcserver/ │ │ ├── server.go gRPC server setup │ │ ├── sync.go SyncNotebook handler │ │ ├── share.go Share link RPCs │ │ └── interceptors.go Auth interceptor │ ├── server/ │ │ ├── server.go REST server setup │ │ ├── routes.go Route registration │ │ ├── auth.go Login/register endpoints │ │ ├── notebooks.go Notebook/page endpoints │ │ └── middleware.go Auth middleware │ ├── render/ │ │ ├── svg.go Page → SVG rendering │ │ ├── jpg.go Page → JPG rendering │ │ └── pdf.go Notebook → PDF rendering │ ├── share/ │ │ └── share.go Share link token management │ └── webserver/ │ ├── server.go Web UI server setup │ ├── routes.go Template routes │ └── handlers.go htmx handlers ├── proto/engpad/ │ └── v1/ │ └── sync.proto gRPC service definition ├── gen/engpad/ │ └── v1/ Generated Go gRPC code ├── web/ │ ├── embed.go //go:embed directive │ ├── templates/ │ │ ├── layout.html Shared HTML skeleton │ │ ├── login.html Login page │ │ ├── notebooks.html Notebook list │ │ ├── notebook.html Single notebook view │ │ └── page.html Page viewer (SVG embed) │ └── static/ │ └── htmx.min.js ├── deploy/ │ ├── docker/ │ │ └── docker-compose.yml │ ├── systemd/ │ │ ├── eng-pad-server.service │ │ └── eng-pad-server-backup.timer │ ├── scripts/ │ │ └── install.sh │ └── examples/ │ └── eng-pad-server.toml ├── Dockerfile ├── Makefile ├── buf.yaml ├── .golangci.yaml ├── .gitignore ├── CLAUDE.md This file ├── README.md ├── ARCHITECTURE.md Full system spec ├── PROJECT_PLAN.md Implementation steps └── PROGRESS.md Completion tracking ``` ## Key Conventions - Stroke point data: packed little-endian floats `[x0,y0,x1,y1,...]` — same binary format as the Android app - Canonical coordinates: 300 DPI. Scaled to 72 DPI for SVG/PDF output (×0.24) - Page sizes: REGULAR (2550×3300 pts), LARGE (3300×5100 pts) - Stroke styles: "plain", "dashed", "arrow", "double_arrow" - No grid rendering — grid is a tablet writing aid only - Share link tokens: 32-byte `crypto/rand`, URL-safe base64 - gRPC auth: username+password in metadata, verified per-request - Web auth: password login → bearer token in session cookie - TLS 1.3 minimum, no exceptions