Skip to content

feat(server): drop bootstrap-user, first /sign-up becomes the install's identity#902

Merged
buremba merged 1 commit into
mainfrom
feat/chrome-extension-e2e
May 19, 2026
Merged

feat(server): drop bootstrap-user, first /sign-up becomes the install's identity#902
buremba merged 1 commit into
mainfrom
feat/chrome-extension-e2e

Conversation

@buremba
Copy link
Copy Markdown
Member

@buremba buremba commented May 19, 2026

Summary

Removes the fake bootstrap-user (dev@lobu.local / lobudev123) that was pre-seeded on every fresh PGlite install. That seed caused the local-install identity fork that earlier PRs (#896, #898) only worked around: the moment the operator visited /sign-up with a real email, the Mac app + CLI kept polling as bootstrap-user while the web UI showed the new account.

New model: first /sign-up = the install's user. Password typed once at signup; everything after that is passwordless via /api/local-init (Mac, CLI) or /api/exchange-token (menubar→web). LOBU_SINGLE_USER=1 (default-on in lobu run) keeps anyone else from forking the install.

Pairs of edits

  • start-local.ts — deleted ensureBootstrapUser and all BOOTSTRAP_* constants/helpers. ensureDefaultAgent now looks up the first personal-org dynamically; no-op when no user yet.
  • auth/routes.ts /api/local-init — replaced "seed bootstrap, refuse if real users exist" with "find the single real user, mint for them." Zero users → 404 no_user_yet (signup_url: /sign-up). >1 users → 404 not_single_user. Legacy bootstrap-user rows filtered out via id <> 'bootstrap-user'.
  • auth/index.tsx databaseHooks.user.create.before — same id <> 'bootstrap-user' filter on the LOBU_SINGLE_USER count, so legacy installs aren't dead-ended.
  • auth/index.tsx databaseHooks.user.create.after — runs ensureDefaultAgent immediately on first signup so the user has an agent without needing a server restart.
  • index.ts /api/auth/ middleware* — dropped the path-based /api/auth/sign-up/* guard that blocked the first signup. DB hook is the single accurate chokepoint.

Reproducer

Before: fresh lobu run seeds bootstrap-user; operator visits localhost:8788/sign-up → DB ends up with 2 users; Mac app's /api/local-init 403s because non-bootstrap users exist; falls back to device-code OAuth which needs a password the operator forgot.

After: fresh lobu run seeds nothing; operator visits localhost:8788/sign-up → Better Auth creates the single user via the hook (count 0, allowed) → personal org provisioned → default agent provisioned synchronously → Mac app's /api/local-init mints session+PAT for THE user → Keychain populated → zero password re-types after that initial signup.

Test plan

  • make typecheck clean.
  • bun test packages/server/src/__tests__/unit — 201 pass, 0 fail.
  • Codex review: caught path-guard-blocking-first-signup, legacy-bootstrap-counted, no-default-agent-after-signup. All three fixed in this commit.
  • End-to-end with fresh PGlite: signup at /sign-up → Mac app auto-connects → Chrome ext side panel mounts iframe signed-in.

Known limitation (deferred)

Two concurrent first-signups can both observe count=0 and both INSERT, leaving 2 users and /api/local-init permanently returning not_single_user. Not a practical risk on a local loopback install; the clean fix is a DB-level partial-unique-index WHERE id <> 'bootstrap-user', separate PR.

Summary by CodeRabbit

  • Refactor
    • Improved single-user mode enforcement to correctly handle account exclusions
    • Enhanced signup flow with automatic provisioning of default agent after personal organization creation
    • Simplified local development initialization process for user account setup

Review Change Stack

…'s identity

Removes the fake `bootstrap-user` (`dev@lobu.local` / `lobudev123`) that
start-local.ts used to pre-seed on every fresh PGlite install. That seed
was the root cause of the local-install identity fork: the moment an
operator visited /sign-up via the web UI with their real email, the Mac
app + CLI kept authing as `bootstrap-user` while the web UI showed the
new account — same machine, two identities, drift.

New model: the first person who signs up IS the install's user. They type
a password once at /sign-up; everything after that is passwordless via
`/api/local-init` for local processes (Mac app, CLI) and `/api/exchange-token`
for the menubar→web handoff. Single-user-mode keeps anyone else from
forking the install.

Server changes:

- start-local.ts: deleted `ensureBootstrapUser` and every BOOTSTRAP_*
  constant + helper (pickBootstrapIdentity, isLoopbackPgUrl, etc.).
  ensureDefaultAgent now looks up the first personal-org dynamically
  instead of targeting a hardcoded BOOTSTRAP_ORG_ID, and is a no-op when
  no user exists yet.
- auth/routes.ts /api/local-init: replaced "seed bootstrap, refuse if real
  users exist" with "find the single real user, mint for them." Zero users
  → 404 no_user_yet pointing at /sign-up. >1 users → 404 not_single_user.
  The `id <> 'bootstrap-user'` filter ignores legacy rows from prior
  installs so existing users don't get dead-ended.
- auth/index.tsx databaseHooks.user.create.before: same legacy filter
  on the LOBU_SINGLE_USER count so a legacy install with bootstrap-user
  still allows the first real signup.
- auth/index.tsx databaseHooks.user.create.after: runs ensureDefaultAgent
  immediately on first signup. Without this the first user lands without
  a default agent until the next `lobu run` boot.
- index.ts /api/auth/* middleware: dropped the path-based sign-up guard
  that blocked the *first* /sign-up when LOBU_SINGLE_USER=1. The DB
  before-hook is the single chokepoint and is more accurate (covers
  magic-link verify + OAuth callbacks too).

Verified:
- `make typecheck` clean.
- `bun test packages/server/src/__tests__/unit` — 201 pass, 0 fail.
- Codex reviewed (twice). Caught: path-guard-blocked-first-signup,
  legacy-bootstrap-counted-against-hook, no-default-agent-after-signup.
  All three fixed in this commit.

Known limitation (not fixed): two concurrent first-sign-ups can both
observe count=0 and both insert, leaving the install with 2 users and
/api/local-init permanently returning not_single_user. Not a practical
risk on a local loopback install; clean fix is a DB-level singleton
constraint, deferred.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 22e07078-c30d-4bff-b0a4-0715fd1c0024

📥 Commits

Reviewing files that changed from the base of the PR and between 1ef5619 and 9f15c06.

📒 Files selected for processing (4)
  • packages/server/src/auth/index.tsx
  • packages/server/src/auth/routes.ts
  • packages/server/src/index.ts
  • packages/server/src/start-local.ts

📝 Walkthrough

Walkthrough

The pull request removes the pre-seeded bootstrap-user initialization model from single-user mode, replacing it with direct discovery of the real user. Single-user enforcement now excludes bootstrap records from the count, the local-init endpoint discovers and returns the actual user's credentials instead of hardcoded bootstrap values, and default-agent provisioning is moved to signup time and local boot; bootstrap seeding code is removed.

Changes

Bootstrap-user removal and single-user discovery

Layer / File(s) Summary
Single-user mode enforcement (exclude bootstrap)
packages/server/src/auth/index.tsx
The user.create.before hook now excludes bootstrap-user from the existing user count, changing when single-user-mode signup blocks are triggered.
Auth route enforcement consolidation
packages/server/src/index.ts
The /api/auth/* handler removes the upstream single-user fast-fail for POST requests and relies solely on the downstream user.create.before database hook enforcement.
Local-init endpoint rework (discover real user)
packages/server/src/auth/routes.ts
The /api/local-init endpoint is reworked to query and discover the single real user (excluding bootstrap-user), look up the user's personal org via metadata, mint a session for the resolved user, create a worker-scoped PAT, and return the discovered user/org details instead of hardcoded bootstrap values.
Default agent provisioning on signup
packages/server/src/auth/index.tsx
After personal organization provisioning in the user.create.after hook, signup now performs best-effort default-agent provisioning via dynamic import; agent provisioning errors are logged and do not block signup.
Local development boot cleanup
packages/server/src/start-local.ts
The local startup removes bootstrap-user auto-seeding and replaces it with on-demand default-agent provisioning for an existing personal org; bootstrap identity helpers, constants, and seeding functions are deleted, and imports are cleaned up.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • lobu-ai/lobu#898: Main PR refactors the LOBU_SINGLE_USER single-user enforcement by adjusting the user.create.before "existing user count" logic to ignore bootstrap-user, and removes the /api/auth/* path-based fast-fail that depended on bootstrap seeding.
  • lobu-ai/lobu#830: Both PRs modify the /api/local-init flow in packages/server/src/auth/routes.ts, with this PR reworking bootstrap-based credential minting to discover the real single user instead.
  • lobu-ai/lobu#779: Both PRs modify server bootstrap-user/org provisioning in packages/server/src/start-local.ts and related auth enforcement logic.

Poem

🐰 The bootstrap bunny hops away,
Real users found, no seeds to play,
Discovery at init-time,
Agents provisioned—now sublime,
Single-user, clean and true! 🌟

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/chrome-extension-e2e

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Comment @coderabbitai help to get the list of available commands and usage tips.

@buremba buremba merged commit f6522b3 into main May 19, 2026
19 of 20 checks passed
@buremba buremba deleted the feat/chrome-extension-e2e branch May 19, 2026 01:49
@codecov-commenter
Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

buremba added a commit that referenced this pull request May 19, 2026
…ing (#905)

* feat(server): passkey (WebAuthn) auth + auth-config flags for local-mode routing

Two additions on top of the local-first refactor (#902):

1. @better-auth/passkey plugin enabled in auth/index.tsx
-------------------------------------------------------
The operator signs up once with email+password, then can enroll a
passkey (Touch ID / Face ID / hardware key) for biometric sign-in.
The plugin's default `requireSession: true` for registration is kept —
this PR adds the sign-in side; post-signup enrollment UI is a small
follow-up.

Codex review caught two subtle bugs:
- `origin: resolveBaseUrl({ request })` would freeze the WebAuthn
  origin onto the first request that constructed the cached BA
  instance — request from localhost could poison passkey for a
  subsequent Tailscale request. Now `origin: null` (plugin reads the
  request Origin header at verification time).
- `rpID: new URL(resolveBaseUrl({ request })).hostname` had the same
  freezing problem. Now derived from `PUBLIC_WEB_URL` env (stable per
  deployment), defaults to "localhost" for local-mode.

Migration db/migrations/20260519000000_passkey_table.sql creates the
`passkey` table with the schema @better-auth/passkey's adapter expects:
publicKey, credentialID, counter, deviceType, backedUp, transports,
aaguid. FK userId → user(id) ON DELETE CASCADE.

2. /api/auth-config extended with passkey + singleUserMode + hasUser
--------------------------------------------------------------------
SPA currently reads auth-config to gate visible providers (magic-link,
social, email-password). After #902 it also needs to know:

- `passkey: true` — plugin is always wired, so the SPA can render the
  Sign-in-with-passkey button unconditionally.
- `singleUserMode: env.LOBU_SINGLE_USER === '1'` — drives the SPA's
  copy + routing for local-first installs.
- `hasUser`: SELECT EXISTS(SELECT 1 FROM "user" WHERE id <> 'bootstrap-user')
  — when singleUserMode + !hasUser, SPA forces /sign-up; when +hasUser,
  forces /sign-in. Filters out the legacy bootstrap-user row so an
  install that still has it pre-#902 isn't mis-routed.

Submodule bump
--------------
packages/owletto → feat/passkey-client (lobu-ai/owletto#8c6c283):
- authClient wires passkeyClient().
- login.tsx renders the Sign-in-with-passkey button + checks result.error
  (third codex finding: BA client returns failure as result.error, not
  via throw).
- login.tsx auto-toggles authIntent based on singleUserMode + hasUser.

Verified:
- `make typecheck` clean.
- `bun test packages/server/src/__tests__/unit` — 201 pass, 0 fail.
- Codex reviewed; 3 findings (origin freeze, rpID freeze, result.error
  not inspected) all fixed before push.

Not in this PR (deferred):
- Post-signup passkey enrollment prompt — operator needs to sign up
  with password first, then go to /settings to add a passkey. A
  one-click "enrol now?" after first sign-up is a follow-up.
- Passkey-only signup (no password at all) — requires the plugin's
  custom `resolveUser` callback + a user-create hook. Bigger lift.

* fix: passkey schema snapshot + cli runtime dep

CI catches:
1. db/schema.sql must match `dbmate up` output (Schema snapshot drift
   check). My branch added a migration that creates the `passkey` table
   but forgot to regenerate db/schema.sql.
2. CLI declares the runtime deps the embedded server bundle pulls at
   start (dev.test.ts 'CLI package declares runtime deps'). Adding
   @better-auth/passkey to packages/server/* needs the matching entry
   in packages/cli/package.json so `lobu run` can resolve it.

Hand-edited db/schema.sql to insert only the four passkey blocks
(CREATE TABLE, PK, two INDEXes, FK) at the alphabetically correct
positions. Avoided a full `make db-schema` regen because my local
Postgres has postgis enabled (not in CI's pg16 image) so a full regen
would also add postgis extension + geo_lookup function rows that
shouldn't be in the snapshot.

* fix(schema): place passkey blocks alphabetically to match dbmate output

Previous patch put the passkey table after personal_access_tokens, but
'passkey' < 'pending_interactions' < 'personal_access_tokens'
alphabetically — dbmate's pg_dump emits them in alpha order, so CI's
schema-drift check flagged the mis-positioning. Re-anchored each block:

- TABLE: before pending_interactions
- PK CONSTRAINT: before pending_interactions_pkey
- INDEXes: before personal_access_tokens_active_idx (after oauth_tokens_*)
- FK CONSTRAINT: before pending_interactions_organization_id_fkey

* chore(submodule): bump owletto to d5ae2a4 (passkey-client merged)

* fix(schema): rebuild passkey delta against fresh origin/main schema

Previous attempts left duplicate passkey blocks: re-patching after
git checkout but before merge layered the new blocks on top of the
old wrong-position ones. Reset db/schema.sql to origin/main's clean
state, then spliced the 4 passkey blocks once at the correct positions
(alphabetical: passkey < pending_interactions < personal_access_tokens)
+ inserted the 20260519000000 row in INSERT INTO schema_migrations.
buremba added a commit that referenced this pull request May 19, 2026
- ensureInstallOperator now converges on every boot: if first-boot
  personal-org provisioning failed, later boots patch it instead of
  short-circuiting on the existing user row (pi finding #1).
- Preserve the legacy bootstrap-user (pre-PR #902) carve-out in every
  human-count predicate so upgraded installs don't treat it as a real
  human (pi finding #2).
- /api/local-init now fails closed when peerRemoteAddress is missing,
  guarded by LOBU_LOCAL_INIT_ALLOW_MISSING_PEER=1 for tests only (pi
  finding #3).
- Docs realigned to implementation: member-list / admin user-list
  carve-outs explicitly NOT in this PR (intentional, scoped via orgs);
  SPA password-manager mitigations deferred to a follow-up owletto PR;
  CLI flow corrected to use /api/local-init (which mints the worker
  PAT, unlike /api/auth/sign-in/email); hashPassword sourced from
  better-auth/crypto (the actual import) (pi findings #4, #5, #6).
buremba added a commit that referenced this pull request May 19, 2026
)

* feat(auth): install_operator bootstrap — unblock headless installs

Fresh lobu run boots with empty user table; CLI calls /api/local-init;
server says no_user_yet → no SPA available in /tmp / CI / containers.
ensureInstallOperator() auto-provisions a synthetic install_operator
user at first boot whose password is the install's ENCRYPTION_KEY.
A new principal_kind discriminator on the user table keeps the
operator out of human-discovery surfaces (signup count, password
reset, magic link, OAuth account-linking).

Supersedes #917 (closed): that PR went through 5+ design revisions
and accumulated machinery (pairing URL file, single_use PAT, POST
/auth/pair-token, /auth/enrol-credential SPA page, custom OTP table)
that codex review revealed was redundant with better-auth +
browser-native WebAuthn cross-device verification + existing
/api/local-init.

See docs/install-operator-bootstrap.md for the full design.

* docs(a1): explicit Chrome extension via Mac bridge + cross-platform fallback

* fix(auth): address pi review on #923

- ensureInstallOperator now converges on every boot: if first-boot
  personal-org provisioning failed, later boots patch it instead of
  short-circuiting on the existing user row (pi finding #1).
- Preserve the legacy bootstrap-user (pre-PR #902) carve-out in every
  human-count predicate so upgraded installs don't treat it as a real
  human (pi finding #2).
- /api/local-init now fails closed when peerRemoteAddress is missing,
  guarded by LOBU_LOCAL_INIT_ALLOW_MISSING_PEER=1 for tests only (pi
  finding #3).
- Docs realigned to implementation: member-list / admin user-list
  carve-outs explicitly NOT in this PR (intentional, scoped via orgs);
  SPA password-manager mitigations deferred to a follow-up owletto PR;
  CLI flow corrected to use /api/local-init (which mints the worker
  PAT, unlike /api/auth/sign-in/email); hashPassword sourced from
  better-auth/crypto (the actual import) (pi findings #4, #5, #6).

* refactor(auth): trim ceremony in install-operator.ts after audit

Audit found that `HUMAN_KIND`, `NOT_INSTALL_OPERATOR_PREDICATE`, and
`isInstallOperator` had zero production consumers — they were imported
only by the unit test that pinned them against themselves. The carve-out
SQL in `auth/index.tsx`, `auth/config.ts`, and `auth/routes.ts` all
hand-roll `principal_kind <> 'install_operator'` / `=== "install_operator"`
literals; the predicate constant never became a single source of truth.

`installOperatorEmail` was also only exported for the unit test; it's a
2-line helper used in one file, so de-export and shrink.

Drop the redundant `encryptionKey.length === 0` clause (empty string is
already falsy) and inline the `operatorEmail` temporary.

Delete the unit test file outright — every remaining subject is either a
plain string constant or the integration-tested `ensureInstallOperator`.
Coverage for the real behavior lives in the existing integration test
(`__tests__/integration/auth/install-operator.test.ts`), which still
passes.

Net: install-operator.ts 159 → 127 LOC; unit test 64 LOC removed; no
behavior change.

* fix(install_operator): validate ENCRYPTION_KEY shape at bootstrap

Before this fix, `ensureInstallOperator()` only checked that
`ENCRYPTION_KEY` was set, then handed it straight to `hashPassword()` —
which accepts any string. A 24-byte base64 or other non-canonical value
bootstrapped the operator fine, but the at-rest encryption path
(provider keys, secrets) requires a canonical 32-byte base64/hex key and
500s with "ENCRYPTION_KEY must be a canonical base64 or hex encoded
32-byte key" on every save. Net result: user could sign in but couldn't
persist any encrypted secret.

Now the install refuses to start with the same canonical error message
the runtime would emit, so the operator either signs in AND can save
secrets, or fails fast with an actionable hint
(`openssl rand -hex 32` / `openssl rand -base64 32`).

- `packages/core/src/utils/encryption.ts`: extract `decodeEncryptionKey`
  (pure, no side effects) and add `assertEncryptionKey` +
  `ENCRYPTION_KEY_FORMAT_ERROR` so upstream validators can reuse the
  exact same shape check the runtime uses.
- `packages/server/src/auth/install-operator.ts`: call
  `assertEncryptionKey` before `hashPassword`.
- Integration test: switch the existing fixture to a canonical hex key
  and add a new case that asserts a malformed key is rejected and no
  user row is written.

* fix(ci): add principal_kind to QUERYABLE_SCHEMA + reset owletto submodule

- table-schema: register the new user.principal_kind column added by the
  install-operator migration so the drift-detection test stops failing.
- owletto: reset the submodule pointer to the SHA that lobu/main carries
  (4f7c757), since this branch's pin (f611c1d) sits behind a real
  non-bot commit on owletto/main, tripping the Submodule Drift check.
buremba added a commit that referenced this pull request May 28, 2026
…egacy enc:v1: decoder

- Drop `id <> 'bootstrap-user'` filters from auth/config.ts hasUser, auth/routes.ts /local-init, and auth/index.tsx single-user signup guard. The pre-PR #902 bootstrap-user row no longer exists on any supported install; install_operator is the only synthetic row to exclude.
- Drop the corresponding "does not count bootstrap-user" case from single-user-signup.test.ts; updated install_operator-only assertion remains.
- Delete `decryptLegacyEncryptedConfig` + `ENC_PREFIX` from lobu/stores/postgres-stores.ts and the now-unused `decrypt` import. Prod connections check: `WHERE config::text LIKE '%enc:v1:%'` -> 0 rows.
- Drop the `health: { checkIntervalMs, staleThresholdMs, protectActiveWorkers }` block from gateway/config: SOCKET_HEALTH_CHECK_INTERVAL_MS / SOCKET_STALE_THRESHOLD_MS / SOCKET_PROTECT_ACTIVE_WORKERS had zero readers. Also drop the matching `.env.example` blocks (SOCKET_* + Slack manifest-sync vars SLACK_CONFIG_TOKEN/SLACK_CONFIG_REFRESH_TOKEN/SLACK_APP_ID — unreferenced anywhere in packages/).
- Drop dead re-exports from gateway/guardrails/index.ts (`BUILTIN_GUARDRAIL_FACTORIES`, `_resetSharedJudgeForTests`, `_setSharedJudgeForTests` — no external consumers); privatize `secretScanGuardrail` / `forbiddenToolsGuardrail` in builtins.ts (used only via the in-file factory map / registerBuiltinGuardrails).
- Delete broken settings-auth.test.ts (bun:test file under a vitest mount that was already failing on stale expectations and would be removed by knip).
- Remove unused `commander` and `yaml` deps from packages/server/package.json (zero source refs); regenerate bun.lock.
- Strip "Phase 5"/"pre-PR #902" historical-context tags from comments in embedded-deployment.ts.
buremba added a commit that referenced this pull request May 28, 2026
…egacy enc:v1: decoder

- Drop `id <> 'bootstrap-user'` filters from auth/config.ts hasUser, auth/routes.ts /local-init, and auth/index.tsx single-user signup guard. The pre-PR #902 bootstrap-user row no longer exists on any supported install; install_operator is the only synthetic row to exclude.
- Drop the corresponding "does not count bootstrap-user" case from single-user-signup.test.ts; updated install_operator-only assertion remains.
- Delete `decryptLegacyEncryptedConfig` + `ENC_PREFIX` from lobu/stores/postgres-stores.ts and the now-unused `decrypt` import. Prod connections check: `WHERE config::text LIKE '%enc:v1:%'` -> 0 rows.
- Drop the `health: { checkIntervalMs, staleThresholdMs, protectActiveWorkers }` block from gateway/config: SOCKET_HEALTH_CHECK_INTERVAL_MS / SOCKET_STALE_THRESHOLD_MS / SOCKET_PROTECT_ACTIVE_WORKERS had zero readers. Also drop the matching `.env.example` blocks (SOCKET_* + Slack manifest-sync vars SLACK_CONFIG_TOKEN/SLACK_CONFIG_REFRESH_TOKEN/SLACK_APP_ID — unreferenced anywhere in packages/).
- Drop dead re-exports from gateway/guardrails/index.ts (`BUILTIN_GUARDRAIL_FACTORIES`, `_resetSharedJudgeForTests`, `_setSharedJudgeForTests` — no external consumers); privatize `secretScanGuardrail` / `forbiddenToolsGuardrail` in builtins.ts (used only via the in-file factory map / registerBuiltinGuardrails).
- Delete broken settings-auth.test.ts (bun:test file under a vitest mount that was already failing on stale expectations and would be removed by knip).
- Remove unused `commander` and `yaml` deps from packages/server/package.json (zero source refs); regenerate bun.lock.
- Strip "Phase 5"/"pre-PR #902" historical-context tags from comments in embedded-deployment.ts.
buremba added a commit that referenced this pull request May 28, 2026
…egacy enc:v1: decoder (#1120)

* chore(server): drop pre-#902 bootstrap-user filters, dead env vars, legacy enc:v1: decoder

- Drop `id <> 'bootstrap-user'` filters from auth/config.ts hasUser, auth/routes.ts /local-init, and auth/index.tsx single-user signup guard. The pre-PR #902 bootstrap-user row no longer exists on any supported install; install_operator is the only synthetic row to exclude.
- Drop the corresponding "does not count bootstrap-user" case from single-user-signup.test.ts; updated install_operator-only assertion remains.
- Delete `decryptLegacyEncryptedConfig` + `ENC_PREFIX` from lobu/stores/postgres-stores.ts and the now-unused `decrypt` import. Prod connections check: `WHERE config::text LIKE '%enc:v1:%'` -> 0 rows.
- Drop the `health: { checkIntervalMs, staleThresholdMs, protectActiveWorkers }` block from gateway/config: SOCKET_HEALTH_CHECK_INTERVAL_MS / SOCKET_STALE_THRESHOLD_MS / SOCKET_PROTECT_ACTIVE_WORKERS had zero readers. Also drop the matching `.env.example` blocks (SOCKET_* + Slack manifest-sync vars SLACK_CONFIG_TOKEN/SLACK_CONFIG_REFRESH_TOKEN/SLACK_APP_ID — unreferenced anywhere in packages/).
- Drop dead re-exports from gateway/guardrails/index.ts (`BUILTIN_GUARDRAIL_FACTORIES`, `_resetSharedJudgeForTests`, `_setSharedJudgeForTests` — no external consumers); privatize `secretScanGuardrail` / `forbiddenToolsGuardrail` in builtins.ts (used only via the in-file factory map / registerBuiltinGuardrails).
- Delete broken settings-auth.test.ts (bun:test file under a vitest mount that was already failing on stale expectations and would be removed by knip).
- Remove unused `commander` and `yaml` deps from packages/server/package.json (zero source refs); regenerate bun.lock.
- Strip "Phase 5"/"pre-PR #902" historical-context tags from comments in embedded-deployment.ts.

* docs: scrub remaining bootstrap-user references from install-operator doc + /local-init comment

Pi review nits from PR #1120: the install-operator-bootstrap.md predicate examples and the /local-init JSDoc still referenced the dropped `id <> 'bootstrap-user'` filter. Align with the simpler `principal_kind <> 'install_operator'` predicate that the code now uses.

* test(server): restore settings-auth coverage for verifySettingsToken/Session

The cleanup commit deleted settings-auth.test.ts as 'broken/stale', but it
was the only coverage for verifySettingsToken, verifySettingsSession and
setSettingsSessionCookie — all still live across ~10 gateway route files.

Restore it under src/gateway/__tests__/ (the dir the integration job's
'bun test src/gateway/__tests__' actually runs, alongside the other gateway
security bun:test files) and fix the genuinely-stale expectations:
- both verify* functions are now async + consult a RevokedTokenStore: await
  them and inject a fake store via setRevokedTokenStore (no Postgres needed).
- the core encryption key is memoized per-process, so the wrong-key rotation
  test now resets it with __resetEncryptionKeyCacheForTests around each switch.
- add revoked-jti kill-switch coverage (token + cookie paths) and a jti-mint
  assertion for setSettingsSessionCookie.

Keeps every security assertion: expiry rejection, exp=0 guard, tamper/wrong-key
rejection, httpOnly/SameSite=Lax/Secure cookie flags. 30 tests pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants