13 KiB
| created | path | project | status | tags | type | updated | worker | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-05-15 | Sources/Homelab | postgres-consolidation-reflection-layer | active |
|
session-note | 2026-05-15 | 2 |
Postgres Consolidation — Worker 2, Phase 0 recon
Snapshot of the homelab Postgres instance before any consolidation
work. Recorded from homelab-postgres (pgvector/pgvector:pg17),
container in /opt/projects/docker/postgres/, port 127.0.0.1:5433,
volume ob1_data (external).
Postgres instance
- Container:
homelab-postgres(imagepgvector/pgvector:pg17) - Volume:
ob1_data(external — survives stack down) - Network:
homelab(aliashomelab-postgres, alsopostgres) - Healthy, up 40 hours.
- POSTGRES_DB default =
openbrain.
Databases on instance
| Name | Owner | Size | pgvector | Notes |
|---|---|---|---|---|
herbylab |
herbylab_user |
7958 kB | NO | Holds the herby-dev artifact store |
openbrain |
postgres |
8838 kB | YES (0.8.2) | OB1 captures live here |
trellis |
trellis_user |
8262 kB | NO | Trellis MCP threads/events |
postgres |
postgres |
7478 kB | - | default; not touched |
No other user databases. Plan's "instance hosts only wiki, ob1, trellis"
holds — under the actual names herbylab / openbrain / trellis.
Existing roles
postgres— Superuser, Create role, Create DB, Replication, Bypass RLSherbylab_user— ownsherbylab.herbylabschematrellis_user— ownstrellis.trellisschema
No lovebug role exists. No ob1_mcp / vault_mcp / trellis_mcp
service users yet — these come in Phase 2.
Schemas, tables, row counts
herbylab DB
- Schemas:
herbylab(ownerherbylab_user),public. - Tables in
herbylab:projects— 0 rows (8192 B; one empty heap page)artifacts— 0 rows (8192 B; one empty heap page)alembic_version— 1 row (9c7e3f1a2b4d)
- Custom enums:
herbylab.project_status,herbylab.artifact_type,herbylab.project_domain. - Triggers / functions:
projects_notify(NOTIFY on row change),artifacts_set_updated_at. - No vector columns.
Why empty (verified, not a missed lookup): the schema was created
by alembic migration 9c7e3f1a2b4d on the unmerged branch
claude/competent-knuth-de9483 (commit 90bc6a9 Phase 1: herbylab Postgres schema infrastructure). That branch also has commits
8e408c5 Phase 2: polymorphic artifact tools backed by Postgres + vault and 78ec094 Phase 3: Trello projection via n8n which would
have wired writes into the schema, but the branch is not merged to
main.
The deployed herbydev-mcp runs main's code, which has zero DB
access:
pyproject.tomldeclares no DB driver (no psycopg / sqlalchemy / asyncpg).mcp_server/tools/memory.pyandmcp_server/tools/tasks.pyare empty stub files marked "Phase 2. Do not implement until workflow patterns are established."mcp_server/tools/vault.pywrites viacore/git_writer.py→ filesystem + git commit + push to gitea. No database insertion.HERBYLAB_DATABASE_URLis plumbed through compose env but never read by the running code.
So the DB schema exists from an integration-test run of the unmerged
branch's migration; no code path on main ever populates it.
Implication for consolidation: the wiki schema in petalbrain is
genuinely empty content-wise. Worker 4's vault-indexing source is the
filesystem (/opt/projects/wiki-vault/), not a wiki.notes /
wiki.artifacts table. If competent-knuth-de9483 lands later, its
artifact tooling will need to be re-pointed at petalbrain.wiki.*
instead of herbylab.herbylab.*, and the Phase 4 reflection design
should account for wiki.artifacts becoming a populated source over
time.
openbrain DB
- Schemas:
publiconly. - Tables in
public:thoughts— 70 rows (live; daemons writing today)
- pgvector 0.8.2 in
public.
public.thoughts shape (relevant columns):
id bigint PK (nextval thoughts_id_seq)
content text NOT NULL
embedding vector(768)
metadata jsonb DEFAULT '{}'
user_id text NOT NULL DEFAULT 'travis'
created_at timestamptz DEFAULT CURRENT_TIMESTAMP
updated_at timestamptz DEFAULT CURRENT_TIMESTAMP
Existing indexes on public.thoughts:
thoughts_pkey(id)idx_thoughts_created_at(created_at DESC)idx_thoughts_embedding_hnsw— hnsw (embedding vector_cosine_ops) WITH (m=16, ef_construction=64) — already matches the locked Phase 4 spec forpublic.embeddings.idx_thoughts_fingerprint— UNIQUE (md5(content), user_id)idx_thoughts_metadata— gin (metadata)idx_thoughts_user_created(user_id, created_at DESC)idx_thoughts_user_id(user_id)
Additional objects:
- Function
match_thoughts(query_embedding vector, match_threshold float, match_count int, filter jsonb, p_user_id text)— bare-vectorparam (no dim), so dimension changes flow through unchanged. - Trigger
thoughts_updated_at->update_updated_at().
Live writer state: 70 rows total, 0 with NULL embedding, 2 still flagged metadata->>'pending' = 'true' (enricher backlog). Most recent row id 255 at 2026-05-16 02:07:23 UTC. Watcher and enricher daemons confirmed live.
trellis DB
- Schemas:
trellis(ownertrellis_user),public. - Tables in
trellis:threads— 13 rowsevents— 50 rowstags— 42 rowsthread_tags— 55 rowsalembic_version— 1 row
- No vector columns.
OB1 embedding model
From ob1-mcp container env (and /opt/projects/homelab/ob1-deploy/compose.yml):
EMBEDDING_PROVIDER=ollamaOLLAMA_URL=http://ollama:11434EMBEDDING_MODEL=nomic-embed-text(768-d output)CHAT_MODEL=openai/gpt-4o-mini(mcp side)CHAT_MODEL=llama3.2:3b(enricher side)
Migration trail:
init.sqloriginally declaredvector(1536)fortext-embedding-3-small.migrations/001_embedding_dim_768.sql(applied 2026-05-14) dropped the HNSW index, altered the column tovector(768), recreated the index. Requiredthoughtsto be empty pre-migration.
Conclusion: the shared public.embeddings table goes in as
vector(768) to match nomic-embed-text, no model switch needed. HNSW
index spec from Travis's overrides (vector_cosine_ops, m=16, ef_construction=64) matches the existing OB1 convention exactly.
MCP server connection configurations
| Container | Image | DB host / DB / user | Live? |
|---|---|---|---|
herbydev-mcp |
herbydev-mcp |
homelab-postgres:5432/herbylab as herbylab_user |
Up 40h |
vault-mcp |
vault-mcp |
vault-postgres:5432/herbylab as herbylab_user |
Up 59m |
ob1-mcp |
ob1-mcp |
homelab-postgres:5432/openbrain as postgres |
Up 14h |
ob1-watcher |
ob1-watcher |
(talks to ob1-mcp HTTP, not DB directly) | Up 26h |
ob1-enricher |
ob1-enricher |
(uses OLLAMA_URL; DB password set but no direct connect string visible) |
Up 14h |
trellis-mcp |
trellis-trellis-mcp |
homelab-postgres:5432/trellis as trellis_user |
Up 40h healthy |
Connection strings live in:
vault-mcp/herbydev-mcp—/opt/projects/homelab/herby-dev/deploy/.env(HERBYLAB_DATABASE_URL)ob1-mcp/ob1-enricher—/opt/projects/homelab/ob1-deploy/.env(POSTGRES_PASSWORD+ per-serviceDB_*env incompose.yml)trellis-mcp—/opt/projects/homelab/trellis-mcp/(compose env,DATABASE_URL)
Anomaly: vault-mcp references hostname vault-postgres which
does not resolve on the homelab network (alias is homelab-postgres
postgres). This is consistent with Travis's mid-flight rename rollout — the container is currently running OLD code per the priming, and the connection-string update is part of the unfinished rollout. Not blocking Phase 1, but Phase 3 must coordinate with the rename completion.
Backup destination
Local-only on herbydev (Travis decision, 2026-05-15). The NAS reference in the plan was unintended — disregard it.
- Destination directory:
/opt/backups/postgres-consolidation/(does not exist yet; Phase 1 step zero ismkdir -pit). - Local disk on
/: 14 G free of 97 G (86% used). - Combined DB size is ~25 MB, so headroom is ample for both Phase 1 bulk dumps and Phase 2-start fresh dumps.
- Format:
pg_dumpall(full instance) + per-databasepg_dumpin custom format (-Fc), gzipped. Filenames include UTC timestamp. - No NAS / Tailscale / off-host destination involved.
Coordination state — running daemons
ob1-watcher and ob1-enricher are both live and writing to
openbrain.public.thoughts (latest write ~5 minutes before this
note). Per priming, the Phase 2 ALTER TABLE public.thoughts SET SCHEMA ob1 step is the cutover gate: daemons must be paused, table
moved + ob1.thoughts shape rewrite by Worker 3, then daemons resumed.
Worker 2 stops at the announcement of readiness for that move and
waits.
Naming reconciliation (plan vs. reality)
Plan vocabulary wiki / ob1 / trellis will land as the schema
names in the consolidated database, per Travis's locked decisions.
Source DB → target schema mapping:
herbylabDB content →wikischema (owned byvault_mcpper override; washerby_mcpin the plan)openbrainDB content →ob1schema;public.thoughtsbecomesob1.thoughts(preservingthoughtsvocabulary per override; plan saidob1.captures)trellisDB content →trellisschema (owned bytrellis_mcp)
Plus shared public.embeddings per Phase 4.
Open: target database name itself. OB1's database is currently
openbrain. The plan says "if OB1's database is the target and is
named appropriately, use in place. Otherwise, rename or create new."
openbrain becomes a misleading name once it also holds wiki +
trellis schemas. Worth a deliberate rename (e.g., homelab,
petalbrain, or similar) before Phase 2 work begins.
Phase 1 readiness assessment
Ready:
- Source DB sizes are trivial; dumps will be small and fast.
- pgvector already present in target DB (
openbrain); no extension install needed. - Embedding model + dim are confirmed and stable (
nomic-embed-text/ 768) — no re-embed forced. - Existing HNSW spec on
public.thoughtsmatches the lockedpublic.embeddingsHNSW spec exactly.
Not ready / needs decision:
NAS backup destinationResolved 2026-05-15: local-only at/opt/backups/postgres-consolidation/. No NAS involved.Target database nameResolved 2026-05-15:petalbrain.openbrainrenamed before Phase 2 destructive ops begin.wikischema content scope —herbylabDB schema came from the unmergedclaude/competent-knuth-de9483branch's migration9c7e3f1a2b4d;main's deployed MCP doesn't open a DB connection at all. Wiki content is filesystem-only under/opt/projects/wiki-vault/. Worker 4's vault-indexing source must walk the filesystem and write intopublic.embeddingsdirectly. See the herbylab-DB section above for the full evidence trail.- Connection-string anomaly in
vault-mcp(vault-postgreshostname). Tied to Travis's rename rollout, blocks Phase 3 cutover for that MCP only. Phase 1/2 unaffected.
Conservative call: gate before Phase 1 until the NAS destination and target DB name decisions are made.
Addendum — Phase 2-start fresh backup (Travis directive, 2026-05-15)
In addition to Phase 1 bulk backups, Phase 2 must begin with a
fresh pg_dump of each source database immediately before the first
destructive operation (no CREATE SCHEMA, no ALTER TABLE, no
restore — nothing destructive precedes this step).
Rationale: ob1-watcher is writing to public.thoughts continuously
(latest write ~5 min before this note). Time elapses between Phase 1
dumps and Phase 2 cutover, especially with gating. Any rows captured
in that window would be lost on rollback if Phase 1 dumps are the
only restore point.
Specifics:
- Run fresh
pg_dumponopenbrainfirst — it's the live writer and the most critical. Thenherbylabandtrellisfor symmetry. - Destination:
/opt/backups/postgres-consolidation/(same as Phase 1), gzipped custom format. - Filename suffix:
-phase2-start-<UTC-timestamp>to distinguish from Phase 1 backups. - Verify each fresh dump is readable via test restore to a scratch database (same pattern as Phase 1).
- Only after the three fresh dumps are verified does Phase 2's first destructive operation run.
This is not a replacement for Phase 1 dumps — Phase 1 remains the
broad safety net (full instance pg_dumpall + per-DB granular
dumps). Phase 2-start dumps are the just-before-disruption snapshot
that captures any in-flight writer activity.
Operational note: this is also the natural point to pause
ob1-watcher and ob1-enricher. Sequence:
- Pause both daemons (
docker compose stop ob1-watcher ob1-enricher). - Confirm no in-flight writes (
pg_stat_activityquiet onopenbrain). - Take the fresh per-DB dumps + verify.
- Begin Phase 2 destructive work.
- (Worker 3 resumes daemons after
ob1.thoughtscutover lands.)