Date: 2026-06-06
The creative catalog itself is PostgreSQL-backed today, not file-backed. The surrounding Create section is mixed:
- Catalog scraps, ingredients, source links, refs, ingredient relations, tags, revisions, and catalog media pointers live in PostgreSQL via
server/services/catalogDB.js,server/lib/db.js, andserver/scripts/init-db.sql. - Most native Create workflow state still lives under
data/as JSON, JSONL, Markdown, and binary media files. - Memory can use PostgreSQL, but it still has a file fallback through
MEMORY_BACKEND=fileor auto-detect fallback. - User-defined catalog types are still stored in
data/settings.json, even though the records that use those types live in PostgreSQL.
This means PortOS already treats PostgreSQL as essential for the catalog, but the install/runtime posture still treats it as optional. That mismatch will get more expensive as Create adds richer relationships.
| Area | Storage | Notes |
|---|---|---|
| Creative catalog scraps | catalog_scraps |
Raw pasted/imported text, chunking metadata, embeddings, FTS, sync sequence. |
| Creative catalog ingredients | catalog_ingredients |
Typed records with JSONB payload, tags, embeddings, generated search_tsv, soft delete, sync sequence. |
| Scrap-to-ingredient provenance | catalog_ingredient_sources |
Many-to-many extraction source links plus optional spans. |
| Catalog-to-app refs | catalog_ingredient_refs |
Links catalog rows to universe, series, issue, work, and creative-director records by string id. |
| Ingredient graph edges | catalog_ingredient_relations |
Directed ingredient-to-ingredient edges. This is already the strongest argument for Postgres as the catalog graph store. |
| Catalog tags | catalog_tags |
First-class canonical tag table, while catalog_ingredients.tags TEXT[] remains the simple write path. |
| Catalog revisions | catalog_ingredient_revisions |
Local-only audit trail, not federated. |
| Catalog media pointers | catalog_ingredient_media |
References media keys in the file-backed media library; does not duplicate bytes. |
| Memory, when available | memories, memory_links |
PostgreSQL + pgvector when explicitly selected or auto-detected. File fallback still exists. |
Relevant files:
server/lib/db.jsserver/scripts/init-db.sqlserver/services/catalogDB.jsserver/routes/catalog.jsserver/services/memoryBackend.jsserver/services/memoryDB.js
| Area | Storage | Postgres fit |
|---|---|---|
| Universe Builder | data/universes/{id}/index.json via collectionStore; legacy data/universe-builder.json migrations |
Strong. Universes contain canon, render pointers, locks, soft deletes, and cross-links to series/catalog. |
| Pipeline series | data/pipeline-series/{id}/index.json |
Strong. Series has universeId, writersRoomWorkId, seasons, arc state, and catalog cast panels. |
| Pipeline issues | data/pipeline-issues/{id}/index.json |
Strong. Issues are children of series and carry stage status, run pointers, output state, and production artifacts. |
| Story Builder | data/story-builder/{id}/index.json |
Strong. Mostly workflow metadata with universeId and seriesId FKs. |
| Writers Room manifests | data/writers-room/works/{workId}/manifest.json |
Medium-strong. Work metadata belongs in DB; draft bodies can remain Markdown files for now. |
| Writers Room drafts | data/writers-room/works/{workId}/drafts/{draftId}.md |
Keep file-backed initially. Long prose benefits from file workflows and possible external editing. Add DB metadata/index rows. |
| Creative Director projects | data/creative-director-projects.json |
Very strong. Project, treatment, scenes, run history, source issue, collection id, and final video id are relational. Current monolithic JSON is a bottleneck. |
| Media collections | data/media-collections/{id}/index.json |
Strong. Collections are many-to-many links over assets, universes, series, and catalog media pointers. |
| Media job queue | data/media-jobs.json |
Medium. Runtime queue state can remain file-backed short term, but job history and artifact lineage should move to DB. |
| Image/video history | data/history.jsonl, data/video-history.json |
Strong for metadata. Bytes should remain files. |
| Prompt Manager | data/prompts/stage-config.json, variables.json, template .md files |
Medium. Stage config/variables are relational enough for DB; templates can remain Markdown files until versioning needs DB. |
| Catalog user types | data/settings.json#catalogUserTypes |
Strong. Type registry should move into Postgres because it defines catalog row semantics and must version/sync with catalog data. |
| Area | Storage | Rationale |
|---|---|---|
| Generated images | data/images/* plus .metadata.json sidecars |
Keep bytes on disk. Move searchable metadata, provenance, favorites, notes, and sidecar fields to DB. |
| Reference images | data/image-refs/* |
Keep bytes on disk; add DB asset rows where durable references matter. |
| Generated videos | data/videos/*, data/video-thumbnails/* |
Keep bytes on disk; move metadata/history/lineage to DB. |
| Audio/music | data/audio/*, data/music/* |
Keep bytes on disk; move library metadata and usage links to DB. |
| LoRAs/models | data/loras/*, data/media-models.json, external HF caches |
Mostly file/model registry. DB can index installed assets later, but not critical for Create relationships. |
| Upload staging | data/uploads/* |
Ephemeral file staging. Do not put in DB except job references. |
| Area | Storage | Rationale |
|---|---|---|
| MortalLoom / Health / Meatspace health data | data/health, data/meatspace, MortalLoom iCloud store |
Keep file-backed to preserve iCloud/file sync workflows and avoid forcing sensitive health records through the app DB before we design that boundary. |
| Settings secrets/config | data/settings.json |
Split later. Not all settings warrant DB storage, but catalog type definitions should move sooner. |
| App scaffolds/repos/browser profiles | data/repos, data/browser-profile, generated project files |
These are inherently filesystem-oriented. |
- PostgreSQL is optional in setup, but the catalog already requires it. If DB setup is skipped or unhealthy, catalog features have no equivalent file-backed implementation.
- The Create section uses string ids across stores instead of real constraints.
catalog_ingredient_refs.ref_id,series.universeId,issue.seriesId,storyBuilder.universeId,storyBuilder.seriesId,creativeDirector.sourceIssueId, andmediaCollection.seriesId/universeIdcan drift. - File-backed collection records require app-level scans for cross-record queries. This is workable for small installs, but poor for “show all artifacts, refs, casts, scenes, issues, and catalog rows related to this universe.”
- Monolithic files still exist in hot paths (
creative-director-projects.json,media-jobs.json,video-history.json), creating unnecessary write contention and growth risk. - Asset metadata is split between DB pointers, JSON history, and sidecar files. The bytes should stay on disk, but metadata should have one queryable home.
- Catalog user-defined types live in settings instead of the catalog database. That makes type evolution and sync/version checks harder than they need to be.
PostgreSQL should become a mandatory install/runtime dependency for PortOS, defaulting to native PostgreSQL where available and keeping Docker as a supported hosting mode. File storage should remain first-class for large assets, external-file workflows, and explicitly file-sync-oriented domains.
Target boundaries:
- PostgreSQL owns app-native records, relationships, indexes, sync cursors, tombstones, lineage, status, and searchable metadata.
- Files own large binary payloads, long externally-editable prose bodies, model weights, temporary uploads, and iCloud-backed health/life stores.
- File assets referenced from DB get stable
asset_keyormedia_keyrows plus integrity metadata. The DB points to files; it does not absorb the bytes.
- Remove
fileas a normal setup choice inscripts/setup-db.js. - Keep Docker and native as hosting modes; default to native when a healthy local PostgreSQL 17 + pgvector is available, otherwise offer Docker setup.
- Fail startup or mark the app unhealthy when required DB schema is unavailable, instead of silently falling back to file memory mode.
- Keep a temporary
MEMORY_BACKEND=fileescape hatch only for development/tests, documented as unsupported for production app installs. - Update backup/restore docs now that PostgreSQL backups are part of required system state.
Create a small documented contract for every domain:
db-primary: app-native relational records.file-primary: external-file or sync-sensitive records.asset-file-db-indexed: bytes on disk, metadata in DB.ephemeral-file: queues/uploads/transient state.
Add a checklist to new feature docs and PR reviews so new Create features must justify any new JSON store.
Start with the highest-value relational surfaces:
-
Creative Director
- Tables:
creative_director_projects,creative_director_scenes,creative_director_runs. - Keep videos/thumbnails in files.
- Add migration from
data/creative-director-projects.json.
- Tables:
-
Media asset index
- Tables:
media_assets,media_collections,media_collection_items,media_jobs, maybemedia_asset_sidecars. - Migrate
data/history.jsonl,data/video-history.json,data/media-collections/*, and durable portions ofdata/media-jobs.json. - Keep
data/images,data/videos, thumbnails, audio, and music as bytes on disk.
- Tables:
-
Story universe/pipeline metadata
- Tables for universes, series, issues, story-builder sessions, stage states, seasons, locks, and tombstones.
- Preserve existing service APIs first; swap their storage adapter underneath.
- Backfill
catalog_ingredient_refsto use canonical DB ids and enforce integrity as much as possible.
-
Writers Room metadata
- Tables for folders, works, draft versions, segment indexes, media collection links, and promote-to-pipeline links.
- Keep
.mddraft bodies file-backed initially; store hashes/word counts/segment metadata in DB.
-
Prompt Manager metadata
- Tables for stages, variables, providers/model overrides, and template versions.
- Keep Markdown template bodies file-backed until DB versioning/search is needed.
- Move catalog user-defined types from
settings.jsoninto catalog DB tables. - Replace or supplement
catalog_ingredient_refs.ref_idstring references with typed DB-backed target mappings. - Add DB-level integrity where possible for app-native targets.
- Add resolver queries for “everything related to this universe/series/issue/work/project.”
- Preserve peer sync semantics by adding per-table sequence cursors before removing old file sync paths.
- Write idempotent importers from current files into DB tables.
- Keep old files as read-only migration sources for at least one release.
- Add export commands for DB-backed domains that can regenerate a portable
data/bundle when needed. - Expand backup verification to assert table counts for new Create tables, not just memory/catalog.
- Add restore tests that verify DB rows and referenced file assets remain coherent.
- Do not move generated image/video/audio bytes into PostgreSQL.
- Do not migrate MortalLoom, Health, or iCloud-backed Meatspace data yet.
- Do not remove file-backed Markdown drafts until we design external editing/versioning.
- Do not rewrite all services at once. Preserve existing HTTP/client APIs while swapping storage behind service modules.
- #998 Make PostgreSQL mandatory for PortOS installs and remove normal file-backend fallback.
- #1000 Add DB-backed media asset and collection metadata while keeping bytes on disk.
- #997 Migrate Creative Director project/treatment/run state from monolithic JSON to PostgreSQL.
- #999 Design the Create relational schema for universes, series, issues, Story Builder sessions, Writers Room metadata, and catalog refs. — Design complete:
2026-06-07-create-relational-schema-design.md; implementation tracked as #1014–#1018. - #1001 Move catalog user-defined types from settings into PostgreSQL.