goodkeeperUnreleased
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Added
- Public changelog + version badge: a
/changelogpage (public, no auth) that renders thisCHANGELOG.mdfrom a small Keep-a-Changelog parser (src/lib/changelog.ts), and a.figure-mono version badge on the landing page linking to it. The displayed version is the latest released CHANGELOG header, falling back topackage.jsonversionuntil a release is cut — the GoodFunds sibling's mechanism, adapted to GoodKeeper's design tokens. - Notifications system: per-tenant escalation and cadence selection, email and HMAC-webhook adapters, config-guard SSRF defense, fenced system-context cron sweep with Vercel scheduled-function support, and nightly digest dispatch with at-least-once semantics.
- MCP surface (
/api/mcp): 24 authenticated tools for agent access, agent-key bearer auth scoped to households, registry invariants (tool count, names, schemas), and live-smoke coverage (malformed/absent key rejection, multi-tenant failure mode sanitization). - Confirm-first ingestion pipeline: proposal schemas bound to domain write schemas, fuzzy vehicle resolution, and an open/extract/get/reject/confirm pipeline. Confirm is transactional (composing Plan 2 tx-cores), rollback-proof, and driven by
captureandconfirm_captureMCP tools. Server extractor uses AI SDK via Vercel AI Gateway (images and PDFs). - Foundation: Next.js (App Router) + TypeScript scaffold, Supabase (Auth, Storage, local Postgres via the Supabase CLI), and Drizzle ORM wired up end to end, with portable env loading and a
vitest@alias. - Multi-tenant domain layer, entirely in its own
goodkeeperPostgres schema (neverpublic), RLS-scoped throughout:- Tenancy tables and RLS isolation, backed by a
goodkeeper_private.current_tenant_ids()helper and a sharedtenantRlsPolicies()policy builder (src/db/schema/_rls.ts). - Asset / vehicle / odometer cluster, and the maintenance record/planned/schedule/template cluster, each with RLS.
- Provider and attachment tables, with provider FKs onto maintenance records and planned services.
- Notification and ingest-job tables, RLS-scoped.
- A storage bucket (
goodkeeper-attachments, private) with RLS policies, plus a seed script for global service templates. - Agent identities: agent service users are provisioned as scoped Supabase users (
app_metadata.kind = 'agent',agents.goodkeeper.localemails), with their writes fenced separately from human-user writes. - Admin (RLS-bypassing, fenced to tenant creation/invites/seeds/tests) and RLS (request-scoped,
createDrizzle(token).rls) Drizzle transaction clients.
- Tenancy tables and RLS isolation, backed by a
- Repo guardrails in
CLAUDE.md: nodrizzle-kit push, local-only tooling (never point dev config at hosted Supabase), and the drizzle migration/custom-SQL split. - Good-family repo standards: this
CONTRIBUTING.md, this changelog, a.github/workflows/ci.yml(local Supabase stack, migrate, seed, test, typecheck, schema-drift check on PRs andmain), and additiveCLAUDE.mdsections (git workflow, commands, issue-tracking convention).
Changed
- App schema moved from
publicto its owngoodkeeperschema, with its own Drizzle migration history (drizzle.__drizzle_migrations_goodkeeper), so it can coexist inside a Supabase project shared with other Good-family apps without touching their objects. - Repo renamed to GoodKeeper end to end (docs,
supabase/config.tomlproject_id, scratch-directory ignores).
Fixed
- Forbid non-admin creation or privilege-flip of global service templates.
- Fenced
agent_identitywrites and hardenedresolveAgentTokenagainst a security gap in agent-token resolution. - Kept the migration journal's timestamps monotonic after a migration reorder, so
drizzle-kit's ordering assumptions hold. - Let
drizzle-kitown the provider-FK migration lineage rather than hand-editing around it.
Known deferred
- Composite-FK hardening (cross-tenant FK plants): RLS scopes rows by
tenant_id, but Postgres FK checks don't consult RLS, so a caller can insert/update a row whose FK points at another tenant's row as long as its own row satisfies its own tenant-scoped check. Full list of affected columns and the two ways it can be exploited (delete-cascade griefing, unique-slot pre-emption) tracked inCLAUDE.md.