Digital Sanctuary Data Model
This document describes the backend data model for Digital Sanctuary. It is intended to guide the first database build from the local-first MVP toward a hosted product with users, walls, sharing, NFC links, uploaded media, folders, and playlists.
Modeling Principles
- A wall is the core owned object.
- Wall items are flexible positioned objects on a wall: artwork, text, decor, folders, and music players.
- Object-specific data can live in typed columns when it is common, and in
metadataJSON when it is variant-specific. - Files and generated assets should be tracked separately from the objects that use them.
- Public sharing and NFC links should point to stable wall share URLs, not temporary local state.
- The first backend should support single-user ownership, public/private visibility, and link-based viewing before adding collaboration.
Entity Overview
users: account owner records.walls: one digital sanctuary wall or board.wall_items: positioned objects on a wall.wall_item_assets: media attached to wall items.folders: folder-specific records for wall items of typefolder.folder_pages: pages inside a folder for work-in-progress material.playlists: playlist state for a wall or music-player item.playlist_tracks: track records inside a playlist.assets: uploaded or generated files such as images.share_links: stable public/private/invite links for walls or specific objects.nfc_links: physical NFC sticker bindings to wall or object destinations.object_catalog_items: available object types and variants shown in the catalog.wall_events: optional audit/activity stream for important wall changes.
Tables
users
Stores the person who owns walls.
| Field | Type | Notes |
|---|---|---|
id | UUID | Primary key. |
email | Text | Unique when using email auth. |
display_name | Text | User-facing name. |
avatar_asset_id | UUID nullable | References assets.id. |
created_at | Timestamp | Creation time. |
updated_at | Timestamp | Last profile update. |
MVP notes:
- If auth is deferred, this table can still exist with anonymous/local imported users.
walls
Stores the wall itself, its atmosphere settings, ownership, and visibility.
| Field | Type | Notes |
|---|---|---|
id | UUID | Primary key. |
owner_user_id | UUID | References users.id. |
title | Text | Wall name. |
slug | Text nullable | Optional human-readable URL segment. |
visibility | Enum | private, public, later unlisted or invite_only. |
mode_default | Enum | Usually visit for shared links, edit for owner sessions. |
material | Text | Current MVP values include hearth, smoke-hearth, smoke-garden, smoke-moonroom, light, greenhouse, brick, concrete, gallery, plaster, wood. |
location_label | Text | Freeform "Where is this?" value. |
window_position | Text | left, center, right, none. |
lighting | Text | Current MVP values include morning, golden, night, spotlight. |
time_of_day | Text nullable | Optional product-level value if it differs from lighting. |
window_view_type | Enum | preset, prompt, image, or none. |
window_view_preset | Text nullable | Selected outside-window preset. Live MVP values: city (New York skyline), ocean, mountain, farmland (stored as wallConfig.windowView). |
window_view_prompt | Text nullable | User prompt for outside-window scene. |
window_view_asset_id | UUID nullable | References generated/uploaded assets.id. |
grid_size | Integer | Default snap-grid size. |
canvas_width | Integer nullable | Optional canonical wall coordinate width. |
canvas_height | Integer nullable | Optional canonical wall coordinate height. |
created_at | Timestamp | Creation time. |
updated_at | Timestamp | Last wall metadata update. |
deleted_at | Timestamp nullable | Soft delete. |
MVP notes:
visibility, share links, and NFC URLs are the main backend unlocks overlocalStorage.- Keep wall atmosphere settings on
wallsbecause they are one-to-one and frequently loaded with the wall.
wall_items
Stores every object positioned on the wall.
| Field | Type | Notes |
|---|---|---|
id | UUID | Primary key. |
wall_id | UUID | References walls.id. |
type | Enum | artwork, quote, note, shelf, lamp, plant, folder, music_player, later more. |
catalog_item_id | UUID nullable | References object_catalog_items.id if created from catalog. |
title | Text | Display title. |
body_text | Text nullable | Quote/note text or object description. |
meaning | Text nullable | Why this belongs on the wall. |
variant | Text | Style variant, such as frame or folder style. |
color | Text | Hex color or design token. |
x | Numeric | Wall coordinate. |
y | Numeric | Wall coordinate. |
width | Numeric | Render width. |
height | Numeric | Render height. |
z_index | Integer | Stacking order. |
rotation | Numeric | Optional later support. |
is_locked | Boolean | Prevent accidental movement. |
metadata | JSON | Type-specific state that does not deserve first-class columns yet. |
created_at | Timestamp | Creation time. |
updated_at | Timestamp | Last item update. |
deleted_at | Timestamp nullable | Soft delete. |
MVP notes:
- This table replaces the local
itemsarray. - Store coordinates in a stable wall coordinate system so responsive rendering does not corrupt layout.
assets
Tracks uploaded, linked, or generated files.
| Field | Type | Notes |
|---|---|---|
id | UUID | Primary key. |
owner_user_id | UUID | References users.id. |
source_type | Enum | upload, external_url, generated, imported_data_url. |
kind | Enum | image, audio, video, document, other. |
storage_key | Text nullable | Path/key in object storage. |
external_url | Text nullable | Original URL when not stored locally. |
mime_type | Text nullable | Content type. |
file_name | Text nullable | Original file name. |
file_size_bytes | Integer nullable | File size. |
width | Integer nullable | Image/video width. |
height | Integer nullable | Image/video height. |
prompt | Text nullable | Prompt used for generated assets. |
alt_text | Text nullable | Accessibility description. |
created_at | Timestamp | Creation time. |
MVP notes:
- Artwork uploads should reference
assetsinstead of storing large data URLs in wall records. - External image URLs can be represented here even if they are not copied into storage yet.
wall_item_assets
Connects assets to wall items.
| Field | Type | Notes |
|---|---|---|
id | UUID | Primary key. |
wall_item_id | UUID | References wall_items.id. |
asset_id | UUID | References assets.id. |
role | Enum | primary_image, thumbnail, attachment, reference, window_view. |
sort_order | Integer | Ordering for multiple attachments. |
created_at | Timestamp | Creation time. |
MVP notes:
- Framed artwork normally has one
primary_image. - Folders may later attach multiple references through this same join table or through folder pages.
folders
Stores folder-specific state for a wall item of type folder.
| Field | Type | Notes |
|---|---|---|
id | UUID | Primary key. |
wall_item_id | UUID | Unique reference to wall_items.id. |
folder_type | Enum | work_in_progress, project, moodboard, manifestation, custom. |
description | Text nullable | Optional folder summary. |
created_at | Timestamp | Creation time. |
updated_at | Timestamp | Last folder update. |
MVP notes:
- A folder exists because a wall item exists; deleting the wall item should archive or delete the folder.
folder_pages
Stores pages or entries inside a folder.
| Field | Type | Notes |
|---|---|---|
id | UUID | Primary key. |
folder_id | UUID | References folders.id. |
title | Text | Page title. |
vision | Text nullable | Main notes, intention, or draft description. |
reminders | Text nullable | Next steps or reminders. |
links | Text nullable | MVP-compatible newline-separated links. Later this can normalize to folder_page_links. |
mood | Text nullable | Current MVP values include hopeful, calm, focused, energized, nostalgic. |
image_asset_id | UUID nullable | References assets.id. |
image_url | Text nullable | Temporary external visual reference URL. |
sort_order | Integer | Page ordering. |
metadata | JSON | Template-specific fields. |
created_at | Timestamp | Creation time. |
updated_at | Timestamp | Last page update. |
deleted_at | Timestamp nullable | Soft delete. |
MVP notes:
- This table maps directly to the current
FolderPagefields. - Work-in-progress folders can start with pages for idea, draft, reference, and next steps without requiring more tables.
playlists
Stores a playlist associated with either a wall or a specific music-player item.
| Field | Type | Notes |
|---|---|---|
id | UUID | Primary key. |
wall_id | UUID | References walls.id. |
wall_item_id | UUID nullable | References wall_items.id for item-specific playlists. |
title | Text | Playlist name. |
active_track_id | UUID nullable | References playlist_tracks.id. |
created_at | Timestamp | Creation time. |
updated_at | Timestamp | Last playlist update. |
MVP notes:
- The current app has one playlist per wall, displayed through the music player object.
- Supporting item-specific playlists keeps the schema open for multiple stereos later.
playlist_tracks
Stores tracks for a playlist.
| Field | Type | Notes |
|---|---|---|
id | UUID | Primary key. |
playlist_id | UUID | References playlists.id. |
title | Text | Track title. |
artist | Text nullable | Artist or channel. |
url | Text | Source URL. |
provider | Enum | youtube, spotify, soundcloud, external, unknown. |
provider_track_id | Text nullable | Provider-specific ID when parsed. |
embed_url | Text nullable | Cached embed URL if supported. |
sort_order | Integer | Track ordering. |
created_at | Timestamp | Creation time. |
updated_at | Timestamp | Last track update. |
MVP notes:
- YouTube links should parse into
provider = youtubeand optionally store the video ID.
share_links
Stores stable share URLs for walls or specific wall items.
| Field | Type | Notes |
|---|---|---|
id | UUID | Primary key. |
wall_id | UUID | References walls.id. |
target_type | Enum | wall, wall_item, folder_page. |
target_id | UUID nullable | ID of item/page when not sharing the whole wall. |
token | Text | Unique opaque URL token. |
mode | Enum | visit, later edit_invite. |
is_active | Boolean | Can disable a shared link. |
created_by_user_id | UUID | References users.id. |
created_at | Timestamp | Creation time. |
expires_at | Timestamp nullable | Optional expiration. |
last_accessed_at | Timestamp nullable | Useful for analytics/debugging. |
MVP notes:
- For public walls, the token can simply route to the wall in visit mode.
- For private walls, share links should either be blocked, require auth, or use a later invite model.
nfc_links
Stores NFC sticker bindings.
| Field | Type | Notes |
|---|---|---|
id | UUID | Primary key. |
wall_id | UUID | References walls.id. |
share_link_id | UUID nullable | References share_links.id. |
target_type | Enum | wall, wall_item, folder_page. |
target_id | UUID nullable | Destination when linking to a specific object/page. |
label | Text nullable | User-facing sticker name, such as Bedroom wall NFC. |
token | Text | Unique opaque NFC URL token. |
is_active | Boolean | Can deactivate a lost sticker. |
created_at | Timestamp | Creation time. |
last_scanned_at | Timestamp nullable | Last successful open. |
MVP notes:
- The NFC token should redirect to a visit-mode wall URL.
- NFC links should respect wall visibility and share-link policy.
object_catalog_items
Defines the object types and variants the user can add from the catalog.
| Field | Type | Notes |
|---|---|---|
id | UUID | Primary key. |
type | Enum | Matches wall_items.type. |
variant | Text | Style variant key. |
label | Text | Catalog label. |
description | Text nullable | Optional internal/product description. |
default_width | Numeric | Default item width. |
default_height | Numeric | Default item height. |
default_color | Text | Hex color or token. |
default_metadata | JSON | Default type-specific state. |
is_active | Boolean | Hide/show in catalog. |
sort_order | Integer | Catalog ordering. |
created_at | Timestamp | Creation time. |
updated_at | Timestamp | Last catalog update. |
MVP notes:
- This can start as seeded data rather than an admin-managed table.
- If catalog variants remain hard-coded in the client for the next phase, this table can be delayed.
wall_events
Optional activity stream for sync, debugging, undo history, or future collaboration.
| Field | Type | Notes |
|---|---|---|
id | UUID | Primary key. |
wall_id | UUID | References walls.id. |
user_id | UUID nullable | Actor, if known. |
event_type | Text | Examples: item_created, item_moved, visibility_changed, share_link_created. |
entity_type | Text nullable | Affected entity type. |
entity_id | UUID nullable | Affected entity ID. |
payload | JSON | Event details. |
created_at | Timestamp | Creation time. |
MVP notes:
- Not required for first CRUD backend.
- Useful if we want undo, history, or collaboration later.
Relationships
- One
userhas manywalls. - One
wallhas manywall_items. - One
wall_itemcan have manywall_item_assets. - One
assetcan be reused by many wall items or pages. - One
folderbelongs to one folder-typewall_item. - One
folderhas manyfolder_pages. - One
wallhas zero or moreplaylists. - One
playlisthas manyplaylist_tracks. - One
wallhas manyshare_links. - One
wallhas manynfc_links. - One
object_catalog_itemcan be the source for manywall_items.
Recommended MVP Build Order
1. users 2. walls 3. wall_items 4. assets 5. wall_item_assets 6. folders 7. folder_pages 8. playlists 9. playlist_tracks 10. share_links 11. nfc_links
Defer object_catalog_items and wall_events unless the first backend also needs remote-configurable catalog data or history.
Important Enums
Suggested starting values:
wall_visibility:private,publicwall_mode:edit,visitwall_item_type:artwork,quote,note,shelf,lamp,plant,folder,music_playerwindow_view_type:none,preset,prompt,imageasset_source_type:upload,external_url,generated,imported_data_urlasset_kind:image,audio,video,document,otherfolder_type:work_in_progress,project,moodboard,manifestation,customtrack_provider:youtube,spotify,soundcloud,external,unknownshare_target_type:wall,wall_item,folder_page
Data To Migrate From Local MVP
Current localStorage wall state can map into backend records like this:
SanctuaryState.wallConfig->walls.material,walls.location_label,walls.window_position,walls.lightingSanctuaryState.items[]->wall_items- Artwork
imageUrlvalues ->assetspluswall_item_assets - Folder item
pages[]->foldersplusfolder_pages SanctuaryState.playlist->playlistsplusplaylist_tracksSanctuaryState.mode-> client UI state orwalls.mode_default
Open Backend Decisions
- Auth: account-required from day one, anonymous walls first, or hybrid?
- Sharing: should a public wall use a simple slug, an opaque token, or both?
- Private sharing: should private walls support invite links in the first backend release?
- Assets: should external URLs stay remote, or should the backend copy them into owned storage?
- Coordinates: should all walls use a fixed virtual canvas size, or store responsive percentages?
- Generated outside-window views: should prompt text and generated output be stored together on
walls, or as separate versioned assets? - Deletion: should deleted objects be soft-deleted for undo/history or hard-deleted for simplicity?