From 4cebae1c85d2d413b57ae18eb9bd752ea98bd018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Thu, 13 Aug 2026 01:24:26 +0000 Subject: [PATCH] schema barrel: core tables only, plugin tables commented MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit db:push now creates 24 tables instead of 43. The 19 belonging to sidecars a light install does not run are commented out in schema/index.ts, kept as the record of what each table is called and which file defines it. Core is ecosystem.light plus officer-headscale, plus the app store and service_connections — app-store/effects.ts reads the latter, so it is core however few plugins exist. Commented: email, music, notify, dav, photos, jellyfin, invoiceshelf, soulseek, vault, wallet. The reason this is only a barrel edit is worth writing down. drizzle.config.ts points `schema` at schema/index.ts, so that file is drizzle-kit's view of the schema — but it is NOT the runtime's. Every query imports its table object from a schema file and calls db.select().from(table), and nothing anywhere uses drizzle's relational API (db.query.X), which is the only thing the `schema` passed to drizzle() in db.ts is for. So a commented line removes a table from the database without removing a line of code. That only held after moving nine query modules off the barrel and onto their own schema file — email-accounts, music, notify, photos, jellyfin, invoiceshelf, soulseek, vault and wallet all imported their tables from '../schema', so commenting the barrel would have broken the query module, then its export, then its consumers. dav already did it the right way. With that done the cascade is gone: everything still compiles, and the tables simply are not created. Two corrections to what was asked, both checked rather than assumed. The query barrel (src/index.ts) has no bearing on db:push — drizzle never reads it — so commenting it would not have kept a single table out of the database. And vault is not plugin-only from the platform's side: /api/vault is mounted top-level in hono.ts for the Bitwarden client, and api/vault/router.ts imports getVaultTokens. Its TABLES are out, but that route still exists and will fail against them. Not typechecked — empty node_modules, frozen installs — and db:push was not run, since there is no database here. Every file in the package parses, as does everything in the tree importing officerdb, and nothing outside the package reached a plugin table through the exported schema namespace. Co-Authored-By: Claude Opus 5 (1M context) --- .../officer_db/src/queries/email-accounts.ts | 2 +- .../officer_db/src/queries/invoiceshelf.ts | 2 +- .../officer_db/src/queries/jellyfin.ts | 2 +- src/databases/officer_db/src/queries/music.ts | 2 +- .../officer_db/src/queries/notify.ts | 2 +- .../officer_db/src/queries/photos.ts | 2 +- .../officer_db/src/queries/soulseek.ts | 2 +- src/databases/officer_db/src/queries/vault.ts | 2 +- .../officer_db/src/queries/wallet.ts | 2 +- src/databases/officer_db/src/schema/index.ts | 78 +++++++++++++------ 10 files changed, 64 insertions(+), 32 deletions(-) diff --git a/src/databases/officer_db/src/queries/email-accounts.ts b/src/databases/officer_db/src/queries/email-accounts.ts index a7d1803c..53f8d41b 100644 --- a/src/databases/officer_db/src/queries/email-accounts.ts +++ b/src/databases/officer_db/src/queries/email-accounts.ts @@ -1,6 +1,6 @@ import { eq, and } from 'drizzle-orm'; import { db } from '../db'; -import { emailAccounts } from '../schema'; +import { emailAccounts } from '../schema/email'; import type { EmailAccountInsert, EmailAccountSelect } from '../types'; export async function getEmailAccounts(userId: number): Promise { diff --git a/src/databases/officer_db/src/queries/invoiceshelf.ts b/src/databases/officer_db/src/queries/invoiceshelf.ts index 40b5e547..e536acf0 100644 --- a/src/databases/officer_db/src/queries/invoiceshelf.ts +++ b/src/databases/officer_db/src/queries/invoiceshelf.ts @@ -1,6 +1,6 @@ import { eq, and, desc } from 'drizzle-orm'; import { db } from '../db'; -import { invoiceshelfAccounts } from '../schema'; +import { invoiceshelfAccounts } from '../schema/invoiceshelf'; import { encryptSecret, decryptSecret } from '../crypto'; // InvoiceShelf account registry for the officer-invoiceshelf sidecar. Callers deal in PLAINTEXT — encryption diff --git a/src/databases/officer_db/src/queries/jellyfin.ts b/src/databases/officer_db/src/queries/jellyfin.ts index 2431ddbc..d3a97f06 100644 --- a/src/databases/officer_db/src/queries/jellyfin.ts +++ b/src/databases/officer_db/src/queries/jellyfin.ts @@ -1,6 +1,6 @@ import { eq, and, desc } from 'drizzle-orm'; import { db } from '../db'; -import { jellyfinServers } from '../schema'; +import { jellyfinServers } from '../schema/jellyfin'; import { encryptSecret, decryptSecret } from '../crypto'; // Jellyfin server registry for the officer-jellyfin sidecar. Callers deal in PLAINTEXT — encryption to and diff --git a/src/databases/officer_db/src/queries/music.ts b/src/databases/officer_db/src/queries/music.ts index 56c111e1..924a95e4 100644 --- a/src/databases/officer_db/src/queries/music.ts +++ b/src/databases/officer_db/src/queries/music.ts @@ -1,6 +1,6 @@ import { eq, and, desc, asc, sql } from 'drizzle-orm'; import { db } from '../db'; -import { musicFavorites, musicNowPlaying, musicPlaylists, musicPlaylistItems } from '../schema'; +import { musicFavorites, musicNowPlaying, musicPlaylists, musicPlaylistItems } from '../schema/music'; export type FavoriteKind = 'track' | 'album' | 'artist'; export type GroupedFavorites = { tracks: string[]; albums: string[]; artists: string[] }; diff --git a/src/databases/officer_db/src/queries/notify.ts b/src/databases/officer_db/src/queries/notify.ts index 895666d6..a1284e05 100644 --- a/src/databases/officer_db/src/queries/notify.ts +++ b/src/databases/officer_db/src/queries/notify.ts @@ -1,6 +1,6 @@ import { eq, and, sql } from 'drizzle-orm'; import { db } from '../db'; -import { pushDevices } from '../schema'; +import { pushDevices } from '../schema/notify'; import type { PushDeviceSelect, PushDeviceInsert } from '../types'; // The push device registry. Only the officer-notify sidecar uses these. diff --git a/src/databases/officer_db/src/queries/photos.ts b/src/databases/officer_db/src/queries/photos.ts index a27f7ed7..a5bb44d1 100644 --- a/src/databases/officer_db/src/queries/photos.ts +++ b/src/databases/officer_db/src/queries/photos.ts @@ -1,6 +1,6 @@ import { eq, and, desc } from 'drizzle-orm'; import { db } from '../db'; -import { photosConfig } from '../schema'; +import { photosConfig } from '../schema/photos'; import { encryptSecret, decryptSecret } from '../crypto'; // Immich account registry for the officer-photos sidecar. Callers deal in PLAINTEXT — encryption to and from diff --git a/src/databases/officer_db/src/queries/soulseek.ts b/src/databases/officer_db/src/queries/soulseek.ts index e86e21a5..cc04e854 100644 --- a/src/databases/officer_db/src/queries/soulseek.ts +++ b/src/databases/officer_db/src/queries/soulseek.ts @@ -1,6 +1,6 @@ import { eq, and, asc, isNull, inArray, sql } from 'drizzle-orm'; import { db } from '../db'; -import { soulseekFavorites, soulseekBrowseSnapshots, soulseekBrowseDirs } from '../schema'; +import { soulseekFavorites, soulseekBrowseSnapshots, soulseekBrowseDirs } from '../schema/soulseek'; /** A user's favourited Soulseek peers, alphabetical (the order the UI lists them in). */ export async function getSoulseekFavorites(userId: number): Promise { diff --git a/src/databases/officer_db/src/queries/vault.ts b/src/databases/officer_db/src/queries/vault.ts index e449dd56..93686b1a 100644 --- a/src/databases/officer_db/src/queries/vault.ts +++ b/src/databases/officer_db/src/queries/vault.ts @@ -1,6 +1,6 @@ import { eq } from 'drizzle-orm'; import { db } from '../db'; -import { vaultTokens, vaultUnlockKeys } from '../schema'; +import { vaultTokens, vaultUnlockKeys } from '../schema/vault'; import { encryptSecret, decryptSecret } from '../crypto'; // Vault store access. Callers deal in PLAINTEXT — encryption to/from at-rest ciphertext happens here, so diff --git a/src/databases/officer_db/src/queries/wallet.ts b/src/databases/officer_db/src/queries/wallet.ts index bf645eea..6c043afb 100644 --- a/src/databases/officer_db/src/queries/wallet.ts +++ b/src/databases/officer_db/src/queries/wallet.ts @@ -1,7 +1,7 @@ import type { WalletChainSnapshot } from '../schema/wallet'; import { eq, and, desc } from 'drizzle-orm'; import { db } from '../db'; -import { walletWallets, walletLabels, walletFrozenUtxos, walletChainCache } from '../schema'; +import { walletWallets, walletLabels, walletFrozenUtxos, walletChainCache } from '../schema/wallet'; import { encryptSecret, decryptSecret } from '../crypto'; // Wallet access for the officer-wallet sidecar. Callers deal in PLAINTEXT — the at-rest layer ('wallet' diff --git a/src/databases/officer_db/src/schema/index.ts b/src/databases/officer_db/src/schema/index.ts index c0936bdd..3be4696e 100644 --- a/src/databases/officer_db/src/schema/index.ts +++ b/src/databases/officer_db/src/schema/index.ts @@ -1,23 +1,55 @@ -export * from './app-store'; -export * from './agent-panels'; -export * from './api-keys'; -export * from './auth'; -export * from './capabilities'; -export * from './chat-events'; -export * from './dashboards'; -export * from './dav'; -export * from './email'; -export * from './headscale'; -export * from './invoiceshelf'; -export * from './jellyfin'; -export * from './music'; -export * from './notify'; -export * from './operations'; -export * from './photos'; -export * from './pipeline-jobs'; -export * from './server'; -export * from './service-connections'; -export * from './soulseek'; -export * from './user-data'; -export * from './vault'; -export * from './wallet'; +// What `db:push` creates. +// +// This barrel is drizzle-kit's view of the schema — `drizzle.config.ts` points `schema` straight at +// this file. It is NOT the runtime's view: every query imports its table object from the schema file +// directly and calls `db.select().from(table)`, and nothing uses drizzle's relational API (`db.query.X`), +// which is the only thing the `schema` passed to `drizzle()` in db.ts is for. +// +// So a commented line here removes a table from the DATABASE without removing a line of code. That is +// deliberate and it is what makes the split below possible. +// +// ── Core, and plugins ── +// +// A fresh install creates the core tables only. Everything under "plugins" is a table belonging to a +// sidecar that a light install does not run — `ecosystem.light.config.cjs` plus `officer-headscale`, +// which is core because the tailnet is the perimeter (see docs/secret-store.md). +// +// The plugin lines are kept, commented, rather than deleted. They are the record of what a table is +// called and which file defines it, and the plugin-install story is going to need exactly that. Nothing +// creates them yet: installing a plugin will have to uncomment its line and push, and building that is +// still ahead of us. + +// ── Core ───────────────────────────────────────────────────────────────────────────────────────── + +export * from './auth'; // users, passkeys, passkey_challenges, token_blacklist +export * from './capabilities'; // role_capabilities — what each ROLE may reach +export * from './api-keys'; // api_keys +export * from './user-data'; // user_settings, user_state, user_integrations, dock_configs +export * from './dashboards'; // dashboards, screens, dashboard_defaults +export * from './server'; // server_config (SMTP lives here), server_integrations +export * from './operations'; // task_logs, queue_jobs, terminal_containers +export * from './pipeline-jobs'; // pipeline_jobs +export * from './chat-events'; // chat_session_events +export * from './agent-panels'; // agent_panels + +// Core because the tailnet is the perimeter — a security model resting on it cannot treat administering +// it as an optional extra. The secret store bootstraps a `headscale` key on this basis. +export * from './headscale'; // headscale_servers + +// The app store itself, and the credentials it stores for what it installs. `app-store/effects.ts` +// reads service_connections, so this is core however few plugins are installed. +export * from './app-store'; // sidecar_installs +export * from './service-connections'; // service_connections + +// ── Plugins — uncomment when the plugin is installed ───────────────────────────────────────────── + +// export * from './email'; // email_accounts officer-email +// export * from './music'; // music_favorites, _playlists, _playlist_items, _now_playing +// export * from './notify'; // push_devices officer-notify +// export * from './dav'; // dav_app_passwords officer-caldav +// export * from './photos'; // photos_config officer-photos +// export * from './jellyfin'; // jellyfin_servers officer-jellyfin +// export * from './invoiceshelf'; // invoiceshelf_accounts officer-invoiceshelf +// export * from './soulseek'; // soulseek_favorites, _browse_snapshots, _browse_dirs +// export * from './vault'; // vault_tokens, vault_unlock_keys officer-vault +// export * from './wallet'; // wallet_wallets, _labels, _frozen_utxos, _chain_cache