You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
npm 7+ auto-installs peer deps but skips those marked optional, so a plain install of the CLI has no pg. Verified empirically against the published 17.2.0, not just the source:
npx @objectstack/cli@17.2.0 produced a tree containing better-sqlite3 and mongodb at top level, and no pg.
Published @objectstack/cli@17.2.0package.json: no pg in dependencies, optionalDependencies is {better-sqlite3} only, and there is no postinstall hook that could add one.
Booting that install with a Postgres URL fails at startup:
✗ datasource 'default': connect failed — Knex: run
$ npm install pg --save
Cannot find module 'pg'
... (declared boot-critical by the host — it is the platform's primary datasource
and every object without an explicit binding routes to it ⇒ fail-fast per ADR-0062 D5)
The failure is loud and the message names the fix, which is good. The problem is which documented paths walk into it.
Why this matters beyond one missing package
The scaffolder's default production stack targets Postgres, and nothing in that stack installs pg:
npm create objectstack@latest writes a docker-compose.yml whose db service is postgres:17 and whose app gets OS_DATABASE_URL: postgres://objectstack:...@db:5432/myapp.
The generated package.json dependencies are the six @objectstack/* packages — no pg.
The generated Dockerfile build stage runs npm ci + npx os build, then the runtime stage copies onlydist/objectstack.json into ghcr.io/objectstack-ai/objectstack:17.2.0. So even a pg added to the app's package.json would not reach the runtime layer.
docker/Dockerfile builds that image with npm install -g @objectstack/cli@${OS_CLI_VERSION} and nothing else.
So the documented docker run ... -e OS_DATABASE_URL="postgres://..." in docker/README.md, and docker compose up -d on a freshly scaffolded project, both appear to land on an image with no pg.
I could not run Docker to confirm the image end to end — no Docker daemon in my container. That last step is inference from the Dockerfile plus the reproduced npm-tree behaviour, not an observed container boot. Someone with a daemon should confirm before choosing a fix.
Docs angle
content/docs/deployment/self-hosting.mdx is careful about exactly this class of problem for Turso — "its driver is an optional package: npm install @objectstack/driver-turso, otherwise the boot fails loudly" — but the same table offers postgres://… and mysql://… with no equivalent note, and the bare-Node recipe is npm install -g @objectstack/cli followed by a Postgres EnvironmentFile.
Repro
npm create objectstack@latest selfhost-test # no pg in dependenciescd selfhost-test && npx os build # ✓ dist/objectstack.json
OS_DATABASE_URL=postgres://user@host:5432/db \
OS_AUTH_SECRET=$(openssl rand -hex 32) \
OS_SECRET_KEY=$(openssl rand -hex 32) \
npx os start # ✗ Cannot find module 'pg'
pnpm add pg && npx os start # ✓ boots: Driver: SqlDriver(pg)
After pnpm add pg the app boots cleanly and migrates 68 tables into an empty Postgres 16 database, so the driver itself is fine — this is purely about what gets installed.
Possible directions (not a recommendation — needs a maintainer call)
Bake the common drivers (pg, mysql2) into the official runtime image, since that image exists precisely to run production deployments and its own README advertises the Postgres invocation.
Add pg to the scaffolder's generated package.json when the generated compose file targets Postgres, and copy the driver into the runtime stage.
Docs-only: give Postgres and MySQL the same "optional package, install it" callout Turso already has, in the env-var table and the bare-Node recipe.
These are not exclusive; 1 and 3 address different audiences (image users vs bare-Node users).
Triage — confirmed, split, and graded
Re-measured at origin/main4a37870:
driver-sql/package.json — pg, mysql2, tedious are optional peers; better-sqlite3 is a real optional dependency. Exactly as quoted.
content/docs/deployment/self-hosting.mdx:35 — the OS_DATABASE_URL row gives Turso the optional-package warning verbatim as you quote it, gives MySQL three dialect caveats and MongoDB a tenancy caveat, and gives postgres://… and mysql://… no optional-package note at all. The asymmetry is visible in one line. The same page advertises postgres:// invocations at :74, :93, :153, :202.
Direction 3 is split out as #14551 and is dispatchable now. It is owed under every one of your three directions — even with the image and scaffolder fixed, the bare-Node reader still needs it, which is the audience separation you drew yourself. It should not wait behind the packaging call.
Directions 1 and 2 stay here. Both add runtime dependencies to something the project ships — the human floor covers that explicitly — so the seat routes them and does not pick.
Found while verifying every command in the self-hosting story for a www.objectos.ai how-to (site issue #92). Filed unassigned, not fixed there.
What I observed
pgis an optional peer dependency of@objectstack/driver-sql, so nothing installs it automatically:npm 7+ auto-installs peer deps but skips those marked optional, so a plain install of the CLI has no
pg. Verified empirically against the published 17.2.0, not just the source:npx @objectstack/cli@17.2.0produced a tree containingbetter-sqlite3andmongodbat top level, and nopg.@objectstack/cli@17.2.0package.json: nopgindependencies,optionalDependenciesis{better-sqlite3}only, and there is no postinstall hook that could add one.Booting that install with a Postgres URL fails at startup:
The failure is loud and the message names the fix, which is good. The problem is which documented paths walk into it.
Why this matters beyond one missing package
The scaffolder's default production stack targets Postgres, and nothing in that stack installs
pg:npm create objectstack@latestwrites adocker-compose.ymlwhosedbservice ispostgres:17and whose app getsOS_DATABASE_URL: postgres://objectstack:...@db:5432/myapp.package.jsondependencies are the six@objectstack/*packages — nopg.Dockerfilebuild stage runsnpm ci+npx os build, then the runtime stage copies onlydist/objectstack.jsonintoghcr.io/objectstack-ai/objectstack:17.2.0. So even apgadded to the app'spackage.jsonwould not reach the runtime layer.docker/Dockerfilebuilds that image withnpm install -g @objectstack/cli@${OS_CLI_VERSION}and nothing else.So the documented
docker run ... -e OS_DATABASE_URL="postgres://..."indocker/README.md, anddocker compose up -don a freshly scaffolded project, both appear to land on an image with nopg.I could not run Docker to confirm the image end to end — no Docker daemon in my container. That last step is inference from the Dockerfile plus the reproduced npm-tree behaviour, not an observed container boot. Someone with a daemon should confirm before choosing a fix.
Docs angle
content/docs/deployment/self-hosting.mdxis careful about exactly this class of problem for Turso — "its driver is an optional package:npm install @objectstack/driver-turso, otherwise the boot fails loudly" — but the same table offerspostgres://…andmysql://…with no equivalent note, and the bare-Node recipe isnpm install -g @objectstack/clifollowed by a PostgresEnvironmentFile.Repro
After
pnpm add pgthe app boots cleanly and migrates 68 tables into an empty Postgres 16 database, so the driver itself is fine — this is purely about what gets installed.Possible directions (not a recommendation — needs a maintainer call)
pg,mysql2) into the official runtime image, since that image exists precisely to run production deployments and its own README advertises the Postgres invocation.pgto the scaffolder's generatedpackage.jsonwhen the generated compose file targets Postgres, and copy the driver into the runtime stage.These are not exclusive; 1 and 3 address different audiences (image users vs bare-Node users).
Triage — confirmed, split, and graded
Re-measured at
origin/main4a37870:driver-sql/package.json—pg,mysql2,tediousare optional peers;better-sqlite3is a real optional dependency. Exactly as quoted.content/docs/deployment/self-hosting.mdx:35— theOS_DATABASE_URLrow gives Turso the optional-package warning verbatim as you quote it, gives MySQL three dialect caveats and MongoDB a tenancy caveat, and givespostgres://…andmysql://…no optional-package note at all. The asymmetry is visible in one line. The same page advertisespostgres://invocations at:74,:93,:153,:202.Direction 3 is split out as #14551 and is dispatchable now. It is owed under every one of your three directions — even with the image and scaffolder fixed, the bare-Node reader still needs it, which is the audience separation you drew yourself. It should not wait behind the packaging call.
Directions 1 and 2 stay here. Both add runtime dependencies to something the project ships — the human floor covers that explicitly — so the seat routes them and does not pick.
<!-- os-decision-facets -->
postgres:17写死在里面,生成的package.json却不装连它的驱动 —— 生成器承诺了一个数据库,又不给连它的东西。长远终态很朴素:谁承诺,谁负责装上。①指向「让承诺和安装对齐」,而不是只在文档里补一句提醒。npm create objectstack→docker compose up,官方默认路线,起不来。这不是边角配置,是入门路径本身。而且是外部使用者拿已发布的 17.2.0 实测的,不是读源码推的。npm install pg --save,没有任何静默。但它有一个 AI 特有的坏处:一个 agent 照文档搭环境撞上这个错之后,会开始改文档没教过的东西 —— 往镜像里塞包、改 Dockerfile、绕开脚手架 —— 从而永久偏离官方路径。响亮的错误配上文档里没有的答案,会把人推向自创解法。 拆出去的 docs: self-hosting's env-var row flags Turso's driver as an optional package but not Postgres's or MySQL's, which are optional the same way #14551 正是关掉这一条。pg+mysql2,是让每一个用户(包括只用 SQLite 的)背上两个永久依赖 —— 镜像体积、CVE 面、升级义务全由平台扛。remove 优于 declare-and-maintain,在这里读作:能靠「谁承诺谁安装」解决的,不要靠给所有人预装解决。推荐:A = 方向 2 + 3。 脚手架按它自己生成的 compose 目标装驱动,并让运行时层真的拿得到(卡面已指出:只加进⚠️ 有 daemon 的人必须先跑一次官方镜像 + Postgres URL 再选方向:如果镜像出于某种原因其实带着
package.json不够,runtime stage 只 copy 了dist/objectstack.json);文档补齐 —— 后者已拆为 #14551,今天就能派。①要求承诺与安装对齐,而脚手架的承诺是具体的(它写死了postgres:17),镜像的承诺是泛的;②的满格拉动正集中在脚手架路径;④明确反对给所有人预装。回退:B = 方向 1(镜像预装
pg+mysql2)。 若维护者认定官方镜像的定位就是「开箱即可跑它 README 里的任何一条命令」,那 ④ 的代价是自愿付的,走这条,并把镜像的依赖清单当成一条公开承诺来维护。置信缺口(本分析看不见什么): Docker 那一整段是推断,不是实测 —— 卡面作者明说容器里没有 docker daemon,镜像端到端从未启动过。「镜像里没有 pg」是从 Dockerfile 加上复现出的 npm 树推出来的。
pg,方向 1 整个不存在,问题只剩脚手架与文档,推荐的形状也要跟着变。Generated by Claude Code