Skip to content

AuthPlugin reads the auth settings namespace in the pre-bind window — stored sys_setting rows are ignored at boot under the shipped os serve order #11579

Description

@os-steve

Found by the step-1 measurement of #11045 (the settings bind-window gate). Filed per-plugin rather than fixed there, per that card's ruling: #11045 is the gate, not a fourth declaration.

What happens

AuthPlugin reaches getService('settings') from kernel:ready handlers it registers in start(), and it uses the handle in the same tick:

  • packages/plugins/plugin-auth/src/auth-plugin.ts:1207bindAuthSettings() resolves the handle and then await applySettings(), which calls settings.getNamespace('auth').
  • packages/plugins/plugin-auth/src/auth-plugin.ts:728 — the second acquisition in the same plugin.

It is reached transitively, at depth 3 from one of the registrations:

ctx.hook('kernel:ready', () => runBackfill('kernel:ready'))   // auth-plugin.ts:1029
  → runBackfill (a const inside start())
    → this.ensureAuthSettingsBound(ctx)                        // :1188
      → this.bindAuthSettings(ctx)                             // :1202
        → ctx.getService<SettingsReadSurface>('settings')      // :1207
          → await settings.getNamespace('auth')

and at depth 2 from auth-plugin.ts:674.

SettingsServicePlugin binds its data engine from its own start()-registered kernel:ready hook (settings-service-plugin.ts:153). Handlers run in registration order, and registration order is start() order. AuthPlugin declares dependencies = ['com.objectstack.engine.objectql'] and nothing about settings, so nothing orders it after the settings plugin.

Why it fires on the shipped composition

packages/cli/src/commands/serve.ts does kernel.use(new AuthPlugin(...)) at line 2693; SettingsServicePlugin arrives later, from the capability loop at line ~3417. resolvePluginOrder preserves insertion order for plugins with no edge between them, so auth starts first.

Measured with the real resolvePluginOrder from packages/core/src/plugin-order.ts, fed the verbatim declarations of the five plugins involved in the serve insertion order:

start() order: ["com.objectstack.engine.objectql","com.objectstack.auth","com.objectstack.audit","com.objectstack.service.settings","com.objectstack.mcp"]
auth  before settings?  true

So auth's kernel:ready handlers are registered — and therefore fire — before the engine bind.

Consequence

In that window SettingsService.loadRows takes its this.memory branch, so getNamespace('auth') answers the manifest defaults with source: 'default' while the deployment's persisted sys_setting rows go unread (this is the exact class #10250 documented and made audible via reportPreBindRead). Everything applySettings() derives is therefore computed from defaults at boot: the ADR-0093 membership policy the D6 backfill runs under, and the google_* social-provider config. settings.subscribe('auth', …) only re-applies on a later change, so a workspace that configured auth in Setup and never touched it again keeps booting with the wrong values.

Note the D6 backfill's own comment at auth-plugin.ts:1175 already reasons about this ordering hazard between auth's two hooks — the hazard against the settings plugin is the one still open.

Repair

The #10250 shape — one declaration on AuthPlugin:

optionalDependencies = ['com.objectstack.service.settings'];

requiresServices would not do it: that asserts the service is registered before init() (it always is), and carries no start() ordering.

Verification: after the fix, pnpm check:settings-bind-window must go green with the com.objectstack.auth entry deleted from KNOWN_PRE_BIND_READS in scripts/check-settings-bind-window.mjs — that ledger is shrink-only and errors on a stale entry, so the deletion is part of the fix, not follow-up.

Suggested lane: whoever owns packages/plugins/plugin-auth.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions