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
{{ message }}
Repository navigation
[Decision] os start in production mode runs on a local SQLite file and self-generated secrets, with no warning #30120
From a real-user walkthrough of the development flow, filed on the maintainer's word: 「第一组:会卡住人或误导人,立卡」 (2026-11-04), under 「等我决定的都要立卡,并且从业务的角度给我具体解释和我沟通」. Measured on main987ff33b88.
The question
When someone runs os start with NODE_ENV=production and no database URL or secrets, should it carry on quietly, warn, or refuse?
Governing text:
packages/cli/src/commands/start.ts header: "zero-config quick boot", and the auth secret comment: "Quick-start should 'just work'".
docker/Dockerfile:119: the runtime image's command is os start, so the same command is both the quick start and the production entry.
packages/cli/src/commands/serve.ts:4845: serve tells the operator to set OS_AUTH_SECRET.
What a customer sees
They deploy, the server says ready, and everything works. Their data is in a SQLite file under the working directory and their secrets were generated on that disk. If the container is replaced without a volume, the data, the sign-in secret and the key for saved credentials are all gone. Nothing warned them on the way in.
Measured
NODE_ENV=production, OS_DATABASE_URL, OS_AUTH_SECRET and OS_SECRET_KEY unset, os start in a project:
It boots and prints Mode: production, with the database at a file: path under the home directory.
It writes auth-secret and dev-crypto-key into the home directory.
The only warning about durability is the storage one: StorageServicePlugin using local driver…. There is none about the database or the secrets.
With no OS_HOME, it uses the same .objectstack/data as os dev: the published 18.3.0 opened the developer's own database, with no message.
The generated docker-compose.yml does inject the three values, so that path is fine. A plain docker run was not measured, because Docker is not running on this machine.
Options
A. Warn loudly. Keep booting, and print one clear block naming what is local and what is lost when the disk goes, with the three variables to set. Local trials stay zero-config.
B. Refuse in production mode. With NODE_ENV=production and no durable database or secrets, exit and name them. The quick start stays available without NODE_ENV=production.
The maintainer challenged option A: 「打印醒目的警告,打印在哪里?管理员也不知道啊」 (2026-11-04). That is right, so the seat withdraws A as written.
Why A falls short
A boot message reaches only whoever reads the terminal or the container log. The administrator who owns the data signs in to the Console, and may not be the person who deployed. Read on main987ff33b88:
The Setup System Overview dashboard (packages/platform-objects/src/apps/dashboards/system_overview.dashboard.ts) shows users, organizations, sessions, packages and audit counts, and nothing about the deployment.
A search of ui/packages/app-shell/src for a banner component by the usual names found none. The Console's only first-run prompt seen is the time zone dialog.
The 2026-08-25 ruling for a walled owner who cannot verify, "warn loudly, by name, at boot; NEVER refuse", is also log-only, so it has the same blind spot.
Revised option
A2. Tell both people, where each of them looks.
Boot log for the deployer, as in A.
For administrators only, a standing notice on the Console home page and at the top of Setup's System Overview: what is at risk, in business words, with a copy button for the three variables to send to whoever deployed it.
It does not interrupt with a dialog, and it stays until the condition is gone.
It shows in production mode only. Members never see it. The state is served to administrators only and not on the public config.
Options B (refuse) and C (leave) stand as written.
Limits
The server cannot tell whether its disk survives a restart, so the notice has to say "if this disk is not kept". A deliberate single-machine install will see it until the variables are set; whether an administrator may acknowledge and hide it is part of the decision.
The maintainer asked: 「是否建议生产环境拒绝启动?」 (2026-11-04). The seat's answer: not across the board, yes in the official container image. Read on main987ff33b88; the image itself was not run, because Docker is not running on this machine.
Why not refuse everywhere
packages/cli/src/commands/start.ts:481: os start sets NODE_ENV=production when it is unset. So the zero-config trial is itself "production mode", and a refusal keyed on the mode would end npm run start on a laptop.
content/docs/deployment/self-hosting.mdx:253 documents SQLite on a single small host with a volume as a supported choice. A local file is not wrong in itself.
Why refuse in the image
docker/Dockerfile sets NODE_ENV=production and runs os start, and docker/README.md:95 says "You must inject at runtime: OS_DATABASE_URL, OS_AUTH_SECRET, OS_SECRET_KEY". That is declared and, from the code path, not enforced: start.ts generates the secrets itself. Under the enforce-or-remove rule, the declaration should hold.
A container's disk is what gets replaced, so a self-generated secret there is the case with the worst outcome.
A refusal at boot costs least: nothing has been written, and the operator is watching the deploy.
packages/cli/src/commands/serve.ts:4845 already refuses a production boot with no auth secret. The refusal text and the idea exist; only the start wrapper steps around them.
Revised option
A3 = A2 plus a narrow B.
The official image refuses to start unless all three variables are set explicitly, naming the missing ones. An explicit OS_DATABASE_URL=file:… on a volume passes, so SQLite stays a choice.
Everywhere else os start keeps booting, with the boot log and the administrator notice of A2.
How the image signals this, for example an image ENV that start reads, is for the dev.
Cost
A deployment already running the image with self-generated secrets on a volume would stop at upgrade. It has to export those values first, from the home directory's auth-secret and dev-crypto-key, and the refusal text has to say so. Whether any such deployment exists was not measured. Per the startup posture, the seat does not propose a staged window unless a named user needs one.
Recommendation: A3
Still awaiting the maintainer's decision, including whether an administrator may acknowledge and hide the notice.
Ruling recorded: option A3. The maintainer, verbatim, in the direct session on 2026-11-04: 「同意」, answering the seat's recommendation on this card.
Official container image: refuses to start unless OS_DATABASE_URL, OS_AUTH_SECRET and OS_SECRET_KEY are all set explicitly, and names the missing ones. An explicit OS_DATABASE_URL=file:… passes. The refusal text says how a deployment running on self-generated secrets exports them first.
Every other os start: keeps booting, with a boot log block for the deployer.
Administrators, production mode only: a standing notice on the Console home page and at the top of Setup's System Overview, with a copy button for the variables. No dialog. Members never see it, and the state is not on the public config. Local file storage and no email service are lower-severity lines; the email line may share its signal with Forgot password says the reset link was sent on a deployment with no email service #30107.
Sub-question, read as the seat's stated recommendation: an administrator may acknowledge and hide the notice. The hide is written to the audit log, and System Overview keeps one small line. The maintainer's word did not separately name this point, so if they say otherwise, their word replaces this line.
The dev verifies the image path in a real container, because the seat could not: Docker was not running on the seat's machine, and the claim that the image boots without the variables comes from reading the code. If that premise is false, report a fork.
Left for triage to route. No lane or claim is set here.
Read fresh. os start with NODE_ENV=production runs on a local SQLite file with self-generated secrets, silently. Ruled A3 (15:11Z, 「同意」), as recorded on this card. p2.
Fix site: packages/cli (start.ts) and the official image (docker/Dockerfile).
Claim: PM loop round 1 · 2026-11-05T21:40Z
Session: session_016WV4cytQjG9X1CrCgytUM5
Account: os-elon-musk (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-30120-start-prod-durability
Worktree: objectstack-issue-30120
Domain: domain:server
Seat: domain:server#3
File surface: packages/cli/src/commands/start.ts (+ its tests), docker/Dockerfile (the image signal start reads), the admin-only served notice state in its owning server package (named by the dev at the first push; never /api/v1/auth/config or any public config), the Console home page notice and Setup System Overview top notice under ui/** (packages/platform-objects/src/apps/dashboards/system_overview.dashboard.ts if the notice rides the dashboard), the acknowledge/hide write with its audit row, i18n keys the notice needs, content/docs/deployment/self-hosting.mdx and docker/README.md lines that state the rule, root .changeset/ files (stop on breach; explain in the report)
Container & model: L, mode:thread, model: default (dispatch-gates --tier: no path-derived mandate; floor sonnet · default opus · ceiling fable), effort: xhigh
Clause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6635741264
Premise: holds per the ruling 6634240777 and triage 6635741264; the ruling's own premise (the image boots without the three variables) is read from code only, so the dev verifies it in a real container first and reports a fork if it is false
Serial constraints cleared: none: no open PR touches packages/cli/src/commands/start.ts, packages/cli/src/commands/serve.ts, docker/** or system_overview.dashboard.ts (19 open non-mailbox PRs read at claim time); no domain:server in-flight claim names those files
Claim: PM loop round 1 · 2026-11-05T21:41Z
Session: session_016WV4cyvF7SP65frVFAYLv7
Account: os-elon-musk (the seat's linked user as GET /user answers it; the card's assignee)
Branch: the card thread's designated claude/project-thread-* branch, named in a claim amendment once the thread reports it
Worktree: objectstack-issue-30120
Domain: domain:server
Seat: domain:server#5
File surface: packages/cli/src/commands/start.ts (boot log block, the image refusal), docker/Dockerfile (the signal start reads), docker/README.md (only the sentence the refusal makes true), the admin-only deployment-durability state where measuring puts it (named in the dev's first report, expected packages/runtime or a service beside the settings service), packages/platform-objects/src/apps/dashboards/system_overview.dashboard.ts, the Console home notice in ui/packages/app-shell/src/**, the audit write for the hide, their tests and locale strings, one changeset per PR. ⛔ packages/spec/** (stop on breach; explain in the report)
Container & model: L, mode:thread, model: default (dispatch-gates --tier: no path-derived mandate; floor sonnet · default opus · ceiling fable), effort: xhigh
Clause-②: no
Metadata-protocol: none
Responsibility: os start in packages/cli (self-generates secrets and a local SQLite file under NODE_ENV=production, silently) and the official image (docker/README.md:95 declares the three variables required, nothing enforces it) | none (serve.ts refuses a production boot with no auth secret, but start steps around it) | anyone deploying the image or running os start in production with no durable database or secrets; measured on the card on main987ff33b88
Thread-read: 6635741264
Premise: holds per the card's measurement; the dev verifies the image path in a real container first, as the ruling requires, and reports a fork if the image does not boot without the variables
Serial constraints cleared: none on start.ts or docker/Dockerfile (no open PR touches them, read over the 19 open PRs' file lists at claim time). #30206 (domain:ui, in flight: first-run admin checklist) may touch the Console home: the dev reads its claim and PR diff before editing ui/packages/app-shell and serialises behind it if they meet. #30107 (email delivery state) is closed: the dev reuses its signal for the email line
Ruling (6634240777, the maintainer verbatim 「同意」 to A3): the official image refuses to start unless OS_DATABASE_URL, OS_AUTH_SECRET, OS_SECRET_KEY are all set explicitly (explicit file: passes; the refusal says how to export self-generated secrets); every other os start boots with a boot log block; administrators in production mode see a standing notice on the Console home and at the top of Setup's System Overview, with a copy button, no dialog, members never see it, not on the public config; an administrator may acknowledge and hide it, the hide is audited, System Overview keeps one small line. The work may split into PRs (server + image first, Console notice after); each split PR carries Refs #30120, the last one Fixes.
Seat 3's claim 6670269811 (session_016WV4cytQjG9X1CrCgytUM5) predates this seat's claim 6670281930, so seat 3 holds #30120 and the claim above is withdrawn. Labels and assignee stay as seat 3 set them.
Handed over from this seat's reading, for seat 3 to use or drop: no open PR touched packages/cli/src/commands/start.ts or docker/Dockerfile at claim time (19 open PRs' file lists); #30206 (domain:ui, in flight, first-run admin checklist) may touch the Console home, so read its claim and diff before editing ui/packages/app-shell; #30107 (the email delivery state the ruling lets the email line share) is closed.
Claim amendment to 6670269811 · 2026-11-05T21:45Z
Branch: claude/project-thread-dj7jk6 (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
Session: session_016WV4cytQjG9X1CrCgytUM5 (seat) · dev card thread session_016WV4cyhB3ePno23ywn1PQd
Every other line of 6670269811 stands.
Claim amendment to 6670269811 · 2026-11-05T22:02Z
File surface adds, from the dev's first measurement (premise holds: the unchanged image boots with none of the three variables, SQLite in the container layer, both secrets minted): packages/services/service-settings/src/** (the admin-only notice state: read + hide routes through the settings family door, capability manage_platform_settings, route-ledger rows, the hide's sys_audit_log row), ui/packages/app-shell/src/views/DashboardView.tsx (System Overview top notice), ui/packages/i18n/src/locales/*.ts. Image signal: a new docker/DockerfileENV that start reads.
Clause-②: no stands, on the condition that the boot-facts channel adds no new export to a package entry (the seat's instruction to the dev 2026-11-05); if an export proves unavoidable the line becomes yes and the seat re-routes.
Every other line stands.
Claim amendment to 6670269811 · 2026-11-05T22:38Z
File surface adds, in a SEPARATE PR only: .github/workflows/scaffold-e2e.yml, one line in the step "Docker build and run (scaffolded Dockerfile)": -e OS_DATABASE_URL="file:/tmp/e2e.db". Reason: the step runs the image built from this checkout with only OS_SECRET_KEY and OS_AUTH_SECRET, so the ruled image refusal would turn it red. .github/workflows/** is governed (Tier H), and a mixed diff is not judged by proportion, so the line rides its own PR (Refs #30120, never Fixes), which lands before or with the main PR under governed approval. The main PR stays off governed paths.
Every other line stands.
Claim: PM loop round 1 · 2026-11-05T22:46Z
Session: session_016WV4cytQjG9X1CrCgytUM5 (seat) · dev card thread session_016WV4cyhB3ePno23ywn1PQd
Account: os-elon-musk (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/project-thread-dj7jk6 (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-30120
Supersedes: claim 6670269811 and its amendments 6670368475, 6670707340, 6671390478 (re-posted whole so the closing-target guard reads the live branch)
Domain: domain:server
Seat: domain:server#3
File surface: packages/cli/src/commands/start.ts (+ its tests), docker/Dockerfile (the image signal start reads), the admin-only served notice state in its owning server package (named by the dev at the first push; never /api/v1/auth/config or any public config), the Console home page notice and Setup System Overview top notice under ui/** (packages/platform-objects/src/apps/dashboards/system_overview.dashboard.ts if the notice rides the dashboard), the acknowledge/hide write with its audit row, i18n keys the notice needs, content/docs/deployment/self-hosting.mdx and docker/README.md lines that state the rule, root .changeset/ files; packages/services/service-settings/src/** (admin-only notice read + hide routes, route-ledger rows, the hide's audit row), ui/packages/app-shell/src/views/DashboardView.tsx, ui/packages/i18n/src/locales/*.ts; and, in a SEPARATE governed PR only (Refs #30120), one line of .github/workflows/scaffold-e2e.yml (stop on breach; explain in the report)
Container & model: L, mode:thread, model: default (dispatch-gates --tier: no path-derived mandate; floor sonnet · default opus · ceiling fable), effort: xhigh
Clause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6671390478
Premise: holds per the ruling 6634240777 and triage 6635741264; the ruling's own premise (the image boots without the three variables) is read from code only, so the dev verifies it in a real container first and reports a fork if it is false
Serial constraints cleared: none: no open PR touches packages/cli/src/commands/start.ts, packages/cli/src/commands/serve.ts, docker/** or system_overview.dashboard.ts (19 open non-mailbox PRs read at claim time); no domain:server in-flight claim names those files
Claim amendment to 6671522963 · 2026-11-05T23:22Z
File surface adds: scripts/check-cli-test-child-env.mjs (not governed), its census only: the count word and one sorted path entry with the "[#N] Joined the population deliberately" comment, as every earlier joiner did in its own PR. The new CLI integration pin spawns bin/run.js and names NODE_ENV: undefined at each call. No other line of the gate moves.
Every other line stands.
{
"issue": 30120,
"status": "done",
"branch": "claude/project-thread-dj7jk6",
"pr": "https://github.com/objectstack-ai/objectstack/pull/30720",
"session": "session_016WV4cyhB3ePno23ywn1PQd",
"premise_still_valid": true,
"summary": "Ruling A3 implemented in draft PR #30720. The official image (docker/Dockerfile ENV OS_DEPLOYMENT_DURABILITY_CHECK_ENABLED=true) makes `os start` exit 1 unless OS_DATABASE_URL, OS_AUTH_SECRET and OS_SECRET_KEY are all set explicitly; it names the missing ones before it writes a file, says how to carry over the values the image generated (auth-secret, dev-crypto-key, data/objectstack.db under /home/node/.objectstack), and an explicit `file:` URL passes. Every other production `os start` keeps booting and prints a boot block naming each file it keeps on its own disk and the variable that moves it; the same facts reach service-settings, which serves them only to `manage_platform_settings` holders (GET /api/settings/deployment/notice; POST .../hide writes a config_change row on sys_audit_log). The Console home and the top of Setup's System Overview show the notice to administrators in production only, with Copy the settings and Hide (no dialog; local storage and email as low-severity lines; System Overview keeps one small line after a hide). Verified in a real container. One check stays red by design: Build & E2E UI's eager-closure verdict (i18n-locale-en 60,540 / 60,000 gz, +633 from the notice copy, ceiling not raised); the PR stays draft until #30640 lands, then main is merged in and re-measured, per the seat.",
"tests": "@objectstack/cli: start-deployment-durability.test.ts (unit) and test/start-deployment-durability.integration.test.ts on the built CLI, 8/8 (refusal names each missing variable; file: URL passes; switch value not true/false/1/0 refused; non-image production start boots with the block; findings channel). @objectstack/service-settings: deployment-notice.routes.test.ts plus the route ledger, auth-gate, anonymous-floor and authz-outage pins, green (member 403; absent from /api/v1/auth/config; hide writes the audit row; 409 with no audit log or a moved-on fingerprint; empty outside production). @object-ui/app-shell: DeploymentNotice.test.tsx plus DashboardView (9 files, 49 tests) and console/home (38 files, 227 tests) green. Ablations A1-A9 each turned their pin red and were restored (/mnt/project-files/server-lane-seat3/30120/ablations.md). Real container: image built from this branch's Dockerfile with the branch CLI deployed in it; refusal with none set, with each one missing, pass with explicit file: URL, carry-over of the generated values boots without regenerating keys (container-verify.md). Measure-first on origin/main recorded in measure-main.md.",
"gates": "dispatch-gates at HEAD ea323f088 (45 paths vs merge base 47c457b23): `Run reconciliation — 120 derived, 120 run, 0 NOT-MEASURED, 0 UNRUN`; every recorded code exit 0, plus `pnpm lint` exit 0. Reruns, each environmental, reported as such: check-plugin-teardown-shape --self-test exit 3 on the shallow clone, exit 0 after fetching its base commit; skill-examples, dual-build-cjs-loads and i18n-coverage exit 3 before the dists existed, exit 0 after; check:dts-closure needed `build:plugin` in ui/apps/console; CLI typecheck needed --max-old-space-size=8192. The tool also printed STALE TREE (31 commits behind origin/main); the gates are rerun after the main merge. CI on ea323f088: Test UI (1/16 … 16/16) all success and the Test UI aggregate success (the 12 Home files the guard caught on 53e09f556 are green); Type Check UI, Lint UI, Live E2E UI, Build Core, Build Docs, Test Core 2,3,5,6,7,8/8, every Lint · and Type Check · lane, TypeScript Type Check, Lint & Repo Gates, Check Changeset, Check PR Size, PR-body guards, Governed Surface Queue Guard, Spec property liveness, Scaffold with repo dist, Dogfood Regression Gate: success. Build & E2E UI: failure, only check:eager-closure — `i18n-locale-en 59.1 KB / 58.6 KB ceiling (OVER by 0.5 KB)`, the known +633 B of notice copy; waits on #30640, ceiling not raised. Test Core 1/8 and 4/8 still running when this was written; I report them with the green-CI note.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"line_budget": "45 files, +2,813/−35 vs merge base: non-test source 11 files +1,149/−5; locale packs +246/−0 (the 18-key deploymentNotice block in each of the 10 packs); tests +1,340/−24; docs + changesets +78/−6",
"deviations": [
"scripts/check-cli-test-child-env.mjs census entry for the new built-CLI integration test: outside the file surface, seat-approved (claim amended 6672140144, census only).",
"The scaffold-e2e workflow line is not in this PR: it is governed PR #30714 (needs os-zhuang or hotlong), named under 需要你做的 and landing first.",
"12 HomePage tests stub the notice module (vi.mock), because the admin-gated fetch reached a real socket under the network-escape guard; the notice's own fetch, gate and copy stay pinned in DeploymentNotice.test.tsx.",
"Build & E2E UI eager-closure stays red until #30640 lands (ceiling not raised, seat ruling); PR kept draft."
],
"clause_2": "no — 0 lines changed in any package entry file; the CLI exports map exposes only StartCommand, and the findings ride the internal OS_INTERNAL_DEPLOYMENT_FINDINGS env channel into service-settings with no new service-settings export; no new error code, no published schema touched.",
"files_changed": [
".changeset/30120-console-deployment-notice.md",
".changeset/30120-image-refuses-undurable-start.md",
"content/docs/deployment/self-hosting.mdx",
"docker/Dockerfile",
"docker/README.md",
"packages/cli/src/commands/start.ts",
"packages/cli/src/commands/start-deployment-durability.test.ts",
"packages/cli/test/start-deployment-durability.integration.test.ts",
"packages/services/service-settings/src/config-change-audit.ts",
"packages/services/service-settings/src/deployment-notice.ts",
"packages/services/service-settings/src/deployment-notice.routes.test.ts",
"packages/services/service-settings/src/settings-route-ledger.ts",
"packages/services/service-settings/src/settings-routes.ts",
"packages/services/service-settings/src/settings-service-plugin.ts",
"packages/services/service-settings/src/settings-routes.anonymous-floor.pin.test.ts",
"packages/services/service-settings/src/settings-routes.auth-gate.pin.test.ts",
"packages/services/service-settings/src/settings-routes.authz-outage-relay.test.ts",
"scripts/check-cli-test-child-env.mjs",
"ui/packages/app-shell/src/console/deployment-notice/DeploymentNotice.tsx",
"ui/packages/app-shell/src/console/deployment-notice/deploymentNotice.ts",
"ui/packages/app-shell/src/console/deployment-notice/__tests__/DeploymentNotice.test.tsx",
"ui/packages/app-shell/src/console/home/HomePage.tsx",
"ui/packages/app-shell/src/console/home/__tests__/HomePage.*.test.tsx (12 files, notice stub only)",
"ui/packages/app-shell/src/views/DashboardView.tsx",
"ui/packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts"
],
"mcp_calls": "23 — all reads: get_job_logs ×13, issue_read:get ×3, issue_read:get_comments ×2, pull_request_read:get ×2, pull_request_read:get_check_runs ×2, pull_request_read:get_files ×1; no write tool",
"api_writes": "2 — relay only: pr_create (#30720) and this issue comment; 0 REST proxy writes outside the relay",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"class: b · reach: skills/objectstack-platform/references/operations.md (Docker/Kubernetes row, Common ops pitfalls) does not say the official image now refuses to start without the three variables · evidence: grep of skills/** for os start production guidance · dedupe words: image refuses start OS_DATABASE_URL skills operations",
"class: b · reach: content/docs environment-variables reference does not list OS_DEPLOYMENT_DURABILITY_CHECK_ENABLED · evidence: grep content/docs/** · dedupe words: environment variables durability switch",
"carrier: PR #30720 body (Skills-docs line) · noted, not filed"
]
}
Landed · 2026-11-06T07:07Z · PR #30720 merged through the queue as a77c2e9260. ACCEPT on head d822ff17; the governed half PR #30714 landed first.
Reading
Value
PR state
merged, closed this card via Fixes #30120
Ruling
A3 (6634240777)
Contract review
not owed: Clause-②: no re-derived at the final head (server-only route-ledger rows, no SDK or spec surface)
Follow-up
#30857 (skills operations.md: the image's refusal and the carry-over, Tier H)
pm:dispatched removed; the card is closed. The official image now refuses to start without OS_DATABASE_URL, OS_AUTH_SECRET and OS_SECRET_KEY; every other production os start says what it keeps on its own disk, and administrators see it in the Console.
Activity
objectstack-fleet commentedon Nov 4, 2026
epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff
The maintainer challenged option A: 「打印醒目的警告,打印在哪里?管理员也不知道啊」 (2026-11-04). That is right, so the seat withdraws A as written.
Why A falls short
A boot message reaches only whoever reads the terminal or the container log. The administrator who owns the data signs in to the Console, and may not be the person who deployed. Read on
main987ff33b88:System Overviewdashboard (packages/platform-objects/src/apps/dashboards/system_overview.dashboard.ts) shows users, organizations, sessions, packages and audit counts, and nothing about the deployment./api/v1/auth/confignames no email delivery state (Forgot password says the reset link was sent on a deployment with no email service #30107).ui/packages/app-shell/srcfor a banner component by the usual names found none. The Console's only first-run prompt seen is the time zone dialog.Revised option
Options B (refuse) and C (leave) stand as written.
Limits
The server cannot tell whether its disk survives a restart, so the notice has to say "if this disk is not kept". A deliberate single-machine install will see it until the variables are set; whether an administrator may acknowledge and hide it is part of the decision.
Recommendation: A2
Still awaiting the maintainer's decision.
objectstack-fleet commentedon Nov 4, 2026
epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff
The maintainer asked: 「是否建议生产环境拒绝启动?」 (2026-11-04). The seat's answer: not across the board, yes in the official container image. Read on
main987ff33b88; the image itself was not run, because Docker is not running on this machine.Why not refuse everywhere
packages/cli/src/commands/start.ts:481:os startsetsNODE_ENV=productionwhen it is unset. So the zero-config trial is itself "production mode", and a refusal keyed on the mode would endnpm run starton a laptop.content/docs/deployment/self-hosting.mdx:253documents SQLite on a single small host with a volume as a supported choice. A local file is not wrong in itself.Why refuse in the image
docker/DockerfilesetsNODE_ENV=productionand runsos start, anddocker/README.md:95says "You must inject at runtime:OS_DATABASE_URL,OS_AUTH_SECRET,OS_SECRET_KEY". That is declared and, from the code path, not enforced:start.tsgenerates the secrets itself. Under the enforce-or-remove rule, the declaration should hold.packages/cli/src/commands/serve.ts:4845already refuses a production boot with no auth secret. The refusal text and the idea exist; only thestartwrapper steps around them.Revised option
OS_DATABASE_URL=file:…on a volume passes, so SQLite stays a choice.os startkeeps booting, with the boot log and the administrator notice of A2.How the image signals this, for example an image
ENVthatstartreads, is for the dev.Cost
A deployment already running the image with self-generated secrets on a volume would stop at upgrade. It has to export those values first, from the home directory's
auth-secretanddev-crypto-key, and the refusal text has to say so. Whether any such deployment exists was not measured. Per the startup posture, the seat does not propose a staged window unless a named user needs one.Recommendation: A3
Still awaiting the maintainer's decision, including whether an administrator may acknowledge and hide the notice.
objectstack-fleet commentedon Nov 4, 2026
epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff
Ruling recorded: option A3. The maintainer, verbatim, in the direct session on 2026-11-04: 「同意」, answering the seat's recommendation on this card.
OS_DATABASE_URL,OS_AUTH_SECRETandOS_SECRET_KEYare all set explicitly, and names the missing ones. An explicitOS_DATABASE_URL=file:…passes. The refusal text says how a deployment running on self-generated secrets exports them first.os start: keeps booting, with a boot log block for the deployer.Sub-question, read as the seat's stated recommendation: an administrator may acknowledge and hide the notice. The hide is written to the audit log, and System Overview keeps one small line. The maintainer's word did not separately name this point, so if they say otherwise, their word replaces this line.
The dev verifies the image path in a real container, because the seat could not: Docker was not running on the seat's machine, and the claim that the image boots without the variables comes from reading the code. If that premise is false, report a fork.
Left for triage to route. No lane or claim is set here.
objectstack-fleet commentedon Nov 4, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p2 ·
domain:server·pm:queue· 2026-11-04T16:15Z⛔ Not a claim.
os startwithNODE_ENV=productionruns on a local SQLite file with self-generated secrets, silently. Ruled A3 (15:11Z, 「同意」), as recorded on this card. p2.packages/cli(start.ts) and the official image (docker/Dockerfile).Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5
Claim: PM loop round 1 · 2026-11-05T21:40Z
Session:
session_016WV4cytQjG9X1CrCgytUM5Account:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-30120-start-prod-durabilityWorktree:
objectstack-issue-30120Domain:
domain:serverSeat:
domain:server#3File surface:
packages/cli/src/commands/start.ts(+ its tests),docker/Dockerfile(the image signalstartreads), the admin-only served notice state in its owning server package (named by the dev at the first push; never/api/v1/auth/configor any public config), the Console home page notice and Setup System Overview top notice underui/**(packages/platform-objects/src/apps/dashboards/system_overview.dashboard.tsif the notice rides the dashboard), the acknowledge/hide write with its audit row, i18n keys the notice needs,content/docs/deployment/self-hosting.mdxanddocker/README.mdlines that state the rule, root.changeset/files (stop on breach; explain in the report)Container & model:
L,mode:thread,model: default(dispatch-gates --tier: no path-derived mandate; floor sonnet · default opus · ceiling fable),effort: xhighClause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6635741264
Premise: holds per the ruling 6634240777 and triage 6635741264; the ruling's own premise (the image boots without the three variables) is read from code only, so the dev verifies it in a real container first and reports a fork if it is false
Serial constraints cleared: none: no open PR touches
packages/cli/src/commands/start.ts,packages/cli/src/commands/serve.ts,docker/**orsystem_overview.dashboard.ts(19 open non-mailbox PRs read at claim time); nodomain:serverin-flight claim names those filesobjectstack-fleet commentedon Nov 5, 2026
domain:server#5 · os-elon-musk · session_016WV4cyvF7SP65frVFAYLv7
Claim: PM loop round 1 · 2026-11-05T21:41Z
Session:
session_016WV4cyvF7SP65frVFAYLv7Account:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee)Branch: the card thread's designated
claude/project-thread-*branch, named in a claim amendment once the thread reports itWorktree:
objectstack-issue-30120Domain:
domain:serverSeat:
domain:server#5File surface:
packages/cli/src/commands/start.ts(boot log block, the image refusal),docker/Dockerfile(the signalstartreads),docker/README.md(only the sentence the refusal makes true), the admin-only deployment-durability state where measuring puts it (named in the dev's first report, expectedpackages/runtimeor a service beside the settings service),packages/platform-objects/src/apps/dashboards/system_overview.dashboard.ts, the Console home notice inui/packages/app-shell/src/**, the audit write for the hide, their tests and locale strings, one changeset per PR. ⛔packages/spec/**(stop on breach; explain in the report)Container & model:
L,mode:thread,model: default(dispatch-gates --tier: no path-derived mandate; floor sonnet · default opus · ceiling fable),effort: xhighClause-②: no
Metadata-protocol: none
Responsibility:
os startinpackages/cli(self-generates secrets and a local SQLite file underNODE_ENV=production, silently) and the official image (docker/README.md:95declares the three variables required, nothing enforces it) | none (serve.tsrefuses a production boot with no auth secret, butstartsteps around it) | anyone deploying the image or runningos startin production with no durable database or secrets; measured on the card onmain987ff33b88Thread-read: 6635741264
Premise: holds per the card's measurement; the dev verifies the image path in a real container first, as the ruling requires, and reports a fork if the image does not boot without the variables
Serial constraints cleared: none on
start.tsordocker/Dockerfile(no open PR touches them, read over the 19 open PRs' file lists at claim time). #30206 (domain:ui, in flight: first-run admin checklist) may touch the Console home: the dev reads its claim and PR diff before editingui/packages/app-shelland serialises behind it if they meet. #30107 (email delivery state) is closed: the dev reuses its signal for the email lineRuling (6634240777, the maintainer verbatim 「同意」 to A3): the official image refuses to start unless
OS_DATABASE_URL,OS_AUTH_SECRET,OS_SECRET_KEYare all set explicitly (explicitfile:passes; the refusal says how to export self-generated secrets); every otheros startboots with a boot log block; administrators in production mode see a standing notice on the Console home and at the top of Setup's System Overview, with a copy button, no dialog, members never see it, not on the public config; an administrator may acknowledge and hide it, the hide is audited, System Overview keeps one small line. The work may split into PRs (server + image first, Console notice after); each split PR carriesRefs #30120, the last oneFixes.objectstack-fleet commentedon Nov 5, 2026
domain:server#5 · os-elon-musk · session_016WV4cyvF7SP65frVFAYLv7
already claimed — yielding · 2026-11-05T21:42Z
Seat 3's claim 6670269811 (
session_016WV4cytQjG9X1CrCgytUM5) predates this seat's claim 6670281930, so seat 3 holds #30120 and the claim above is withdrawn. Labels and assignee stay as seat 3 set them.Handed over from this seat's reading, for seat 3 to use or drop: no open PR touched
packages/cli/src/commands/start.tsordocker/Dockerfileat claim time (19 open PRs' file lists); #30206 (domain:ui, in flight, first-run admin checklist) may touch the Console home, so read its claim and diff before editingui/packages/app-shell; #30107 (the email delivery state the ruling lets the email line share) is closed.objectstack-fleet commentedon Nov 5, 2026
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5
Claim amendment to 6670269811 · 2026-11-05T21:45Z
Branch:
claude/project-thread-dj7jk6(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Session:
session_016WV4cytQjG9X1CrCgytUM5(seat) · dev card threadsession_016WV4cyhB3ePno23ywn1PQdEvery other line of 6670269811 stands.
objectstack-fleet commentedon Nov 5, 2026
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5
Claim amendment to 6670269811 · 2026-11-05T22:02Z
File surface adds, from the dev's first measurement (premise holds: the unchanged image boots with none of the three variables, SQLite in the container layer, both secrets minted):
packages/services/service-settings/src/**(the admin-only notice state: read + hide routes through the settings family door, capabilitymanage_platform_settings, route-ledger rows, the hide'ssys_audit_logrow),ui/packages/app-shell/src/views/DashboardView.tsx(System Overview top notice),ui/packages/i18n/src/locales/*.ts. Image signal: a newdocker/DockerfileENVthatstartreads.Clause-②: no stands, on the condition that the boot-facts channel adds no new export to a package entry (the seat's instruction to the dev 2026-11-05); if an export proves unavoidable the line becomes
yesand the seat re-routes.Every other line stands.
wip(#30120): Console deployment notice, locales, pins
objectstack-fleet commentedon Nov 5, 2026
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5
Claim amendment to 6670269811 · 2026-11-05T22:38Z
File surface adds, in a SEPARATE PR only:
.github/workflows/scaffold-e2e.yml, one line in the step "Docker build and run (scaffolded Dockerfile)":-e OS_DATABASE_URL="file:/tmp/e2e.db". Reason: the step runs the image built from this checkout with onlyOS_SECRET_KEYandOS_AUTH_SECRET, so the ruled image refusal would turn it red..github/workflows/**is governed (Tier H), and a mixed diff is not judged by proportion, so the line rides its own PR (Refs #30120, neverFixes), which lands before or with the main PR under governed approval. The main PR stays off governed paths.Every other line stands.
docs(#30120): image refusal, carrying generated values over, the admi…
objectstack-fleet commentedon Nov 5, 2026
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5
Claim: PM loop round 1 · 2026-11-05T22:46Z
Session:
session_016WV4cytQjG9X1CrCgytUM5(seat) · dev card threadsession_016WV4cyhB3ePno23ywn1PQdAccount:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/project-thread-dj7jk6(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-30120Supersedes: claim 6670269811 and its amendments 6670368475, 6670707340, 6671390478 (re-posted whole so the closing-target guard reads the live branch)
Domain:
domain:serverSeat:
domain:server#3File surface:
packages/cli/src/commands/start.ts(+ its tests),docker/Dockerfile(the image signalstartreads), the admin-only served notice state in its owning server package (named by the dev at the first push; never/api/v1/auth/configor any public config), the Console home page notice and Setup System Overview top notice underui/**(packages/platform-objects/src/apps/dashboards/system_overview.dashboard.tsif the notice rides the dashboard), the acknowledge/hide write with its audit row, i18n keys the notice needs,content/docs/deployment/self-hosting.mdxanddocker/README.mdlines that state the rule, root.changeset/files;packages/services/service-settings/src/**(admin-only notice read + hide routes, route-ledger rows, the hide's audit row),ui/packages/app-shell/src/views/DashboardView.tsx,ui/packages/i18n/src/locales/*.ts; and, in a SEPARATE governed PR only (Refs #30120), one line of.github/workflows/scaffold-e2e.yml(stop on breach; explain in the report)Container & model:
L,mode:thread,model: default(dispatch-gates --tier: no path-derived mandate; floor sonnet · default opus · ceiling fable),effort: xhighClause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6671390478
Premise: holds per the ruling 6634240777 and triage 6635741264; the ruling's own premise (the image boots without the three variables) is read from code only, so the dev verifies it in a real container first and reports a fork if it is false
Serial constraints cleared: none: no open PR touches
packages/cli/src/commands/start.ts,packages/cli/src/commands/serve.ts,docker/**orsystem_overview.dashboard.ts(19 open non-mailbox PRs read at claim time); nodomain:serverin-flight claim names those filestest(#30120): non-vacuity control on the config leak check; changesets
objectstack-fleet commentedon Nov 5, 2026
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5
Claim amendment to 6671522963 · 2026-11-05T23:22Z
File surface adds:
scripts/check-cli-test-child-env.mjs(not governed), its census only: the count word and one sorted path entry with the "[#N] Joined the population deliberately" comment, as every earlier joiner did in its own PR. The new CLI integration pin spawnsbin/run.jsand namesNODE_ENV: undefinedat each call. No other line of the gate moves.Every other line stands.
objectstack-fleet commentedon Nov 6, 2026
domain:server#3 · os-elon-musk · session_016WV4cyhB3ePno23ywn1PQd
os-dev-report
{ "issue": 30120, "status": "done", "branch": "claude/project-thread-dj7jk6", "pr": "https://github.com/objectstack-ai/objectstack/pull/30720", "session": "session_016WV4cyhB3ePno23ywn1PQd", "premise_still_valid": true, "summary": "Ruling A3 implemented in draft PR #30720. The official image (docker/Dockerfile ENV OS_DEPLOYMENT_DURABILITY_CHECK_ENABLED=true) makes `os start` exit 1 unless OS_DATABASE_URL, OS_AUTH_SECRET and OS_SECRET_KEY are all set explicitly; it names the missing ones before it writes a file, says how to carry over the values the image generated (auth-secret, dev-crypto-key, data/objectstack.db under /home/node/.objectstack), and an explicit `file:` URL passes. Every other production `os start` keeps booting and prints a boot block naming each file it keeps on its own disk and the variable that moves it; the same facts reach service-settings, which serves them only to `manage_platform_settings` holders (GET /api/settings/deployment/notice; POST .../hide writes a config_change row on sys_audit_log). The Console home and the top of Setup's System Overview show the notice to administrators in production only, with Copy the settings and Hide (no dialog; local storage and email as low-severity lines; System Overview keeps one small line after a hide). Verified in a real container. One check stays red by design: Build & E2E UI's eager-closure verdict (i18n-locale-en 60,540 / 60,000 gz, +633 from the notice copy, ceiling not raised); the PR stays draft until #30640 lands, then main is merged in and re-measured, per the seat.", "tests": "@objectstack/cli: start-deployment-durability.test.ts (unit) and test/start-deployment-durability.integration.test.ts on the built CLI, 8/8 (refusal names each missing variable; file: URL passes; switch value not true/false/1/0 refused; non-image production start boots with the block; findings channel). @objectstack/service-settings: deployment-notice.routes.test.ts plus the route ledger, auth-gate, anonymous-floor and authz-outage pins, green (member 403; absent from /api/v1/auth/config; hide writes the audit row; 409 with no audit log or a moved-on fingerprint; empty outside production). @object-ui/app-shell: DeploymentNotice.test.tsx plus DashboardView (9 files, 49 tests) and console/home (38 files, 227 tests) green. Ablations A1-A9 each turned their pin red and were restored (/mnt/project-files/server-lane-seat3/30120/ablations.md). Real container: image built from this branch's Dockerfile with the branch CLI deployed in it; refusal with none set, with each one missing, pass with explicit file: URL, carry-over of the generated values boots without regenerating keys (container-verify.md). Measure-first on origin/main recorded in measure-main.md.", "gates": "dispatch-gates at HEAD ea323f088 (45 paths vs merge base 47c457b23): `Run reconciliation — 120 derived, 120 run, 0 NOT-MEASURED, 0 UNRUN`; every recorded code exit 0, plus `pnpm lint` exit 0. Reruns, each environmental, reported as such: check-plugin-teardown-shape --self-test exit 3 on the shallow clone, exit 0 after fetching its base commit; skill-examples, dual-build-cjs-loads and i18n-coverage exit 3 before the dists existed, exit 0 after; check:dts-closure needed `build:plugin` in ui/apps/console; CLI typecheck needed --max-old-space-size=8192. The tool also printed STALE TREE (31 commits behind origin/main); the gates are rerun after the main merge. CI on ea323f088: Test UI (1/16 … 16/16) all success and the Test UI aggregate success (the 12 Home files the guard caught on 53e09f556 are green); Type Check UI, Lint UI, Live E2E UI, Build Core, Build Docs, Test Core 2,3,5,6,7,8/8, every Lint · and Type Check · lane, TypeScript Type Check, Lint & Repo Gates, Check Changeset, Check PR Size, PR-body guards, Governed Surface Queue Guard, Spec property liveness, Scaffold with repo dist, Dogfood Regression Gate: success. Build & E2E UI: failure, only check:eager-closure — `i18n-locale-en 59.1 KB / 58.6 KB ceiling (OVER by 0.5 KB)`, the known +633 B of notice copy; waits on #30640, ceiling not raised. Test Core 1/8 and 4/8 still running when this was written; I report them with the green-CI note.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "line_budget": "45 files, +2,813/−35 vs merge base: non-test source 11 files +1,149/−5; locale packs +246/−0 (the 18-key deploymentNotice block in each of the 10 packs); tests +1,340/−24; docs + changesets +78/−6", "deviations": [ "scripts/check-cli-test-child-env.mjs census entry for the new built-CLI integration test: outside the file surface, seat-approved (claim amended 6672140144, census only).", "The scaffold-e2e workflow line is not in this PR: it is governed PR #30714 (needs os-zhuang or hotlong), named under 需要你做的 and landing first.", "12 HomePage tests stub the notice module (vi.mock), because the admin-gated fetch reached a real socket under the network-escape guard; the notice's own fetch, gate and copy stay pinned in DeploymentNotice.test.tsx.", "Build & E2E UI eager-closure stays red until #30640 lands (ceiling not raised, seat ruling); PR kept draft." ], "clause_2": "no — 0 lines changed in any package entry file; the CLI exports map exposes only StartCommand, and the findings ride the internal OS_INTERNAL_DEPLOYMENT_FINDINGS env channel into service-settings with no new service-settings export; no new error code, no published schema touched.", "files_changed": [ ".changeset/30120-console-deployment-notice.md", ".changeset/30120-image-refuses-undurable-start.md", "content/docs/deployment/self-hosting.mdx", "docker/Dockerfile", "docker/README.md", "packages/cli/src/commands/start.ts", "packages/cli/src/commands/start-deployment-durability.test.ts", "packages/cli/test/start-deployment-durability.integration.test.ts", "packages/services/service-settings/src/config-change-audit.ts", "packages/services/service-settings/src/deployment-notice.ts", "packages/services/service-settings/src/deployment-notice.routes.test.ts", "packages/services/service-settings/src/settings-route-ledger.ts", "packages/services/service-settings/src/settings-routes.ts", "packages/services/service-settings/src/settings-service-plugin.ts", "packages/services/service-settings/src/settings-routes.anonymous-floor.pin.test.ts", "packages/services/service-settings/src/settings-routes.auth-gate.pin.test.ts", "packages/services/service-settings/src/settings-routes.authz-outage-relay.test.ts", "scripts/check-cli-test-child-env.mjs", "ui/packages/app-shell/src/console/deployment-notice/DeploymentNotice.tsx", "ui/packages/app-shell/src/console/deployment-notice/deploymentNotice.ts", "ui/packages/app-shell/src/console/deployment-notice/__tests__/DeploymentNotice.test.tsx", "ui/packages/app-shell/src/console/home/HomePage.tsx", "ui/packages/app-shell/src/console/home/__tests__/HomePage.*.test.tsx (12 files, notice stub only)", "ui/packages/app-shell/src/views/DashboardView.tsx", "ui/packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts" ], "mcp_calls": "23 — all reads: get_job_logs ×13, issue_read:get ×3, issue_read:get_comments ×2, pull_request_read:get ×2, pull_request_read:get_check_runs ×2, pull_request_read:get_files ×1; no write tool", "api_writes": "2 — relay only: pr_create (#30720) and this issue comment; 0 REST proxy writes outside the relay", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "class: b · reach: skills/objectstack-platform/references/operations.md (Docker/Kubernetes row, Common ops pitfalls) does not say the official image now refuses to start without the three variables · evidence: grep of skills/** for os start production guidance · dedupe words: image refuses start OS_DATABASE_URL skills operations", "class: b · reach: content/docs environment-variables reference does not list OS_DEPLOYMENT_DURABILITY_CHECK_ENABLED · evidence: grep content/docs/** · dedupe words: environment variables durability switch", "carrier: PR #30720 body (Skills-docs line) · noted, not filed" ] }objectstack-fleet commentedon Nov 6, 2026
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5
Landed · 2026-11-06T07:07Z · PR #30720 merged through the queue as
a77c2e9260. ACCEPT on headd822ff17; the governed half PR #30714 landed first.Fixes #30120Clause-②: nore-derived at the final head (server-only route-ledger rows, no SDK or spec surface)operations.md: the image's refusal and the carry-over, Tier H)pm:dispatchedremoved; the card is closed. The official image now refuses to start withoutOS_DATABASE_URL,OS_AUTH_SECRETandOS_SECRET_KEY; every other productionos startsays what it keeps on its own disk, and administrators see it in the Console.