Data model
Every table the database has, from the migration snapshot drizzle-kit writes
out of the schema — so this page cannot disagree with the schema, and a
column added without a migration does not appear here because it does not
exist.
48 tables.
| Table | Columns | Belongs to a user |
|---|---|---|
account |
13 | yes |
activities |
9 | yes |
api_tokens |
12 | yes |
audit_events |
7 | yes |
billing_events |
8 | — |
calendar_feeds |
9 | yes |
categories |
5 | yes |
client_errors |
7 | yes |
daily_wins |
6 | yes |
data_points |
10 | yes |
data_streams |
14 | yes |
diary_entries |
9 | yes |
diary_entry_tags |
4 | yes |
entry_people |
4 | yes |
exceptional_slots |
17 | yes |
goal_areas |
6 | yes |
goal_links |
6 | yes |
goals |
17 | yes |
habit_occurrences |
6 | yes |
habits |
7 | yes |
idea_tags |
4 | yes |
ideas |
8 | yes |
invites |
8 | — |
mail_failures |
11 | — |
notebooks |
7 | yes |
people |
7 | yes |
planner_todos |
15 | yes |
planning_schemes |
5 | yes |
plugin_manifests |
8 | yes |
price_points |
6 | yes |
quotes |
5 | yes |
recipe_items |
8 | yes |
recipes |
12 | yes |
reminders |
9 | yes |
scheme_slots |
11 | yes |
session |
9 | yes |
shopping_categories |
6 | yes |
shopping_items |
12 | yes |
subscriptions |
14 | yes |
suppressed_slots |
5 | yes |
tags |
3 | yes |
task_instances |
16 | yes |
user |
11 | — |
user_settings |
4 | yes |
verification |
6 | — |
webhook_subscriptions |
11 | yes |
weekly_reviews |
7 | yes |
weekly_slots |
18 | yes |
account
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
text | not null | — | primary key |
account_id |
text | not null | — | — |
provider_id |
text | not null | — | — |
user_id |
text | not null | — | → user.id |
access_token |
text | null | — | — |
refresh_token |
text | null | — | — |
id_token |
text | null | — | — |
access_token_expires_at |
integer | null | — | — |
refresh_token_expires_at |
integer | null | — | — |
scope |
text | null | — | — |
password |
text | null | — | — |
created_at |
integer | not null | (cast(unixepoch('subsecond') * 1000 as integer)) |
— |
updated_at |
integer | not null | — | — |
Indexes:
account_userId_idxonuser_id
activities
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
name |
text | not null | — | — |
category_id |
integer | not null | — | → categories.id |
description |
text | null | '' |
— |
color |
text | null | '' |
— |
active |
integer | not null | true |
— |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
updated_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
activities_user_idxonuser_idactivities_category_idxoncategory_idactivities_active_idxonactive
api_tokens
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
name |
text | not null | — | — |
token_hash |
text | not null | — | — |
prefix |
text | not null | — | — |
plaintext |
text | null | — | — |
scopes |
text | not null | '' |
— |
last_used_at |
text | null | — | — |
expires_at |
text | null | — | — |
revoked_at |
text | null | — | — |
created_at |
text | not null | — | — |
updated_at |
text | not null | — | — |
Indexes:
api_tokens_hash_uniqueontoken_hash— uniqueapi_tokens_user_idxonuser_id
audit_events
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
actor_id |
text | null | — | — |
event |
text | not null | — | — |
detail |
text | not null | '{}' |
— |
ip |
text | null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
audit_events_user_idxonuser_idaudit_events_created_idxoncreated_at
billing_events
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
provider |
text | not null | — | — |
event_id |
text | not null | — | — |
event_type |
text | not null | — | — |
payload |
text | not null | — | — |
processed_at |
text | null | — | — |
error |
text | null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
billing_events_uniqueonprovider,event_id— unique
calendar_feeds
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
name |
text | not null | — | — |
url |
text | not null | — | — |
color |
text | not null | '#6b7280' |
— |
body |
text | null | — | — |
fetched_at |
text | null | — | — |
last_error |
text | null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
calendar_feeds_user_idxonuser_id
categories
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
name |
text | not null | — | — |
color |
text | not null | '#6b7280' |
— |
color_light |
text | not null | '#f3f4f6' |
— |
Indexes:
categories_user_idxonuser_idcategories_user_name_uniqueonuser_id,name— unique
client_errors
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | null | — | — |
message |
text | not null | — | — |
url |
text | null | — | — |
stack |
text | null | — | — |
user_agent |
text | null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
client_errors_created_idxoncreated_at
daily_wins
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
for_date |
text | not null | — | — |
position |
integer | not null | — | — |
content |
text | not null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
daily_wins_user_date_idxonuser_id,for_datedaily_wins_slot_uniqueonuser_id,for_date,position— unique
data_points
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
stream_id |
integer | not null | — | → data_streams.id |
external_id |
text | not null | — | — |
at |
text | not null | — | — |
local_date |
text | not null | — | — |
value_num |
real | null | — | — |
value_text |
text | null | — | — |
meta |
text | not null | '{}' |
— |
created_at |
text | not null | — | — |
Indexes:
data_points_stream_external_uniqueonstream_id,external_id— uniquedata_points_stream_at_idxonstream_id,atdata_points_user_idxonuser_iddata_points_stream_local_date_idxonstream_id,local_date
data_streams
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
slug |
text | not null | — | — |
name |
text | not null | — | — |
source |
text | not null | — | — |
kind |
text | not null | — | — |
unit |
text | not null | '' |
— |
display |
text | not null | 'list' |
— |
config |
text | not null | '{}' |
— |
show_on_dashboard |
integer | not null | false |
— |
retention_days |
integer | null | — | — |
archived_at |
text | null | — | — |
created_at |
text | not null | — | — |
updated_at |
text | not null | — | — |
Indexes:
data_streams_user_slug_uniqueonuser_id,slug— uniquedata_streams_user_idxonuser_id
diary_entries
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
seq |
integer | not null | 0 |
— |
notebook_seq |
integer | null | — | — |
content |
text | not null | — | — |
for_date |
text | null | — | — |
notebook_id |
integer | null | — | → notebooks.id |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
updated_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
diary_entries_user_idxonuser_iddiary_entries_created_idxoncreated_atdiary_entries_for_date_idxonfor_datediary_entries_notebook_idxonnotebook_iddiary_entries_user_seq_uniqueonuser_id,seq— uniquediary_entries_notebook_seq_uniqueonnotebook_id,notebook_seq— unique
diary_entry_tags
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
entry_id |
integer | not null | — | → diary_entries.id |
tag_id |
integer | not null | — | → tags.id |
Indexes:
diary_entry_tags_user_idxonuser_iddiary_entry_tags_entry_idxonentry_iddiary_entry_tags_tag_idxontag_id
entry_people
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
entry_id |
integer | not null | — | → diary_entries.id |
person_id |
integer | not null | — | → people.id |
Indexes:
entry_people_user_idxonuser_identry_people_entry_idxonentry_identry_people_person_idxonperson_identry_people_uniqueonentry_id,person_id— unique
exceptional_slots
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
date |
text | not null | — | — |
start_time |
text | not null | — | — |
duration_minutes |
integer | not null | 60 |
— |
mode |
text | not null | — | — |
category_id |
integer | null | — | → categories.id |
activity_id |
integer | null | — | → activities.id |
label |
text | null | '' |
— |
active |
integer | not null | true |
— |
notebook_id |
integer | null | — | → notebooks.id |
urgency |
integer | null | — | — |
interest |
integer | null | — | — |
energy |
integer | null | — | — |
meta |
text | not null | '{}' |
— |
recipe_id |
integer | null | — | → recipes.id |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
exceptional_slots_user_date_idxonuser_id,dateexceptional_slots_notebook_idxonnotebook_id
Checks — enforced by the database, not only by the service layer:
exceptional_urgency_range:"exceptional_slots"."urgency" IS NULL OR "exceptional_slots"."urgency" BETWEEN 1 AND 5exceptional_interest_range:"exceptional_slots"."interest" IS NULL OR "exceptional_slots"."interest" BETWEEN 1 AND 5exceptional_energy_range:"exceptional_slots"."energy" IS NULL OR "exceptional_slots"."energy" BETWEEN 1 AND 5exceptional_mode_category:"exceptional_slots"."mode" != 'category' OR "exceptional_slots"."category_id" IS NOT NULLexceptional_mode_activity:"exceptional_slots"."mode" != 'activity' OR "exceptional_slots"."activity_id" IS NOT NULL
goal_areas
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
name |
text | not null | — | — |
color |
text | not null | '#6b7280' |
— |
sort_order |
integer | not null | 0 |
— |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
goal_areas_user_idxonuser_idgoal_areas_user_name_uniqueonuser_id,name— unique
goal_links
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
goal_id |
integer | not null | — | → goals.id |
slot_id |
integer | null | — | → weekly_slots.id |
todo_id |
integer | null | — | → planner_todos.id |
activity_id |
integer | null | — | → activities.id |
Indexes:
goal_links_user_idxonuser_idgoal_links_goal_idxongoal_id
Checks — enforced by the database, not only by the service layer:
goal_link_has_exactly_one_target:(CASE WHEN "goal_links"."slot_id" IS NULL THEN 0 ELSE 1 END) + (CASE WHEN "goal_links"."todo_id" IS NULL THEN 0 ELSE 1 END) + (CASE WHEN "goal_links"."activity_id" IS NULL THEN 0 ELSE 1 END) = 1
goals
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
area_id |
integer | null | — | → goal_areas.id |
notebook_id |
integer | null | — | → notebooks.id |
parent_id |
integer | null | — | — |
title |
text | not null | — | — |
notes |
text | null | '' |
— |
horizon |
text | not null | — | — |
period_start |
text | not null | — | — |
target_value |
real | null | — | — |
current_value |
real | not null | 0 |
— |
unit |
text | null | '' |
— |
status |
text | not null | 'open' |
— |
outcome |
text | null | '' |
— |
closed_at |
text | null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
updated_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
goals_user_idxonuser_idgoals_period_idxonuser_id,horizon,period_startgoals_parent_idxonparent_idgoals_area_idxonarea_idgoals_notebook_idxonnotebook_id
Checks — enforced by the database, not only by the service layer:
goals_target_positive:"goals"."target_value" IS NULL OR "goals"."target_value" > 0
habit_occurrences
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
habit_id |
integer | not null | — | → habits.id |
date |
text | not null | — | — |
notes |
text | null | '' |
— |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
habit_occurrences_user_idxonuser_idhabit_occurrences_habit_idxonhabit_idhabit_occurrences_date_idxondate
habits
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
name |
text | not null | — | — |
description |
text | null | '' |
— |
type |
text | not null | 'bad' |
— |
scheduled_days |
text | null | '' |
— |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
habits_user_idxonuser_id
idea_tags
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
idea_id |
integer | not null | — | → ideas.id |
tag_id |
integer | not null | — | → tags.id |
Indexes:
idea_tags_user_idxonuser_ididea_tags_idea_idxonidea_ididea_tags_tag_idxontag_id
ideas
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
content |
text | not null | — | — |
is_applied |
integer | not null | false |
— |
applied_note |
text | null | — | — |
favorite |
integer | not null | false |
— |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
updated_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
ideas_user_idxonuser_idideas_created_idxoncreated_at
invites
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
code |
text | not null | — | — |
note |
text | null | '' |
— |
created_by |
text | not null | — | — |
expires_at |
text | null | — | — |
used_at |
text | null | — | — |
used_by |
text | null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
invites_code_uniqueoncode— unique
mail_failures
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
kind |
text | not null | — | — |
to_email |
text | not null | — | — |
subject |
text | not null | — | — |
error |
text | not null | — | — |
attempts |
integer | not null | 1 |
— |
body_text |
text | null | — | — |
body_html |
text | null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
last_attempt_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
resolved_at |
text | null | — | — |
Indexes:
mail_failures_open_idxonresolved_at
notebooks
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
title |
text | not null | — | — |
description |
text | null | '' |
— |
closed_at |
text | null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
updated_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
notebooks_user_idxonuser_idnotebooks_user_title_uniqueonuser_id,title— unique
people
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
name |
text | not null | — | — |
relationship |
text | not null | 'other' |
— |
notes |
text | null | '' |
— |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
updated_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
people_user_idxonuser_idpeople_user_name_uniqueonuser_id,name— unique
planner_todos
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
title |
text | not null | — | — |
notes |
text | null | '' |
— |
completed |
integer | not null | false |
— |
category_id |
integer | null | — | → categories.id |
notebook_id |
integer | null | — | → notebooks.id |
scheduled_date |
text | null | — | — |
status |
text | not null | 'todo' |
— |
sort_order |
integer | not null | 0 |
— |
urgency |
integer | null | — | — |
interest |
integer | null | — | — |
energy |
integer | null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
updated_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
planner_todos_user_idxonuser_idplanner_todos_scheduled_idxonuser_id,scheduled_dateplanner_todos_notebook_idxonnotebook_id
Checks — enforced by the database, not only by the service layer:
todos_urgency_range:"planner_todos"."urgency" IS NULL OR "planner_todos"."urgency" BETWEEN 1 AND 5todos_interest_range:"planner_todos"."interest" IS NULL OR "planner_todos"."interest" BETWEEN 1 AND 5todos_energy_range:"planner_todos"."energy" IS NULL OR "planner_todos"."energy" BETWEEN 1 AND 5
planning_schemes
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
name |
text | not null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
updated_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
schemes_user_idxonuser_idschemes_user_name_uniqueonuser_id,name— unique
plugin_manifests
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
source |
text | not null | — | — |
name |
text | not null | — | — |
description |
text | null | '' |
— |
homepage |
text | null | '' |
— |
meta_keys |
text | not null | '[]' |
— |
updated_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
plugin_manifests_user_source_uniqueonuser_id,source— uniqueplugin_manifests_user_idxonuser_id
price_points
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
item_id |
integer | not null | — | → shopping_items.id |
price_cents |
integer | not null | — | — |
for_date |
text | not null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
price_points_user_item_idxonuser_id,item_idprice_points_date_idxonfor_date
quotes
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
text |
text | not null | — | — |
author |
text | null | '' |
— |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
quotes_user_idxonuser_id
recipe_items
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
recipe_id |
integer | not null | — | → recipes.id |
item_id |
integer | not null | — | → shopping_items.id |
quantity |
real | null | — | — |
unit |
text | null | '' |
— |
note |
text | null | '' |
— |
sort_order |
integer | not null | 0 |
— |
Indexes:
recipe_items_user_idxonuser_idrecipe_items_recipe_idxonrecipe_idrecipe_items_item_idxonitem_idrecipe_items_uniqueonrecipe_id,item_id— unique
Checks — enforced by the database, not only by the service layer:
recipe_items_quantity_positive:"recipe_items"."quantity" IS NULL OR "recipe_items"."quantity" > 0
recipes
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
title |
text | not null | — | — |
method |
text | not null | '' |
— |
notes |
text | null | '' |
— |
servings |
integer | null | — | — |
minutes |
integer | null | — | — |
source |
text | null | '' |
— |
last_cooked_at |
text | null | — | — |
archived_at |
text | null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
updated_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
recipes_user_idxonuser_id
Checks — enforced by the database, not only by the service layer:
recipes_servings_positive:"recipes"."servings" IS NULL OR "recipes"."servings" > 0recipes_minutes_positive:"recipes"."minutes" IS NULL OR "recipes"."minutes" > 0
reminders
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
subject_kind |
text | not null | 'free' |
— |
subject_id |
integer | null | — | — |
remind_at |
text | not null | — | — |
message |
text | not null | — | — |
delivered_at |
text | null | — | — |
dismissed_at |
text | null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
reminders_user_idxonuser_idreminders_due_idxonuser_id,delivered_at,remind_atreminders_subject_idxonsubject_kind,subject_id
scheme_slots
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
scheme_id |
integer | not null | — | → planning_schemes.id |
weekday |
integer | not null | — | — |
start_time |
text | not null | — | — |
duration_minutes |
integer | not null | 60 |
— |
mode |
text | not null | — | — |
category_id |
integer | null | — | → categories.id |
activity_id |
integer | null | — | → activities.id |
label |
text | null | '' |
— |
active |
integer | not null | true |
— |
Indexes:
scheme_slots_user_idxonuser_idscheme_slots_scheme_idxonscheme_id
session
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
text | not null | — | primary key |
expires_at |
integer | not null | — | — |
token |
text | not null | — | — |
created_at |
integer | not null | (cast(unixepoch('subsecond') * 1000 as integer)) |
— |
updated_at |
integer | not null | — | — |
ip_address |
text | null | — | — |
user_agent |
text | null | — | — |
user_id |
text | not null | — | → user.id |
impersonated_by |
text | null | — | — |
Indexes:
session_token_uniqueontoken— uniquesession_userId_idxonuser_id
shopping_categories
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
name |
text | not null | — | — |
is_food |
integer | not null | false |
— |
sort_order |
integer | not null | 0 |
— |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
shopping_categories_user_idxonuser_idshopping_categories_user_name_uniqueonuser_id,name— unique
shopping_items
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
name |
text | not null | — | — |
type |
text | not null | — | — |
shopping_category_id |
integer | null | — | → shopping_categories.id |
notes |
text | null | '' |
— |
bought |
integer | not null | false |
— |
bought_at |
text | null | — | — |
price_cents |
integer | null | — | — |
snoozed |
integer | not null | false |
— |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
updated_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
shopping_items_user_idxonuser_idshopping_items_type_idxontypeshopping_items_bought_idxonboughtshopping_items_snoozed_idxonsnoozedshopping_items_category_idxonshopping_category_id
subscriptions
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
plan |
text | not null | 'none' |
— |
status |
text | not null | 'trialing' |
— |
provider |
text | not null | 'none' |
— |
provider_customer_id |
text | null | — | — |
provider_subscription_id |
text | null | — | — |
current_period_end |
text | null | — | — |
trial_ends_at |
text | null | — | — |
trial_notice_sent_at |
text | null | — | — |
cancel_at |
text | null | — | — |
portal_url |
text | null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
updated_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
subscriptions_user_uniqueonuser_id— uniquesubscriptions_provider_idxonprovider_subscription_id
suppressed_slots
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
date |
text | not null | — | — |
slot_id |
integer | not null | — | → weekly_slots.id |
moved_to_id |
integer | null | — | → exceptional_slots.id |
Indexes:
suppressed_slots_user_date_idxonuser_id,datesuppressed_slots_uniqueonuser_id,date,slot_id— unique
tags
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
name |
text | not null | — | — |
Indexes:
tags_user_idxonuser_idtags_user_name_uniqueonuser_id,name— unique
task_instances
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
slot_id |
integer | null | — | → weekly_slots.id |
exceptional_slot_id |
integer | null | — | → exceptional_slots.id |
scheduled_at |
text | not null | — | — |
status |
text | not null | 'todo' |
— |
timing |
text | null | — | — |
completed_at |
text | null | — | — |
notes |
text | null | '' |
— |
resolved_activity_id |
integer | null | — | → activities.id |
duration_override |
integer | null | — | — |
label_override |
text | null | — | — |
urgency_override |
integer | null | — | — |
interest_override |
integer | null | — | — |
energy_override |
integer | null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
instances_user_idxonuser_idinstances_slot_idxonslot_idinstances_exceptional_idxonexceptional_slot_idinstances_scheduled_idxonscheduled_atinstances_status_idxonstatusinstances_slot_scheduled_idxonslot_id,scheduled_atinstances_exceptional_uniqueonexceptional_slot_id— unique
Checks — enforced by the database, not only by the service layer:
instance_has_exactly_one_source:("task_instances"."slot_id" IS NULL) != ("task_instances"."exceptional_slot_id" IS NULL)
user
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
text | not null | — | primary key |
name |
text | not null | — | — |
email |
text | not null | — | — |
email_verified |
integer | not null | false |
— |
image |
text | null | — | — |
role |
text | not null | 'member' |
— |
banned |
integer | null | false |
— |
ban_reason |
text | null | — | — |
ban_expires |
integer | null | — | — |
created_at |
integer | not null | (cast(unixepoch('subsecond') * 1000 as integer)) |
— |
updated_at |
integer | not null | (cast(unixepoch('subsecond') * 1000 as integer)) |
— |
Indexes:
user_email_uniqueonemail— unique
user_settings
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
key |
text | not null | — | — |
value |
text | not null | — | — |
Indexes:
user_settings_user_key_uniqueonuser_id,key— uniqueuser_settings_user_idxonuser_id
verification
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
text | not null | — | primary key |
identifier |
text | not null | — | — |
value |
text | not null | — | — |
expires_at |
integer | not null | — | — |
created_at |
integer | not null | (cast(unixepoch('subsecond') * 1000 as integer)) |
— |
updated_at |
integer | not null | (cast(unixepoch('subsecond') * 1000 as integer)) |
— |
Indexes:
verification_identifier_idxonidentifier
webhook_subscriptions
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
url |
text | not null | — | — |
events |
text | not null | — | — |
secret |
text | not null | — | — |
last_delivery_at |
text | null | — | — |
last_status |
integer | null | — | — |
fail_count |
integer | not null | 0 |
— |
disabled_at |
text | null | — | — |
created_at |
text | not null | — | — |
updated_at |
text | not null | — | — |
Indexes:
webhook_subscriptions_user_idxonuser_id
weekly_reviews
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
week_start |
text | not null | — | — |
position |
integer | not null | — | — |
content |
text | not null | — | — |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
updated_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
weekly_reviews_user_week_idxonuser_id,week_startweekly_reviews_slot_uniqueonuser_id,week_start,position— unique
weekly_slots
| Column | Type | Null | Default | Notes |
|---|---|---|---|---|
id |
integer | not null | — | primary key, auto |
user_id |
text | not null | — | → user.id |
weekday |
integer | not null | — | — |
recurrence |
text | not null | 'weekly' |
— |
start_time |
text | not null | — | — |
duration_minutes |
integer | not null | 60 |
— |
mode |
text | not null | — | — |
category_id |
integer | null | — | → categories.id |
activity_id |
integer | null | — | → activities.id |
label |
text | null | '' |
— |
active |
integer | not null | true |
— |
urgency |
integer | null | — | — |
interest |
integer | null | — | — |
energy |
integer | null | — | — |
meta |
text | not null | '{}' |
— |
recipe_id |
integer | null | — | → recipes.id |
created_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
updated_at |
text | not null | (CURRENT_TIMESTAMP) |
— |
Indexes:
slots_user_idxonuser_idslots_weekday_idxonweekdayslots_weekday_time_idxonweekday,start_time
Checks — enforced by the database, not only by the service layer:
slots_urgency_range:"weekly_slots"."urgency" IS NULL OR "weekly_slots"."urgency" BETWEEN 1 AND 5slots_interest_range:"weekly_slots"."interest" IS NULL OR "weekly_slots"."interest" BETWEEN 1 AND 5slots_energy_range:"weekly_slots"."energy" IS NULL OR "weekly_slots"."energy" BETWEEN 1 AND 5slots_weekday_range:"weekly_slots"."weekday" >= 0 AND "weekly_slots"."weekday" <= 6slots_mode_category:"weekly_slots"."mode" != 'category' OR "weekly_slots"."category_id" IS NOT NULLslots_mode_activity:"weekly_slots"."mode" != 'activity' OR "weekly_slots"."activity_id" IS NOT NULL