--- name: geo-auth-backend-plan overview: Design and implement a Go + PostgreSQL(PostGIS) backend with Ed25519 auth, hybrid invitation onboarding, and per-user GeoJSON feature-collection management; plus a no-build Vue/Vuetify frontend for key lifecycle and API usage. todos: - id: bootstrap-go-api content: Create greenfield Go API skeleton with config, routing, and health endpoints status: pending - id: auth-ed25519 content: Implement challenge-response auth with Ed25519 verification and token issuance status: pending - id: hybrid-invitations content: Implement hybrid invitation issuance/verification with lineage and replay protection status: pending - id: geo-storage content: Add PostGIS-backed models/migrations for collections and point features status: pending - id: geo-crud-endpoints content: Expose authenticated per-user GeoJSON collection/feature CRUD endpoints status: pending - id: frontend-static-vue content: Build no-bundler Vue+Vuetify static frontend with key generation/import/export in localStorage status: pending - id: tests-and-hardening content: Add tests for auth/invites/geo ownership and enforce validation/security checks status: pending - id: ts-api-client-lib content: Create reusable TypeScript API client library with @noble/ed25519 for key generation, key storage helpers, and signed API communication status: pending - id: js-frontend-wrapper content: Add plain JavaScript wrapper adapter so no-build frontend can consume the TypeScript-built client bundle status: pending isProject: false --- # Geo App Auth + GeoJSON Service Plan ## Current Project Context - No existing backend source files were found in this repository root, so this plan assumes a greenfield implementation. - Stack confirmed: **Go + PostgreSQL (+ PostGIS)**. - Invitation model confirmed: **Hybrid** (signed token + inviter lineage tracking). - Client scope confirmed: **both** a reusable TS library and a plain JS frontend integration. - Build policy confirmed: **npm/build allowed for library package only**; frontend remains no npm/no bundling. ## Architecture - **Auth identity:** user identity = Ed25519 public key (`base64url`); server never stores private keys. - **Login/auth:** wallet-style challenge-response: - client requests nonce - client signs nonce with private key - server verifies signature against registered public key - server returns short-lived access token + optional refresh token. - **Registration via invitation:** - inviter generates signed invitation payload (invitee pubkey optional at creation time) - invite token includes inviter key, expiry, nonce/jti, usage constraints, and lineage metadata - invitee submits invite token + own pubkey + signature proof - server verifies inviter trust chain + token signature + replay protection. - **Data model:** users own feature collections; each collection owns GeoJSON Features (initially Point geometry). - **Storage:** PostGIS geometry column (`geometry(Point, 4326)`) plus raw GeoJSON JSONB for roundtrip fidelity. ```mermaid flowchart TD inviter[ExistingUserInviter] -->|"signs invite token"| inviteToken[InviteToken] invitee[NewUserInvitee] -->|"submits pubkey + signed challenge + token"| api[GoAPI] api -->|"verify inviter + token + signature"| users[(Users)] api -->|"issue access token"| session[(Sessions)] invitee -->|"CRUD collections/features"| api api --> collections[(FeatureCollections)] api --> features[(FeaturesPostGIS)] ``` ## Planned Files and Components - Backend bootstrap: - `cmd/api/main.go` - `internal/http/routes.go` - `internal/config/config.go` - Auth/invitation domain: - `internal/auth/challenge.go` - `internal/auth/ed25519_verify.go` - `internal/invite/token.go` - `internal/invite/service.go` - Geo domain: - `internal/geo/collections_service.go` - `internal/geo/features_service.go` - Database: - `migrations/0001_init_users_invites.sql` - `migrations/0002_geo_collections_features.sql` - Frontend (no npm/bundling): - `web/index.html` - `web/app.js` - `web/api.js` - Reusable TypeScript client library (separate package): - `libs/geo-api-client/package.json` - `libs/geo-api-client/tsconfig.json` - `libs/geo-api-client/src/GeoApiClient.ts` - `libs/geo-api-client/src/keys.ts` - `libs/geo-api-client/src/storage.ts` - `libs/geo-api-client/dist/` (build output consumed by frontend) ## API Surface (v1) - `POST /v1/auth/challenge` -> nonce - `POST /v1/auth/login` -> verify signature, issue token - `POST /v1/auth/register` -> invite validation + pubkey registration - `POST /v1/invitations` -> inviter creates signed invite payload - `GET /v1/me/keys` -> registered pubkey metadata - `POST /v1/collections` / `GET /v1/collections` - `POST /v1/collections/{id}/features` / `GET /v1/collections/{id}/features` - `PATCH/DELETE /v1/features/{id}` ## Security and Validation - Enforce strict canonical payload format before signature verification. - Replay protection with nonce/jti tables and TTL. - Invite expiration + max use + optional binding to target pubkey. - Per-user row-level authorization in service layer (and optionally DB RLS later). - Input validation for GeoJSON (`Feature`, `Point`, lon/lat ranges, SRID 4326). ## Frontend Constraints Implementation - Serve Vue + Vuetify from CDN (no npm, no bundling). - Key lifecycle in browser via client library: - generate Ed25519 keypair using `@noble/ed25519` in TS client - precreate key material when first profile/session is initialized - store key material in `localStorage` through client helper API (with optional encryption passphrase layer) - import/export profile key material from user settings page. - UI pages: Login/Register (invite), Collections list, Mapless Feature editor (Point first), Profile key management. ## TypeScript API Client Library Scope - Export `GeoApiClient` class for all API communication: - `createChallenge()`, `loginWithSignature()`, `registerWithInvitation()` - `createInvitation()` - collection/feature CRUD methods. - Export key-management helpers: - `generateKeyPair()`, `signChallenge()`, `exportKeys()`, `importKeys()` - `ensureKeysInStorage()` for precreate-on-first-use behavior. - Add request signing conventions compatible with backend challenge/invitation verification. - Build to browser-consumable ESM/UMD artifacts; frontend uses built JS file directly without bundling. ## TDD-first Delivery Slices 1. Auth challenge + login signature verification tests. 2. Invitation token verification + replay/expiry tests. 3. Registration tests (happy path + invalid inviter chain). 4. Geo collection ownership tests. 5. GeoJSON point validation + CRUD tests. 6. TS client unit tests (`@noble/ed25519` flows, storage, API error handling). 7. Frontend key-management smoke tests (manual + scripted API checks via JS wrapper). ## Acceptance Criteria - Only registered public keys can authenticate and access their own data. - New user registration succeeds only with valid inviter lineage and signed token. - Users can create/read/update/delete their own feature collections and Point features as GeoJSON. - Reusable TS API client class handles auth + geo API communication and key lifecycle with `@noble/ed25519`. - Frontend runs as static files without build tooling, consuming prebuilt client bundle, and supports key import/export + local persistence.