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
plugin-security: the first sign-up on a freshly seeded database blocks ~45 s while claimSeedOwnership re-owns every seed row through the full write pipeline — app hooks, record-change flows, approvals and notification emails fire for a bookkeeping write #22067
Filing gate: ① product defect with reach measured. Class (a). reach: public door, POST /api/v1/auth/sign-up/email (the first human sign-up on a freshly seeded database), which every new deployment and every demo reset passes through. Measured on @objectstack/* 17.7.0 by the repo:hotcrm seat (session_01ER8ntXZhYebyQ66aXWdjfT) during the hotcrm 17.7.0 upgrade (hotcrm PR #2008). The maintainer ruled in that session to file it here: 「只立卡」.
Who acts on it: objectstack triage routes it. Likely domain:security (plugin-security) with a note to the engine lane. ⛔ Not a claim; triage sets type and grade.
Measured
Setup: hotcrm at 56d98f7e built to an artifact, then objectstack start --artifact … -d file:… --log-level debug on an EMPTY database. The default seed is 354 rows ([Seeder] Seed loading complete {"inserted":354,…}). The first user signed up through REST.
run
seed settled
wait before sign-up
first POST /auth/sign-up/email
17.7.0, debug log
32 s after the health check went green
120 s more
45.8 s
17.7.0, browser (console Create Account)
—
~45 s
44.3 s
17.7.0, REST, same procedure as the 17.6.0 row
—
20 s
34.1 s
17.6.0, REST
—
~20 s
~31 s
any later sign-up
—
—
0.02 s (403: self-registration closes after the first user, as designed)
So this is not a 17.7.0 regression, and it is not the seed still writing: on the debug run the seed had settled two minutes before the request.
Where the 45.8 s go. Request sent at 09:21:45.0Z, answered at 09:22:30.8Z. The log during that window:
09:21:45.5Z — [security] first user promoted to platform admin, then the default-organization bind.
09:21:45.9Z → 09:22:30.6Z, 44.7 s, ending in: [security] handed 350 seeded record(s) to platform admin … (17 of 18 eligible object(s) had unowned rows) followed by platform bootstrap complete {"adminPromoted":true,"ownershipClaimed":350,…}.
The response is sent immediately after. The claim runs inside the sign-up request.
What those 44.7 s did, counted from the debug log of the window (13,261 lines):
what
count
Update operation starting on business objects (crm_campaign 64, crm_opportunity 38, crm_quote 16, …)
~200
[BodyRunner] hook fired — app hooks bound from metadata, run in the QuickJS sandbox (opportunity_amount_rollup, campaign_metrics_refresh, opportunity_lifecycle, lead_automation, …)
record-change flows that ran: case_escalation ×4, task_urgent_alert ×4
8 runs
approval requests opened (approval node suspended run, flow:opportunity_approval on two seeded deals)
2
notification emails handed to the transport ([LogTransport] would send email)
8
sys_audit_log / sys_activity inserts
392 / 391
Why it matters
Latency on the one request every deployment makes first. The console sits on Creating account… for 45 s. On a slower database, or a larger seed, it grows with the row count. A proxy or client with a 30 s timeout fails the very first sign-up, even though the account and the claim both complete server-side (measured: the account signs in afterwards).
A bookkeeping write fires business automation. Re-owning seed rows to the first admin is attribution, not a user event. Yet it:
escalates cases;
raises urgent-task alerts;
opens approval requests on demo deals;
emails the new admin.
With a real SMTP transport, the first admin's inbox fills at sign-up. The seed itself is written under SEED_WRITE_EXECUTION_CONTEXT (skipTriggers), on the principle the engine states at engine.ts:5413: "seed loads end-state data, not user events". The claim that follows it does not keep that principle.
:483–487: reown = ql.update(schema.name, { owner_id: adminUserId }, { where: predicate, multi: true, context: SYSTEM_CTX }). The context has no skipAutomations and no skipTriggers, so the predicate write dispatches every per-row after-hook and the record-change trigger.
packages/plugins/plugin-security/src/bootstrap-platform-admin.ts:1168: await claimSeedOwnership(ql, chosen.id, …), inside the bootstrap that the first sign-up awaits.
packages/objectql/src/engine.ts:4185–4204: session.skipAutomations suppresses hooks bound FROM METADATA (entry.meta). Code-registered hooks run regardless: "audit, capability gates, sharing projection — have no meta and always run: the opt-out must never bypass security or audit (数据导入:批量 insert 给 Hook 的输入形状与单条不一致(installFlatInput 失效);「运行自动化与触发器」开关是摆设且默认值应为选中 #2922)".
engine.ts:5413–5419: skipAutomations implies skipTriggers, so the record-change flow dispatch is suppressed too.
A direction, for triage to rule on (⛔ not a ruling)
Run the claim's reown writes with { isSystem: true, skipAutomations: true }.
The engine already keeps audit and the sharing projection running under that flag. The claim's own comments (the per-row hook budget, the plugin-sharing recompute) depend on exactly those code-registered hooks, not on app hooks.
The flag would remove the app hooks, the flows and the approvals/emails, and most of the 44.7 s.
Open question for the ruling: does any app-hook contract need to see an owner change made by the claim? An owner-propagation hook would be one. If so, that is the case that decides between skipAutomations and a narrower flag.
Separately: should the claim run inside the first sign-up request at all, or after the response (it already settles on app:seeded for later seeds)?
Not measured: the effect of either change. An A/B in the hotcrm session that patched the installed bundle was stopped by the session's safety policy (it would have edited installed dependencies). The measurement belongs on an objectstack branch built from source.
None of them measures the sign-up latency or the automation side effects.
Duplicate check
Semantic issue search on objectstack, three queries:
claimSeedOwnership seed ownership claim slow first sign-up fires hooks flows automation: 8 hits, all closed. The three above are the related ones; the rest concern dev-admin seeding, seed budgets and owner seeds.
first user sign up takes 30 seconds platform admin bootstrap: 0 hits.
ownership claim owner_id update triggers record-change flows approvals notifications emails skipAutomations seed: 6 hits, all closed and on other surfaces.
Positive control: the first query surfaces #14530 and #14719, the claim's own cards.
Dedupe words: first sign-up slow · claimSeedOwnership latency · ownership claim fires hooks · seed claim triggers flows · claim skipAutomations · first admin emails on sign-up
Filing gate: ① product defect with reach measured. Class (a). reach: public door,
POST /api/v1/auth/sign-up/email(the first human sign-up on a freshly seeded database), which every new deployment and every demo reset passes through. Measured on@objectstack/*17.7.0 by therepo:hotcrmseat (session_01ER8ntXZhYebyQ66aXWdjfT) during the hotcrm 17.7.0 upgrade (hotcrm PR #2008). The maintainer ruled in that session to file it here: 「只立卡」.Who acts on it: objectstack triage routes it. Likely
domain:security(plugin-security) with a note to the engine lane. ⛔ Not a claim; triage sets type and grade.Measured
Setup: hotcrm at
56d98f7ebuilt to an artifact, thenobjectstack start --artifact … -d file:… --log-level debugon an EMPTY database. The default seed is 354 rows ([Seeder] Seed loading complete {"inserted":354,…}). The first user signed up through REST.POST /auth/sign-up/emailSo this is not a 17.7.0 regression, and it is not the seed still writing: on the debug run the seed had settled two minutes before the request.
Where the 45.8 s go. Request sent at 09:21:45.0Z, answered at 09:22:30.8Z. The log during that window:
[security] first user promoted to platform admin, then the default-organization bind.[security] handed 350 seeded record(s) to platform admin … (17 of 18 eligible object(s) had unowned rows)followed byplatform bootstrap complete {"adminPromoted":true,"ownershipClaimed":350,…}.What those 44.7 s did, counted from the debug log of the window (13,261 lines):
Update operation startingon business objects (crm_campaign64,crm_opportunity38,crm_quote16, …)[BodyRunner] hook fired— app hooks bound from metadata, run in the QuickJS sandbox (opportunity_amount_rollup,campaign_metrics_refresh,opportunity_lifecycle,lead_automation, …)Flow '…' skipped: start condition not met)case_escalation×4,task_urgent_alert×4approval node suspended run,flow:opportunity_approvalon two seeded deals)[LogTransport] would send email)sys_audit_log/sys_activityinsertsWhy it matters
Latency on the one request every deployment makes first. The console sits on Creating account… for 45 s. On a slower database, or a larger seed, it grows with the row count. A proxy or client with a 30 s timeout fails the very first sign-up, even though the account and the claim both complete server-side (measured: the account signs in afterwards).
A bookkeeping write fires business automation. Re-owning seed rows to the first admin is attribution, not a user event. Yet it:
With a real SMTP transport, the first admin's inbox fills at sign-up. The seed itself is written under
SEED_WRITE_EXECUTION_CONTEXT(skipTriggers), on the principle the engine states atengine.ts:5413: "seed loads end-state data, not user events". The claim that follows it does not keep that principle.Code reading (
origin/main8caa131e52)packages/plugins/plugin-security/src/claim-seed-ownership.ts:132:const SYSTEM_CTX = { isSystem: true };:483–487:reown=ql.update(schema.name, { owner_id: adminUserId }, { where: predicate, multi: true, context: SYSTEM_CTX }). The context has noskipAutomationsand noskipTriggers, so the predicate write dispatches every per-row after-hook and the record-change trigger.packages/plugins/plugin-security/src/bootstrap-platform-admin.ts:1168:await claimSeedOwnership(ql, chosen.id, …), inside the bootstrap that the first sign-up awaits.packages/objectql/src/engine.ts:4185–4204:session.skipAutomationssuppresses hooks bound FROM METADATA (entry.meta). Code-registered hooks run regardless: "audit, capability gates, sharing projection — have nometaand always run: the opt-out must never bypass security or audit (数据导入:批量 insert 给 Hook 的输入形状与单条不一致(installFlatInput 失效);「运行自动化与触发器」开关是摆设且默认值应为选中 #2922)".engine.ts:5413–5419:skipAutomationsimpliesskipTriggers, so the record-change flow dispatch is suppressed too.A direction, for triage to rule on (⛔ not a ruling)
reownwrites with{ isSystem: true, skipAutomations: true }.skipAutomationsand a narrower flag.app:seededfor later seeds)?Related, not duplicates
Duplicate check
Semantic issue search on objectstack, three queries:
Positive control: the first query surfaces #14530 and #14719, the claim's own cards.
Dedupe words: first sign-up slow · claimSeedOwnership latency · ownership claim fires hooks · seed claim triggers flows · claim skipAutomations · first admin emails on sign-up
Generated by Claude Code