Skip to main content

Core user and media schema

This reference describes the PostgreSQL baseline installed by the ordered users and media-library Supabase migrations. The authenticated media-upload capability uses these tables as its system of record; this page describes their persistence rules rather than its HTTP interface.

Identity and profiles​

users contains the application-owned identity.

ColumnTypeRules
idUUIDPrimary key; generated by PostgreSQL.
identity_provider_idtextRequired and unique opaque identifier from the active authentication provider.
created_at, updated_attimestamptzRequired creation and update timestamps.

identity_provider_id intentionally has no provider namespace. The system supports one authentication provider at a time. During a provider migration, update this value while retaining users.id; profile and media ownership use the stable internal UUID.

profiles is a zero-or-one extension of a user:

ColumnTypeRules
user_idUUIDPrimary key and foreign key to users.id.
display_nametextOptional.
biotextOptional.
created_at, updated_attimestamptzRequired creation and update timestamps.

Deleting a user cascades to its profile. A second profile for the same user is rejected because user_id is the profile primary key.

Upload attempts​

media_uploads records an upload workflow before a file becomes durable media.

ColumnTypeRules
idUUIDPrimary key; generated by PostgreSQL.
owner_user_idUUIDRequired reference to users.id.
purposetextRequired, trimmed non-empty value such as avatar or library.
statusmedia_upload_statusRequired; defaults to pending.
original_filename, requested_content_typetextRequired intended file attributes.
expected_size_bytesbigintRequired and non-negative.
checksumtextOptional.
upload_keytextRequired, trimmed non-empty, and unique object-upload key.
expires_attimestamptzRequired expiration time.
uploaded_at, completed_attimestamptzOptional lifecycle times.
created_at, updated_attimestamptzRequired creation and update timestamps.

The closed media_upload_status enum permits only pending, uploaded, completed, failed, and expired. Uploads are indexed by (owner_user_id, status) for ownership and lifecycle lookups.

Durable media items​

media_library_items records a finalized file.

ColumnTypeRules
idUUIDPrimary key; generated by PostgreSQL.
owner_user_idUUIDRequired reference to users.id.
media_upload_idUUIDRequired, unique source upload reference.
purposetextRequired, trimmed non-empty use-case label.
final_keytextRequired, trimmed non-empty, unique durable S3 object key.
content_type, original_filenametextRequired file attributes.
size_bytesbigintRequired and non-negative.
checksumtextOptional.
metadataJSONBRequired JSON object; defaults to {} for extensible attributes such as image dimensions.
created_at, updated_attimestamptzRequired creation and update timestamps.

final_key is deliberately not a CloudFront URL. Delivery code combines it with the configured CloudFront hostname, allowing a distribution or domain change without rewriting database rows.

Relationship constraints​

A media item can reference only one upload, and an upload can produce at most one media item. A composite foreign key requires a library item's owner_user_id to match the owner of its source upload. These rules make retries unable to create duplicate final media and reject cross-user finalization.

Media rows restrict deletion of their owner and source upload. Remove database rows and their external objects through a future explicit cleanup workflow; deleting a user does not cascade to media.

The database does not verify that a referenced upload is already completed. The media finalization command verifies the private object, sets that status, and creates its library item in one transaction. The command also owns authorization: these tables have no authentication-provider foreign keys, RLS policies, or direct browser access model.

Timestamp writers​

PostgreSQL defaults created_at and updated_at when it inserts a row. The migrations do not install an update trigger. Repositories write updated_at explicitly when they update a row.