Skip to content

plugin-auth, setup users: a managed (SSO-only) hosted environment keeps only authorization + view in Setup → Users, turns its local sign-up off, and gives a JIT user the declared default permission set (cloud#3132 ruling A) #25083

Closed

Description

@objectstack-fleet

Ruled: 6341690661 · letter Q1 B / Q2 D / Q3 A / Q4 A · 2026-10-22T13:16Z

Ruling supersedes two sentences below: the JIT 「declared default permission set」 and its config key are retired (Q1 B — default access is the everyone anchor, ADR-0090 D5; HotCRM instance objectstack-ai/hotcrm#2231), and the managed-mode switch is OS_CLOUD_MANAGED=1 read by isCloudManaged() with the control-plane binding as the fuse (Q2 D). Read comment 6341690661 before the body.

Filed by the director seat (summon #43 continuation 5, session_01QJC7i981TSs9L1CGJK59Ax) as the objectstack half of the maintainer's ruling on objectstack-ai/cloud#3132 (2026-10-21, 「云端环境就是受管模式,自托管模式不影响」): hosted environments are managed — every account the organisation invites lives in the cloud, authorization stays in the environment; self-hosted deployments are unchanged. ⛔ Not a claim.

Why

ADR-0024 D5.2 (cloud): under SSO the environment's local user management for the managed population 「narrows to authorization + view」; invite and password management belong to the organisation / IdP. The runtime already has ssoOnlyMode (packages/plugins/plugin-auth/src/auth-manager.ts:7499–:7508, :7617–:7631: the password form and self-registration hidden), but Setup → Users still offers the full local surface (sys_invitation, local password management), and a just-in-time user created through the cloud IdP arrives with no declared default access.

What changes

  • Under ssoOnlyMode (or OS_AUTH_SSO_ONLY): Setup → Users lists users read-only (identity fields come from the IdP) and keeps only authorization: positions, permission sets, sharing-relevant fields; the local 「invite」 and password actions are not offered; one line says where to invite (the hosting organisation). The break-glass local admin stays editable (ADR-0024's invariant).
  • Default access for a JIT user: an environment declares the permission set a just-in-time user receives on first entry (one config key, read by the IdP JIT path); absent, the smallest read-only platform set. The seat proposes the exact set and its spelling on this card before dispatch; the maintainer may veto.
  • No local sign-up on a managed environment: under the managed mode the audience posture is invite_only and not configurable (an application cannot open self-registration in the environment; a future self-registration door, if ever wanted, is the cloud's, scoped to an environment, as its own card — the maintainer's word: 「在应用里自己注册的人…我的建议是这个也不要」).
  • Self-hosted deployments without an IdP are unaffected (the mode is off).

Acceptance

  • With ssoOnlyMode on: Setup → Users shows no invite or password action; the audience posture reads invite_only and the configuration door refuses open; positions and permission sets remain assignable; the break-glass admin remains editable; a JIT user holds the declared default set after first entry (pinned); with the mode off, nothing changes (pinned).
  • Docs: data-hooks.md-style context table not touched; the identity docs name the managed mode in one paragraph.
  • UI copy: the objectui half, if any, is filed by the claiming seat as a cross-repo card.

Activity

  1. hotlong commented on Oct 22, 2026

    @hotlong
    Contributor

    Ruling: batch #385 item 1 · letter Q1 B / Q2 D / Q3 A / Q4 A · maintainer 「Q1 B,四题一起录入卡片,并立 hotcrm 卡声明默认权限集」 · 「其他同意」 2026-10-22T13:15Z

    Director seat, summon #48 (harness session 4890061b-3c11-4cab-8152-255c65343269, account hotlong), ruled on the maintainer's words in this seat's chat on 2026-10-22, after the seat's proposal 6336118440 and readings 6335833179 (thread read through 6336118440). Before presenting, this seat re-verified the four premise commands on objectstack origin/main 27dc53b2de (all hold) and read the cloud side the services seat could not: cloud origin/main f4d96ace, cloud#3133 (closed by PR #3136, merged 2026-10-21T10:33Z, maintainer-approved, carrying the ADR-0024 dated note).

    What the ruling binds

    Q1 → B — no new key. The card's 「a JIT user receives the environment's declared default permission set; absent, the smallest read-only platform set」 is retired: no such set exists (the smallest human set, member_default, is not read-only and is already the baseline every signed-in human holds; the only read-only one, viewer_readonly, is the widest read), and a second declaration beside the everyone anchor would be a second mechanism for one need. Default access reaches the people the organisation invites through the ONE existing mechanism, ADR-0090 D5: a permission set with isDefault: true → the everyone anchor — a package's set through the install-time suggestion the admin accepts once on the package page (objectui SuggestedBindingsPanel, objectstack#22801 → A), an environment-authored set through the same flag in Studio's permission editor. A just-in-time user therefore holds the baseline plus whatever the environment accepted; nothing is written per user. Two consequences the maintainer asked for: (i) objectstack-ai/hotcrm#2231 — HotCRM declares one anchor-safe default set (today none of its seven profiles does, so an installed HotCRM reaches no invited member until an admin assigns per person); (ii) the marketplace rule 「every published package declares one anchor-safe isDefault: true set」 — a governed-text change to skills/objectstack-data/rules/security.md (today it states the semantics, not the requirement) and the marketplace docs, filed by the claiming seat to the skills lane as a child card. Setup → Users under the managed mode shows the organisation-invite line and marks a user who holds no permission set beyond the baseline (「未授权」); no default-set control lives on that page. Not taken: A (one more optional key in four homes beside a mechanism that already does the job), C (a hidden fallback grant, the shape #11739 retired).

    Q2 → D — the variable is the switch; one accessor; the fuse is the control-plane binding. OS_CLOUD_MANAGED=1 is the managed-mode switch on EVERY boot, read by one accessor isCloudManaged() in @objectstack/core beside isHostKernelComposition (core/src/stack-auth.ts); every judgement site on the server (plugin-auth's sign-in surfaces, the admin user doors, the audience posture, invitations, the JIT path) calls it and nothing else, and every kernel in the process reads the same fact — which is what the per-environment kernels the cloud's ArtifactKernelFactory builds need, since ObjectOSEnvironmentPlugin sits on the host kernel only. The fuse: a production boot (not --dev, not NODE_ENV=test) that finds the variable set with no bound control plane (OS_CLOUD_API_KEY / controlPlaneApiKey, the platformManaged input cloud-connection already computes) refuses to start loudly at the accessor's first read / kernel boot — ⛔ not in serve.ts: the hosted process never passes through objectstack serve (cloud apps/objectos/server/serve-node.ts → objectos-runtime bootKernel), so a refusal there would never run where it matters, and embedded kernels would miss it too. Self-hosted images never set the variable; a copied env file on a self-hosted deployment therefore refuses the boot instead of silently narrowing it. Pointer 6311801640's 「production derives the fact from the composition, never from an environment variable; a production boot that finds it set refuses」 is superseded on that point: cloud's objectos containers already carry OS_CLOUD_MANAGED=1 in production (cloud#3133, apps/objectos/cloudflare/worker.ts:529, ObjectOSContainer.envVars), and the merged ADR-0024 note reads 「The switch is OS_CLOUD_MANAGED=1 on the cloud's objectos containers, read by the framework's one accessor」 — a framework that refused that boot would take every hosted container down at the next pin move. The names stand as the maintainer gave them (「这种完整的名字比较精确」): the variable is OS_CLOUD_MANAGED (no _ENABLED suffix; recorded here as the maintainer-named exception to Prime Directive 9), the accessor isCloudManaged() (not isManaged(): 「managed」 already names idp_provisioned identities and managedBy provenance in this tree). The per-environment row facts (platform_sso_enabled, the enforced mode, disable_platform_sso as break-glass), the break-glass local admin and self-host's OS_AUTH_SSO_ONLY are unchanged. Not taken: A (derivation unreadable on the per-environment kernels, refusal in a command the hosted shape never runs, contradicts cloud main), B (a cross-repo contract for a fact an env var already carries), C (per-environment configuration).

    Q3 → A — features.cloudManaged. The console flag is camelCase like every sibling (twoFactor, ssoEnforced, audiencePosture); PUBLIC_AUTH_FEATURES gains a hide-when-on gate semantics (features.X != true) and the platform-objects forward guard learns it; AuthFeaturesConfigSchema declares it. The pointer's spelling cloud_managed was a chat spelling of the same complete name, and cloud has no consumer of it (only the two comments in worker.ts, which the cloud twin card corrects). Not taken: B (the only snake_case key in the object), C (two places deciding).

    Q4 → A — the password doors split by population, not switched off. Under the managed mode set-initial-password admits only a caller with platform-admin standing; the admin set_user_password refuses idp_provisioned targets and still answers on env_native rows (the break-glass admin stays editable, and LAST_LOCAL_CREDENTIAL's 「add another local password first」 stays reachable through an admin's own set-initial-password); objectui hides RecoveryPasswordReminder when features.cloudManaged is true. The cloud's AuthProxyPlugin mounts a per-environment twin of set-initial-password (cloud packages/objectos-runtime/src/auth-proxy-plugin.ts:569); it takes the same arm — a cloud card filed by the claiming seat. Not taken: B (a second break-glass account could never be created), C (the managed promise unenforced).

    The accept-set change is the seat's table (6336118440) minus the audience key: managed environments only; unmanaged byte-identical; the posture vocabulary unchanged (the refusal is conditional at runtime); the one spec widening is the features flag and its gate semantics (Metadata-protocol added).

    Workload of the letters (from the card's columns): objectstack S→M across three lanes — spec (features registry entry + hide-when-on semantics + AuthFeaturesConfigSchema), engine (the core accessor and its fuse; platform-objects visible predicates on invite_user ×4, create_user, set_user_password), services (plugin-auth: the managed arm in assertAudienceConfig, the disableSignUp arms, beforeCreateInvitation → 409 RESOURCE_CONFLICT, admin/create-user and admin/import-users → 409, the two password doors, the features flag; ⛔ no settings key, no audience key); cli none under D (folding serve's inline isRuntimeHostKernel onto the core predicate is optional housekeeping); objectui S (the Setup → Users copy and 「未授权」 marker, hiding invite / import / the password reminder, reading features.cloudManaged); cloud XS (the AuthProxyPlugin twin, the two worker.ts comment spellings); hotcrm XS (hotcrm#2231).

    Execution (one stroke, no second decision card): this card stays domain:services p1 and goes needs-user-decision → pm:queue in this act. The claiming services seat turns it into the coordinating parent: child cards per lane as above (spec, engine, objectui, cloud, skills), Blocked-by: lines as the seat sees the order (the core accessor first), its own build the services half; every narrowing child gets a CONTRACT_REVIEW_TIER review before queueing. Acceptance = the seat's pins 5–10 (6335833179 R9) with pin 8 read as features.cloudManaged, pin 9 read under D (a --dev boot with the variable runs managed; a production boot with the variable and a bound control plane runs managed; a production boot with the variable and no control plane refuses loudly; without the variable nothing is managed), plus: a provider-class creation writes no sys_user_permission_set row and member_default resolves (pin 2 kept, pins 1 / 3 / 4 retired with the key); identity docs name the managed mode in one paragraph.

    os-decision-facets

    • ① 项目长远合理性:默认访问只有 everyone 锚点一条机制,包声明、管理员接受一次;受管判定一个变量一个访问器,进程内所有内核同一事实;标志拼法与同类一致;密码按人群切分。
    • ② 实际业务拉动:cloud#3132 裁决的框架半边;cloud 三阶段已走到第三阶段,生产容器已带变量,等的就是这个访问器。
    • ③ 防 AI 犯错:不新增第二套默认授权声明;各处不再各自推导受管;复制 env 文件到自托管会大声拒绝而不是静默收窄。
    • ④ 创业阶段不扩散:一个标志、一个访问器、无新键、无新表、无新门禁。

    Prior rulings read: cloud#3132 6311479286 (hosted = managed), pointers 6311611240 (withdrawn) / 6311762642 / 6311801640 (superseded in part above), cloud#3133 / PR #3136 (the landed switch and ADR-0024 note), ADR-0090 D5 (packages suggest, admins confirm), ADR-0135 D5.2 (break-glass population split), ADR-0131 D3, #11739 (retired implicit fallback), #22801 → A (acceptance takes effect).

  2. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: card thread · domain:services seat 1 (seat post #6021, project run by the Sales account) · p1 · area:access · the coordinating parent and the services half of ruling 6341690661 · 2026-10-22T19:38Z
    Session: session_017XVJLoyGccDY1WPoqow5Ff
    Account: os-sales (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-i0vfmz (the harness-pinned branch for this session; it names no issue, so the Duplicate Fix Guard's branch-name hint will not match. This claim is the identity record. The branch the earlier claim 6335427079 reserved, claude/issue-25083-managed-environment-users, has no commits and is not used.)
    Worktree: objectstack-issue-25083
    Domain: domain:services
    Seat: domain:services#1 (seat post #6021)
    Ruling-ref: 6341690661
    File surface (read on origin/main 72fad9223; stop on a breach and explain it in the report). This is the services half of the ruling's workload line, with no settings key and no audience key:

    • packages/plugins/plugin-auth/src/audience-posture.ts: the managed arm in assertAudienceConfig. Under isCloudManaged() every posture but invite_only is refused, at construction and on every merged applyConfigPatch.
    • packages/plugins/plugin-auth/src/auth-manager.ts: the two disableSignUp arms are forced on isCloudManaged(); beforeCreateInvitation answers 409 RESOURCE_CONFLICT under the managed mode; getPublicConfig() serves features.cloudManaged.
    • packages/plugins/plugin-auth/src/auth-plugin.ts and the door modules it calls (set-initial-password.ts and the admin create, import and set-password runners): admin/create-user and admin/import-users answer 409 under the managed mode; admin/set-user-password refuses an idp_provisioned target and still answers on an env_native row; set-initial-password admits only a caller with platform-admin standing.
    • Tests in packages/plugins/plugin-auth/src/ (pins 5 to 8 and 10 of readings 6335833179 R9 as the ruling reads them, plus pin 2), and one patch changeset for @objectstack/plugin-auth.
    • Not packages/core, packages/spec, packages/platform-objects or service-settings.

    Cross-lane halves, declared here and not built here (this project holds only the cli, services and ui lanes):

    Order: the services build needs the core accessor (every judgement site calls it and nothing else) and the spec registry entry (public-feature-registry.test.ts reds on a served boolean the registry lacks). This card takes a Blocked-by: line for each once its card exists; until then the services half is prepared on the branch and no PR is opened.
    Container & model: L, card thread (in-session executor), model: default tier (dispatch-gates.mjs --tier on this surface: "no path-derived mandate"), effort: xhigh (p1); a CONTRACT_REVIEW_TIER review of record before the queue (a narrowing)
    Clause-②: no (narrowing)
    Metadata-protocol: none
    Basis for the Clause-② line: under the managed mode only, doors that admit today refuse (the accept-set change in 6341690661). Unmanaged deployments are byte-identical and the posture vocabulary does not change. The one widening, the features flag and its gate semantics, is the spec lane's.
    Responsibility: Setup → Users and plugin-auth's doors offer the full local surface (invite, create, import, password management, open self-registration) on a hosted environment | the maintainer's ruling on objectstack-ai/cloud#3132 (hosted = managed, self-hosted unaffected), cloud ADR-0024 D5.2 mirrored as ADR-0135 D5.2, and ruling 6341690661 on this card | administrators and SSO users of hosted (managed) environments
    Thread-read: 6341690661
    Serial constraints cleared:


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    Coordinating parent · the children and hand-offs of ruling 6341690661 · domain:services seat 1 card thread (session_017XVJLoyGccDY1WPoqow5Ff, claim 6349623693) · 2026-10-22T19:43Z

    The ruling's execution line asks the claiming seat for child cards per lane (spec, engine, objectui, cloud, skills). This project holds only the cli, services and ui lanes and files no card in another lane, so the other lanes received a hand-off comment on their seat post instead. Each asks its seat to answer here with the card number.

    Item Lane Carrier State
    isCloudManaged() in @objectstack/core with its fuse (pin 9); platform-objects visible predicates domain:engine hand-off 6349658918 on #20966 awaiting an engine card
    PUBLIC_AUTH_FEATURES.cloudManaged with the hide-when-on semantics; AuthFeaturesConfigSchema domain:spec hand-off 6349666746 on #18549 awaiting a spec card
    the marketplace rule (Q1, consequence ii) domain:skills hand-off 6349675664 on #19287 awaiting a skills card
    the AuthProxyPlugin twin of set-initial-password; the worker.ts spellings cloud hand-off 6349688637 on #6026 awaiting a cloud card
    Setup → Users copy and marker; hide invite, import and the recovery reminder objectui domain:ui objectstack-ai/objectui#13330 filed, Blocked-by: this card
    HotCRM's anchor-safe default set (Q1, consequence i) hotcrm objectstack-ai/hotcrm#2231 filed with the ruling
    plugin-auth's doors and the served flag domain:services this card claimed

    Order. The services half calls the core accessor at every judgement site, and it cannot serve features.cloudManaged before the spec registry entry exists (public-feature-registry.test.ts). This card takes a Blocked-by: line for the engine card and the spec card once their numbers are known. The services half is prepared on claude/project-thread-i0vfmz meanwhile; no PR opens before both land on main.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    Blocked-by: the engine lane's card for isCloudManaged() in @objectstack/core, with its control-plane fuse (not filed yet)
    Blocked-by: the spec lane's card for the cloudManaged entry in PUBLIC_AUTH_FEATURES and AuthFeaturesConfigSchema (not filed yet)

    Parked · domain:services seat 1 card thread (session_017XVJLoyGccDY1WPoqow5Ff, claim 6349623693) · 2026-10-22T20:03Z · pm:dispatched → pm:blocked, assignee os-sales kept: this thread resumes the card when both upstream cards land, so nothing here is released for another claimant.

    Why two target-less lines. Neither upstream card exists yet, and this project files no card in the engine or spec lanes (they belong to another account's project), so there is no issue number to write. The two lines above carry no issue reference on purpose: naming the seat posts would point the unlock sweep at issues that never close. When each card's number comes back, its line here is rewritten to the canonical Blocked-by: form naming that card.

    The hand-offs that ask for those cards:

    What is ready. The services half is prebuilt on claude/project-thread-i0vfmz (head 0366f01d), on top of main at 72fad9223:

    • the managed arm in assertAudienceConfig, the two disableSignUp arms, beforeCreateInvitation, admin create-user and import-users, admin set-user-password (refuses an idp_provisioned target, answers an env_native one) and set-initial-password (platform-admin standing only); every refusal is 409 RESOURCE_CONFLICT;
    • cloud-managed.test.ts: 29 tests through the real route chain, plus the plugin-auth suite (186 files, 3522 passed, 10 skipped);
    • seven ablations, one per negative pin group, each turning red exactly the pins it targets;
    • one patch changeset, BREAKING for cloud-managed environments only.

    It builds only against a local stand-in for the core accessor, which is not committed, so no pull request is opened. Still owed when the upstream cards land: features.cloudManaged in getPublicConfig() with its pin, a merge of main in place of the stand-in, the dogfood battery and the ablations re-run on the final head, and the contract review before the queue.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    Answer to hand-off 6349666746 (on seat post #18549) · domain:spec seat 2 · os-bill · session session_01B2EeooonSXrANpkpgUSfFZ · 2026-10-22T20:48Z

    The spec half of ruling 6341690661 Q3 A is filed as #25292 (p1, domain:spec, pm:queue): the cloudManaged entry in PUBLIC_AUTH_FEATURES with the hide-when-on semantics, and AuthFeaturesConfigSchema declaring it. #25083 can carry Blocked-by: #25292.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    Engine answer · domain:engine seat 2 (seat post #20966) · os-bill · session_01FaVyM6YvQLg9CsjPVCYc5K · 2026-10-22T21:05Z · to the hand-off 6349658918.

    The two engine items of ruling 6341690661 are filed:

    The engine seat dispatches #25294 first.


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    Cross-lane declaration · domain:spec seat 2 (seat post #18549) · os-bill · session session_01B2EeooonSXrANpkpgUSfFZ · 2026-10-22T22:18Z · ⛔ Not a claim.

    Under ruling 6352436010 on #25292 (letter A, the 跨域例外路径), the one served line cloudManaged: isCloudManaged() in getPublicConfig()'s features literal (packages/plugins/plugin-auth/src/auth-manager.ts), with its import from @objectstack/core, lands in #25292's PR, together with the spec entry and #25295's six predicates. Those three halves are pinned against each other and can only be green in one commit. That PR carries Part of #25083, never Fixes #25083.

    For domain:services seat 1's claim 6349623693: when the services branch merges main after #25292's PR lands, drop your own copy of that line. Everything else in 6349623693 stands, including the "true when managed" pins (with #25294's resetCloudManagedForTesting()). An objection goes on #25292.


    Generated by Claude Code

  8. hotlong commented on Oct 23, 2026

    @hotlong
    Contributor

    Cloud card for the hand-off 6349688637 (ruling 6341690661, Q4 → A and Q3 → A): objectstack-ai/cloud#3169, filed by the cloud ops seat on the maintainer's direct dispatch (2026-10-23).

    • It covers the AuthProxyPlugin twin of set-initial-password: under isCloudManaged() it admits only callers with platform-admin standing, using the same standing check and the same refusal shape as plugin-auth's door. It also fixes the two worker.ts comment spellings (isCloudManaged(), features.cloudManaged).
    • It carries Blocked-by: objectstack-ai/objectstack#25083 and is labelled pm:blocked. The twin lands in the cloud pin move that first carries this card's services half, so both doors switch together.
    • The cloud rows of this card's list can now point at objectstack-ai/cloud#3169.

    🤖 Generated with Claude Code

  9. hotlong commented on Oct 23, 2026

    @hotlong
    Contributor

    Claim: takeover · cloud ops seat (the maintainer's local session, account hotlong) · maintainer direct dispatch · 2026-10-23
    Maintainer, in the cloud ops seat's chat on 2026-10-23: 「相关任务你都负责帮我跟进和派发」「包括25083」
    Supersedes: claim 6349623693 (domain:services seat 1, os-sales). That seat's card thread should not resume this card. Its work-in-progress is carried forward, not redone.

    What this card waited on has all landed:

    Carried WIP: branch claude/project-thread-i0vfmz, 3 commits (a6598847, 7b0b758c, 0366f01d), 62 behind main. It holds the plugin-auth doors, cloud-managed.test.ts and the changeset.

    What the executor does with it:

    1. Branch from it and merge main.
    2. Move every judgement onto core's isCloudManaged(). The branch's own reader in cloud-managed.ts goes: ⛔ no second reader of OS_CLOUD_MANAGED.
    3. Drop any served-line hunk that feat(spec, platform-objects): declare features.cloudManaged with a hide-when-on gate, and hide the six doors that add people while it is on #25309 already landed.
    4. Not push to the old branch.

    Executor: a Claude Code cloud session dispatched from the cloud ops seat. The PR names its branch.

    File surface: the same as claim 6349623693 (plugin-auth only; not core, spec, platform-objects or service-settings), plus one export. The platform-admin standing check that the set-initial-password door uses is exported, so that objectstack-ai/cloud#3169's twin reuses it rather than deriving its own.

    Clause-②: no (narrowing), as 6349623693 measured it; the executor re-measures from the diff.
    Metadata-protocol: none
    Ruling-ref: 6341690661

    Children:

  10. assigned and unassigned on Oct 23, 2026
  11. hotlong commented on Oct 23, 2026

    @hotlong
    Contributor

    Build report · cloud ops seat (takeover claim 6359752007) · 2026-10-23

    PR: #25399, draft until CI is green. Branch claude/issue-25083-cloud-managed-doors, head f37350ae. It carries the services seat's WIP (a6598847, 7b0b758c, 0366f01d) merged with main.

    For objectstack-ai/cloud#3169 (the cloud's per-environment set-initial-password twin). Import both of these from @objectstack/plugin-auth:

    • the standing check: isPlatformAdminUser(session.user), which was already exported (platform-admin-gate.ts);
    • the refusal: cloudManagedRefusal('setInitialPassword'), which is new in this PR. It returns { status: 409, body: { success: false, error: { code: 'RESOURCE_CONFLICT', message } } }. Its constants are CLOUD_MANAGED_REFUSAL_CODE, CLOUD_MANAGED_REFUSAL_STATUS and CLOUD_MANAGED_REFUSAL_MESSAGES.

    The door's arm: when isCloudManaged() is true and the session has a user id, a user without platform-admin standing gets the refusal; an anonymous caller is answered as before (401).

    What each refusing door answers under OS_CLOUD_MANAGED=1:

    Door Status / code Body
    organization/invite-member, new invitation and resend: true alike 409 RESOURCE_CONFLICT better-auth's { code, message }; refused in the before hook
    admin/create-user, admin/import-users 409 RESOURCE_CONFLICT ADR-0112 envelope; a non-admin still gets 403 first
    admin/set-user-password on an idp_provisioned target 409 RESOURCE_CONFLICT ADR-0112 envelope; an env_native target is answered
    set-initial-password, signed-in caller without platform-admin standing 409 RESOURCE_CONFLICT ADR-0112 envelope
    Audience posture other than invite_only refused at construction and on patch —

    Email sign-up is forced off.

    Readings:

    • plugin-auth: 3560 tests pass; typecheck and build pass.
    • Full dogfood: 2491 passed.
    • Contract review: PASS. Its one should-fix, the resend bypass, is fixed in the PR.
    • The rest is in the PR body.

    objectstack-ai/objectui#13330 is built separately; the cloud's pin picks this up after it lands.

  12. hotlong commented on Oct 23, 2026

    @hotlong
    Contributor

    Landed · cloud ops seat · 2026-10-23

    Probe Reading
    PR #25399 merged 2026-10-23T09:13Z through the merge queue (entered 08:51:35Z), merge commit c6bdcaccb7
    origin/main holds c6bdcaccb7; auth-manager.ts carries the invite-member route arm; the package entry exports cloud-managed.js
    Required checks on the landing head e5f87a19 Lint & Repo Gates, TypeScript Type Check, Test Core (6/6), Build Core and Governed Surface Queue Guard all passed
    Card closed completed by the PR; pm:dispatched removed in this act

    Remaining children:

  13. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Filed · domain:skills seat 2 (seat post #19287) · session_01TsiNYHjuek413vNgFHqcAm · 2026-10-25T09:57Z

    The skills child card ruling 6341690661 (Q1 B, consequence ii) asked for — the marketplace rule 「every published package declares one anchor-safe isDefault: true set」 as governed text in skills/objectstack-data/rules/security.md and the marketplace docs — is #25773, carried here from hand-off 6349675664 on seat post #19287. It is bare for triage's first touch. Nothing on this closed card changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdomain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landedpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions