Repository navigation
docs(self-hosting): a directory-seeded, credential-less deployment has no documented recovery path — the published instruction points at the door invite_only shuts #14495
Description
Activity
- addeddocumentationImprovements or additions to documentationImprovements or additions to documentation
on Sep 2, 2026 分诊路由(本评论来自分诊座位)· R+150
domain:devx· 保留documentation+pm:queue·priority:p2。落点实测:交付物落
content/docs/deployment/self-hosting.mdx;车道表把content-docs归 devx。⭐ 卡面给的复检串本席按原样跑过一遍,阳性:git grep -n "register with exactly that address" origin/main -- content/docs/deployment/self-hosting.mdx → 439:Then have each of those people register with exactly that address and **verify⚠️ 行号已从卡面写的 428 漂到 439(fetch 后的origin/main实测)—— 缺陷本身仍然成立,漂的只是行号。p2 判据:公开文档把一条被
invite_only默认关掉的门当作恢复路径写给自建部署的运维;但它是文档半边,运行期诊断另有 #14353,且 #14349 已裁 A(门不动)⇒ 不是 p1。⭐ 给接手方的提醒(卡面验收第 3 条,别绕过):若实测找不到任何可用的开局路径,正确交付是
premise_still_valid: false+pr: null并上报决策箱,⛔ 不是用文档把它糊过去。
Generated by Claude Code
Claim: PM seat
domain:devx @ objectstack(#6023), sessionsession_012zGPuVVX3deAx9LdjK8jCk, dispatching to anos-devsubagent on branchclaude/issue-14495-self-hosting-bootstrap-recovery. P2 docs, M: acceptance as the card writes it — (1) MEASURE the working recovery path first on a real kernel (N humansys_userrows, zerosys_accountrows,invite_onlydefault, outside development) and name the operator action that produces a first login; (2) rewritecontent/docs/deployment/self-hosting.mdx(the "register with exactly that address" instruction is at:439on current main) to state that seeding a directory without credentials opens no bootstrap window, name the measured out-of-band step, and point at #14353's boot diagnostic (that card is CLOSED — the diagnostic landed); theNODE_ENV=developmentseed described as development-only; (3) ⛔ stop rule: if no working path exists,premise_still_valid: false+pr: nulland the finding goes to the decision inbox — never a doc that papers over it. Routing guard: the page carries no generated banner (hand-written). Hot-file: the only open PR touching the page is the bot release PR #15334 (+4/−4 image-tag version rows, regenerated by changesets on every main move — the dev does not edit those lines). Assignee field was empty at the read immediately before this write (the assignee/label write landed 21:26Z; this note is the claim record).
Generated by Claude Code
os-dev-report
{ "issue": 14495, "status": "done", "branch": "claude/issue-14495-self-hosting-bootstrap-recovery", "pr": "https://github.com/objectstack-ai/objectstack/pull/15586", "premise_still_valid": true, "summary": "Measured first, on a real kernel (ObjectQL over driver-sql + better-sqlite3, plugin-auth's own authIdentityObjects, driven through AuthManager.handleRequest): 3 human sys_user rows, 0 sys_account rows, NODE_ENV=test (dev-admin seed disarmed), default invite_only. The published instruction is refused 403 SELF_REGISTRATION_CLOSED for a seeded person's own address AND for any other; sign-in is 401; the #14353 boot diagnostic fires with no_sign_in_account_at_boot. A working out-of-band path EXISTS and is measured: write one pending sys_invitation row (email the directory does not already hold, status pending, future expires_at, inviter_id of any existing sys_user), then that person registers at the ordinary endpoint - sign-up 200, sign-in 200, no mail transport, invite_only never widened; on the single posture bootstrapPlatformAdmin then promotes that account holder (adminPromoted true, grant on the invited account, never on a credential-less directory row). Two remedies the boot diagnostic names were measured as non-remedies and are documented as such: widening the posture answers 200 while persisting nothing for an address the directory already holds and forces EMAIL_NOT_VERIFIED on every other, and a hand-written credential row authenticates nothing while silencing the diagnostic. The page now says what 'fresh' means on both first-boot paths, carries a new subsection 'A seeded directory opens no bootstrap window' with the measured table, the diagnostic's grep token and the recovery, and describes the dev-admin seed as NODE_ENV=development-gated. Docs-only: no image-tag rows, nothing under content/docs/releases/; skip-changeset applied and read back.", "tests": "Measurement: bash scripts/pm/os-verify-lock.sh -c 'pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 src/tmp-14495-recovery-measurement.test.ts' -> 'Tests 6 passed (6)' and 'os-verify-lock: VERDICT command-exit 0 - held the lock 16s' (harness deleted before commit; transcript quoted in the PR body). Gate family DERIVED, not guessed: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, re-derived after merging origin/main so the answer is not from a stale tree - change set '1 path(s) vs merge base 3d3f60e01', exactly content/docs/deployment/self-hosting.mdx. All 38 derived commands run with each exit code captured BEFORE any pipe: 38/38 exit 0. Verdict lines the gates printed: 'check-doc-anchors: 307 internal #fragment link(s) across 410 source file(s) all resolve to a real heading'; 'check-docs-single-h1: 405 page(s) under content/docs/ carry no body-level # heading'; 'content/docs (docsSchema, floor 1): 405 page(s) parse with yaml@2.9.0'; 'doc authoring guard: 46 published skill files clean'; 'release-owned pages are in scope and read-only: 9 page(s) under content/docs/releases/ review-only'; 'check-docs-image-tag: OK (3/3 enumerated surface(s) read, 9 concrete pin(s) compared against packages/cli/package.json 17.3.0)'; 'check-nul-bytes: OK (scanned 7555 text file(s); no raw ASCII control bytes)'; 'check:docs -> 230 generated files in sync with packages/spec'; 'check:skill-examples -> 257 prose examples type-check across 3 surface(s)'. Two gates first answered PREREQUISITE NOT MET rather than a verdict (check:docs 'packages/spec/json-schema is older than packages/spec/src'; check:skill-examples stale spec .d.ts, then 'packages/client-react/dist holds no .d.ts declarations') - recorded as NOT MEASURED, never a pass: pnpm --filter @objectstack/spec build and pnpm --filter @objectstack/client-react... build were run (both 'os-verify-lock: VERDICT command-exit 0') and both gates re-run to the green verdict lines above. Repo-wide pnpm lint is CI's: DECLARED NARROWING with its three pieces - (1) eslint's own config resolution answers, for the only changed file, 'File ignored because no matching configuration was supplied'; (2) --format json returns 1 result, 0 errors; (3) the diff adds no eslint config and no JS/TS source, so no untouched file's verdict can move. Every figure measured on the final tree, git rev-parse --short HEAD = 472e204e1 (nothing committed after the gate run).", "mcp_calls": "0 - every GitHub read and write went through REST with $GH_TOKEN; dedup used the REST list endpoint (714 open items paged) plus a local grep, never search_issues", "open_questions": [], "out_of_scope_findings": [ "filed as #15587: plugin-auth - a sign-up for an address that already has a sys_user row answers 200 with a user id no row holds and persists nothing (posture email_domain); the same call under invite_only is refused 422 USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL", "filed as #15588: plugin-auth - both remedies in the no_sign_in_account_at_boot report are unexecutable as written (opening the posture forces EMAIL_NOT_VERIFIED and cannot admit an existing person; a hand-written credential row authenticates nothing) and the second one silences the report, because the probe asks only whether ANY sys_account row exists" ] }
Generated by Claude Code
LANDED — PM seat
domain:devx @ objectstack(#6023), sessionsession_012zGPuVVX3deAx9LdjK8jCk.PR #15586 merged 2026-09-04T23:11:19Z (squash
f1d787294; enqueued 22:48Z, one merge-group run, no eviction). Probed onorigin/mainafter a re-fetch (HEADf1d787294):content/docs/deployment/self-hosting.mdxcarries the new subsection### A seeded directory opens no bootstrap windowwith the measured 403/403/401 table (SELF_REGISTRATION_CLOSED), the boot diagnostic's grep tokenno_sign_in_account_at_bootand its blind spot, the one-rowsys_invitationrecovery with its measured result (adminPromoted: trueonsingle), the two measured non-remedies, and theNODE_ENV=developmentgate on the dev seed; both first-boot paragraphs link the subsection.Stripping
pm:dispatched+ assignee;Fixes #14495closed this card. The two plugin-auth findings the measurement produced (#15587, #15588) stay bare for triage.
Generated by Claude Code
- added a commit that references this issue
on Sep 17, 2026
Filed by the director seat (session
session_01ShyhexkB2d1AeRZ85tgAAe, 2026-09-02) as the follow-up the ruling on #14349 names. ⛔domain:*is triage's to produce; not set here (the fix lands incontent/docs/**).Provenance
#14349 was ruled A by the maintainer on 2026-09-02 (verbatim on that card): the bootstrap carve-out keeps counting human users, so a production deployment that seeds a people directory (
sys_userrows) and no credentials (sys_accountrows) stays unrecoverable from inside — nobody can sign in, theinvite_onlydefault refuses self-registration, and there is no administrator to invite anyone. The door does not move; the operator must provision an account out of band. #14353 (queued) adds the boot-time diagnostic. This card is the documentation half.The defect
Triage verified on
origin/main@72adb7f(#14349, comment 5503693743) thatcontent/docs/deployment/self-hosting.mdx:428tells the operator to "have each of those people register with exactly that address and verify their email". On the population above that instruction is refused by theinvite_onlydefault, with nobody able to open the posture or send an invitation. So the published recovery path is the one that is shut. Re-check:git grep -n "register with exactly that address" origin/main -- content/docs/deployment/self-hosting.mdx.Acceptance
sys_userrows and zerosys_accountrows under theinvite_onlydefault outside development, which operator action actually produces a first login (an env-declared owner path, a CLI command, a posture change, a direct data write — whichever exists). TheNODE_ENV=developmentdev-admin seed is not the answer for production and must be described as development-only.sys_accountrows boots silently into an unrecoverable state — say so loudly atkernel:ready#14353 emits once it lands.premise_still_valid: falsewithpr: nullis a legitimate delivery here.Size: S. Refs: #14349 (ruled A) · #14353 (boot diagnostic) · #14157 (the development-lane fix) · #11184 (owner comes only from the env-declared owner email on walled postures).
Dedup:
search_issuesin this repo for the self-hosting registration path → no open card (closed neighbours #11184, #9441 are distinct).