Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -121,17 +121,3 @@ EMAIL_REPLY_TO=emre@lobu.ai
# List-Unsubscribe value. Adds one-click unsubscribe headers (helps keep mail out of Promotions).
EMAIL_UNSUBSCRIBE=mailto:emre@lobu.ai

# Socket Mode Health Monitoring
SOCKET_HEALTH_CHECK_INTERVAL_MS=300000
SOCKET_STALE_THRESHOLD_MS=900000
SOCKET_PROTECT_ACTIVE_WORKERS=true

# ===========================================
# SLACK MANIFEST SYNC (Optional)
# ===========================================
# Used by: bun run slack:manifest:validate / update
# Get config tokens from Slack API dashboard → "Your App Configuration Tokens"

SLACK_CONFIG_TOKEN=xoxe.xoxp-your-config-access-token-here
SLACK_CONFIG_REFRESH_TOKEN=xoxe-your-config-refresh-token-here
SLACK_APP_ID=A1234567890
22 changes: 10 additions & 12 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 7 additions & 12 deletions docs/install-operator-bootstrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ matches what's already in operator muscle memory.
`principal_kind` is a new `text NOT NULL DEFAULT 'human'` column on
`user`. The discriminator lets surfaces that gate on "is there a real
human?" exclude the install operator with a single predicate:
`WHERE principal_kind <> 'install_operator' AND id <> 'bootstrap-user'`
(the trailing `id <>` clause keeps the pre-PR-#902 legacy bootstrap row
from being mistaken for a human on upgraded installs).
`WHERE principal_kind <> 'install_operator'`.

Carve-outs land at: signup count (`databaseHooks.user.create.before`),
`getAuthConfig().hasUser`, password reset (`sendResetPassword`), magic
Expand Down Expand Up @@ -161,10 +159,9 @@ Trade-offs:

Carve-outs (one predicate, applied at each surface):

- `databaseHooks.user.create.before` — install_operator (and the legacy
`bootstrap-user` row, if present) excluded from the "deployment already
has a user" count, so the first human signup can still proceed in
single-user mode.
- `databaseHooks.user.create.before` — install_operator excluded from
the "deployment already has a user" count, so the first human signup
can still proceed in single-user mode.
- `getAuthConfig().hasUser` — same predicate, so the SPA gateway knows
"the install has a *human*" not "the install has the operator row".
- `sendResetPassword` / `sendMagicLink` — reject when the target user has
Expand All @@ -174,9 +171,8 @@ Carve-outs (one predicate, applied at each surface):
any non-`credential` provider attempting to write an account row for
the install operator. `credential` is allowed so `ensureInstallOperator`
can write the password-hash row at boot.
- `/api/local-init` user-selection — orders the operator last and
excludes `bootstrap-user` entirely, so the route mints credentials
for a real human when one exists.
- `/api/local-init` user-selection — orders the operator last, so the
route mints credentials for a real human when one exists.

Not carved out in this PR (intentional — see "Design" section):
member listing / org member UI / admin user lists. The install operator
Expand All @@ -190,8 +186,7 @@ On next boot, every existing install auto-provisions its install_operator.
Existing human users with normal email + password accounts keep working —
their auth is independent of the operator row. No user-visible disruption.
The `principal_kind` column defaults to `'human'` for every pre-existing
row, so existing predicates that used to filter `WHERE id <> 'bootstrap-user'`
(legacy, pre-#902) can be replaced with the cleaner
row, so the "is there a real human?" gate is the single predicate
`WHERE principal_kind <> 'install_operator'`.

## Stage 2 implementation files
Expand Down
2 changes: 0 additions & 2 deletions packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
"@better-auth/passkey": "^1.6.9",
"better-auth": "^1.4.10",
"chat": "^4.26.0",
"commander": "^14.0.1",
"cron-parser": "^5.5.0",
"handlebars": "^4.7.9",
"hono": "^4.10.4",
Expand All @@ -62,7 +61,6 @@
"postgres": "^3.4.7",
"react": "^19.2.5",
"resend": "^6.6.0",
"yaml": "^2.7.1",
"zod": "^4.4.0"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
* Pins two contracts:
*
* 1. The guard counts real humans correctly — the synthetic
* install_operator row and the legacy bootstrap-user row are
* excluded, so the FIRST human signup proceeds and the SECOND is
* refused with SIGN_UP_DISABLED_IN_SINGLE_USER_MODE.
* install_operator row is excluded, so the FIRST human signup
* proceeds and the SECOND is refused with
* SIGN_UP_DISABLED_IN_SINGLE_USER_MODE.
*
* 2. The guard does not deadlock. Sign-up runs inside Better Auth's
* runWithTransaction, which reserves a pooled connection. The hook must
Expand Down Expand Up @@ -142,11 +142,10 @@ describe("single-user-mode sign-up guard", () => {
expect(res.body.code).toBe("SIGN_UP_DISABLED_IN_SINGLE_USER_MODE");
});

it("does not count install_operator or bootstrap-user as the existing human", async () => {
it("does not count install_operator as the existing human", async () => {
await seedUser("user_install_seed", "install_operator");
await seedUser("bootstrap-user", "human");

// Neither seeded row is a real human, so the first human signup
// The seeded row is not a real human, so the first human signup
// must still be admitted.
const first = await signUp({
email: "first@local.test",
Expand Down
8 changes: 3 additions & 5 deletions packages/server/src/auth/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,18 +373,16 @@ export async function getAuthConfig(
const passkey = true;
const singleUserMode = env.LOBU_SINGLE_USER === '1';
// Filter out the synthetic install_operator row (auto-provisioned at
// boot in ensureInstallOperator) AND the legacy bootstrap-user row
// (pre-PR #902) — neither counts as "the install has a *human*".
// Real users include anyone signed up via the web UI. See
// docs/install-operator-bootstrap.md.
// boot in ensureInstallOperator) — it doesn't count as "the install
// has a *human*". Real users include anyone signed up via the web UI.
// See docs/install-operator-bootstrap.md.
let hasUser = false;
try {
const sql = getDb();
const rows = (await sql`
SELECT EXISTS(
SELECT 1 FROM "user"
WHERE principal_kind <> 'install_operator'
AND id <> 'bootstrap-user'
) AS has_user
`) as unknown as Array<{ has_user: boolean }>;
hasUser = !!rows[0]?.has_user;
Expand Down
8 changes: 3 additions & 5 deletions packages/server/src/auth/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -653,18 +653,16 @@ export async function createAuth(env: Env, request?: Request) {
// which is the fail-closed posture we want.
if (env.LOBU_SINGLE_USER === "1") {
// Exclude the synthetic install_operator row
// (auto-provisioned by ensureInstallOperator) AND the
// legacy bootstrap-user row (pre-PR #902) so the
// first human signup still proceeds on upgraded
// installs. See docs/install-operator-bootstrap.md.
// (auto-provisioned by ensureInstallOperator) so the
// first human signup still proceeds. See
// docs/install-operator-bootstrap.md.
const existing =
await ctx!.context.internalAdapter.countTotalUsers([
{
field: "principalKind",
operator: "ne",
value: "install_operator",
},
{ field: "id", operator: "ne", value: "bootstrap-user" },
]);
if (existing > 0) {
throw new APIError("FORBIDDEN", {
Expand Down
8 changes: 2 additions & 6 deletions packages/server/src/auth/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -507,8 +507,7 @@ credentialRoutes.get('/extension-bootstrap', (c) => {
* Tailscale Funnel / ngrok / cloudflared / nginx proxy fronting a
* loopback bind sets these — the bind looks local but the *exposure*
* isn't, so a public client could otherwise reach this endpoint.
* - Refuses when the deployment has more than one user (legacy bootstrap
* row counts as zero — see the `id <> 'bootstrap-user'` filter below).
* - Refuses when the deployment has more than one user.
* - Refuses when the single user has no personal org (shouldn't happen —
* databaseHooks.user.create.after provisions one).
*
Expand All @@ -534,14 +533,11 @@ credentialRoutes.post('/local-init', async (c) => {
// row (auto-provisioned at boot in ensureInstallOperator). Ordering by
// principal_kind keeps 'install_operator' last so a human comes first
// when both exist; on a fresh install before signup, only the operator
// row exists and it gets minted credentials. The legacy bootstrap-user
// (pre-PR #902) is excluded entirely — upgraded installs that still
// carry it should still see the install_operator / human flow. See
// row exists and it gets minted credentials. See
// docs/install-operator-bootstrap.md.
const userRows = (await sql`
SELECT id, email, name, principal_kind
FROM "user"
WHERE id <> 'bootstrap-user'
ORDER BY
CASE WHEN principal_kind = 'install_operator' THEN 1 ELSE 0 END ASC,
"createdAt" ASC
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,6 @@ function createGatewayConfig(
},
auth: {},
lobuMemory: {},
health: {
checkIntervalMs: 1000,
staleThresholdMs: 2000,
protectActiveWorkers: true,
},
secrets: {
aws: {},
},
Expand Down
Loading
Loading