flowchart TB
subgraph Client [Browser]
A[Next.js 15 PWA]
AUTH[Auth UI]
end
subgraph Agent [AI Agents]
MCP[MCP Server<br/>apps/mcp]
CLAUDE[Claude Code]
HERMES[Hermes Agent]
CURSOR[Cursor]
end
subgraph Supabase [Supabase]
DB[(PostgreSQL)]
RLS[Row-Level Security]
SVC[Auth Service]
STORAGE[Storage Bucket]
EF[Edge Functions<br/>identify-plant]
end
A --> AUTH
AUTH --> SVC
A --> DB
DB --> RLS
A -.-> STORAGE
CLAUDE --> MCP
HERMES --> MCP
CURSOR --> MCP
MCP --> DB
MCP --> STORAGE
MCP --> EF
sequenceDiagram
participant U as User
participant B as Browser
participant S as Supabase
participant R as RLS
U->>B: Sign in
B->>S: Auth request
S->>B: Session token
U->>B: Add plant
B->>S: INSERT plants (with token)
S->>R: auth.uid() check
R->>S: Policy pass
S->>B: Plant created
U->>B: View dashboard
B->>S: SELECT plants
S->>R: auth.uid() filter
B->>U: Plant cards + care tasks
opensprout/
|-- apps/
| |-- web/ # Next.js 15 PWA
| | |-- public/ # manifest, service worker, app icons
| | `-- src/
| | |-- app/ # App Router pages and route handlers
| | |-- components/ # Product and UI components
| | `-- lib/
| | |-- data/ # Supabase query and domain helpers
| | `-- supabase/ # Browser/server clients
| `-- mcp/ # MCP server for AI agent integration
| |-- src/
| | |-- index.ts # MCP server entry point
| | |-- supabase.ts # Auth + Supabase client setup
| | |-- types.ts # Database type definitions
| | `-- tools/ # Tool registrations (plants, care, journal, etc.)
| `-- __tests__/ # Vitest test suite
|-- packages/
| |-- ui/ # Future shared shadcn/ui package
| |-- database/ # Future generated DB types and helpers
| |-- shared/ # Shared domain types
| `-- config/ # Shared lint/ts/tailwind config
|-- docs/ # Architecture, API, roadmap, license notes
|-- supabase/
| `-- migrations/ # PostgreSQL schema and RLS
|-- scripts/ # Maintenance and release scripts
`-- .github/ # Issue templates and project docs
The dashboard uses Supabase directly from the browser with the publishable key. RLS is the security boundary, and no service role key is used in the frontend.
flowchart LR
subgraph DataLayer [Browser Data Layer]
SPECIES[src/lib/data/species.ts]
PLANTS[src/lib/data/plants.ts]
CARE[src/lib/data/care.ts]
end
subgraph Tables [Supabase Tables]
PS[plant_species]
P[plants]
CS[care_schedules]
CL[care_logs]
JE[journal_entries]
JP[journal_photos]
end
SPECIES --> PS
PLANTS --> P
PLANTS --> CS
PLANTS --> CL
CARE --> CS
CARE --> CL
JOURNAL[journal.ts] --> JE
PHOTOS[photos.ts] --> JP
PHOTOS --> STORAGE[Storage: plant-photos]
P --> RLS[Row-Level Security]
CS --> RLS
CL --> RLS
JE --> RLS
JP --> RLS
Initial route handlers live in apps/web/src/app/api:
GET /api/plants: list the signed-in user's plants.POST /api/plants: create a plant with validated input and server-generated sync metadata.GET /api/export: export user-owned tables as portable JSON.
The current dashboard primarily uses the browser data layer in src/lib/data. Route handlers remain available for future server-driven flows.
Planned routes:
/api/care-schedules/api/care-logs/api/import/api/sync/pull/api/sync/push
Note:
journal_entries,journal_photos, and photo uploads are handled client-side through the Supabase JS SDK directly — route handlers are not needed for the current architecture.
RootLayout
`-- Home
|-- AppShell
| |-- AuthPanel
| |-- PlantForm
| |-- PlantCard
| |-- PlantDetail
| |-- Care reminders
| `-- Recent care logs
`-- PwaRegister
+--------------+---------------------------------------------+-------------+
| OpenSprout | Plant dashboard | Plant detail|
| Dashboard | [Export JSON] [Import] [Add plant] [Logout] | Photo |
| Plants | | Care buttons|
| Calendar | Due today | Healthy plants | Backups | Notes |
| Journal | +-------------+
| Backups | Care reminders from schedules | Care logs |
| Supabase sync| | preview |
| | Plant search + plant list + edit form | |
+--------------+---------------------------------------------+-------------+
The initial migration includes:
profilesplant_speciesplantscare_schedulestask_instancescare_logsjournal_entriesjournal_photosdata_transferssync_devices- private Supabase Storage bucket
plant-photos
Every user-owned public table has RLS enabled. Policies use auth.uid() against user_id or profiles.id. Mutable tables include client_id, sync_version, last_modified_at, and deleted_at for local-first sync and tombstones.
plant_species is a public, read-only Care Templates table. It stores common plant guidance and suggested watering/fertilizing intervals. Plants can optionally reference plant_species.id through plants.species_id, while still preserving custom species text for plants that are not in the template list.
For the hosted OpenSprout project, the schema was applied through Supabase MCP raw SQL execution. The repo migration file remains the source of truth.