engine.ts231 lines · main
1/**
2 * briven-engine — Briven Auth vault (DOLTGRES ONLY).
3 *
4 * HARD RULE: the complete Briven project is Doltgres. New parts do not get
5 * stock Postgres. There is NO SuperTokens Core process and NO separate
6 * Postgres for Auth.
7 *
8 * briven-engine = this API package + tables in Doltgres database `briven_engine`.
9 * Recipe *behavior* still follows the SuperTokens feature checklist as docs,
10 * but all state is native Doltgres.
11 *
12 * Platform operator sign-in (routes/auth.ts Better Auth on briven_control)
13 * is separate and also on Doltgres.
14 *
15 * DEPLOY GATE: local build only until complete product + flndrn OK.
16 */
17
18import { log } from '../../lib/logger.js';
19import { env } from '../../env.js';
20import { ensureBrivenEngineDatabase } from './ensure-db.js';
21import { bootstrapBrivenEngineSchema } from './schema.js';
22import { getEnginePool, isEnginePoolReady } from './db.js';
23
24export const BRIVEN_ENGINE_ID = 'briven-engine' as const;
25/** Product engine version shown on yellow Auth shell (Option B Phase 1). */
26export const BRIVEN_ENGINE_VERSION = '0.8.0-enterprise-sso' as const;
27
28export type AuthCoreStatus = {
29 enabled: boolean;
30 engine: typeof BRIVEN_ENGINE_ID;
31 engineVersion: typeof BRIVEN_ENGINE_VERSION;
32 storage: 'doltgres';
33 database: 'briven_engine';
34 ok: boolean;
35 schemaReady: boolean;
36 poolReady: boolean;
37 phase: number;
38 appLoginReady: boolean;
39 loginMethods: string[];
40 message: string;
41 deployGate: 'enterprise-sso';
42 recipeNames: string[];
43 sdkInitialized: boolean;
44 recipePhase: number | null;
45 connectionUri: string;
46 hello: string | null;
47 apiVersion: string | null;
48};
49
50let schemaReady = false;
51let bootstrapped = false;
52
53const LOADED_RECIPES = [
54 'session',
55 'emailpassword',
56 'passwordless',
57 'thirdparty',
58 'webauthn',
59 'multifactorauth',
60 'usermetadata',
61 'userroles',
62 'sso-saml',
63 'sso-oidc',
64] as const;
65
66/**
67 * Probe + ensure Doltgres Auth vault is ready.
68 */
69export async function probeBrivenEngine(): Promise<AuthCoreStatus> {
70 const base = {
71 engine: BRIVEN_ENGINE_ID,
72 engineVersion: BRIVEN_ENGINE_VERSION,
73 storage: 'doltgres' as const,
74 database: 'briven_engine' as const,
75 appLoginReady: false,
76 loginMethods: [] as string[],
77 deployGate: 'enterprise-sso' as const,
78 recipeNames: [...LOADED_RECIPES],
79 sdkInitialized: bootstrapped && schemaReady,
80 recipePhase: bootstrapped ? 8 : null,
81 // Redact credentials — this object is returned on public /info.
82 connectionUri: redactConnectionUri(env.BRIVEN_ENGINE_DATABASE_URL),
83 hello: null as string | null,
84 apiVersion: null as string | null,
85 };
86
87 if (!env.BRIVEN_AUTH_CORE_ENABLED) {
88 return {
89 ...base,
90 enabled: false,
91 ok: false,
92 schemaReady: false,
93 poolReady: false,
94 phase: 2,
95 message: 'BRIVEN_AUTH_CORE_ENABLED is false',
96 };
97 }
98
99 try {
100 if (!isEnginePoolReady()) {
101 return {
102 ...base,
103 enabled: true,
104 ok: false,
105 schemaReady,
106 poolReady: false,
107 phase: 1,
108 message: 'Doltgres engine pool not ready — call initAuthCoreSdk at boot',
109 };
110 }
111
112 const pool = getEnginePool();
113 const r = await pool.query('SELECT 1 AS ok');
114 const ok = r.rows[0]?.ok === 1 || r.rows[0]?.ok === '1';
115 const ready = ok && schemaReady && bootstrapped;
116 return {
117 ...base,
118 enabled: true,
119 ok: ready,
120 schemaReady,
121 poolReady: true,
122 phase: 8,
123 appLoginReady: ready,
124 loginMethods: ready ? listPhase5LoginMethods() : [],
125 hello: ok ? 'Hello' : null,
126 message: ready
127 ? 'briven-engine login + dashboard + enterprise SAML/OIDC SSO'
128 : schemaReady
129 ? 'Doltgres reachable; engine not fully bootstrapped'
130 : 'Doltgres reachable; schema not bootstrapped',
131 };
132 } catch (err) {
133 const message = err instanceof Error ? err.message : String(err);
134 log.warn('briven_engine_probe_failed', { message });
135 return {
136 ...base,
137 enabled: true,
138 ok: false,
139 schemaReady,
140 poolReady: false,
141 phase: 1,
142 message: `Doltgres unreachable: ${message}`,
143 };
144 }
145}
146
147function listPhase5LoginMethods(): string[] {
148 const methods = [
149 'emailpassword',
150 'passwordless-email',
151 'passwordless-sms',
152 'magic-link',
153 'totp-mfa',
154 'passkeys',
155 'sso-saml',
156 'sso-oidc',
157 ];
158 if (process.env.BRIVEN_GOOGLE_CLIENT_ID && process.env.BRIVEN_GOOGLE_CLIENT_SECRET) {
159 methods.push('google');
160 }
161 if (process.env.BRIVEN_GITHUB_CLIENT_ID && process.env.BRIVEN_GITHUB_CLIENT_SECRET) {
162 methods.push('github');
163 }
164 return methods;
165}
166
167function redactConnectionUri(raw: string | undefined): string {
168 if (!raw) return '(unset)';
169 try {
170 const u = new URL(raw);
171 if (u.password) u.password = '***';
172 return u.toString();
173 } catch {
174 return '(set)';
175 }
176}
177
178/** @deprecated name kept for old imports */
179export const probeSuperTokensCore = probeBrivenEngine;
180
181/**
182 * Boot briven-engine: ensure Doltgres DB + schema. No SuperTokens Core.
183 */
184export async function initAuthCoreSdk(): Promise<boolean> {
185 if (bootstrapped && schemaReady) return true;
186 if (!env.BRIVEN_AUTH_CORE_ENABLED) return false;
187
188 try {
189 const ensured = await ensureBrivenEngineDatabase();
190 if (!ensured.ok) {
191 log.error('briven_engine_init_db_failed', ensured);
192 return false;
193 }
194
195 const { openEnginePool } = await import('./db.js');
196 openEnginePool();
197 await bootstrapBrivenEngineSchema();
198 schemaReady = true;
199 bootstrapped = true;
200
201 log.info('briven_engine_initialized', {
202 engine: BRIVEN_ENGINE_ID,
203 engineVersion: BRIVEN_ENGINE_VERSION,
204 storage: 'doltgres',
205 database: 'briven_engine',
206 recipes: LOADED_RECIPES,
207 deployGate: 'enterprise-sso',
208 appLoginReady: true,
209 loginMethods: listPhase5LoginMethods(),
210 });
211 return true;
212 } catch (err) {
213 const message = err instanceof Error ? err.message : String(err);
214 log.error('briven_engine_init_failed', { message });
215 return false;
216 }
217}
218
219export function isAuthCoreInitialized(): boolean {
220 return bootstrapped && schemaReady;
221}
222
223export function getAuthCoreRecipeMeta(): {
224 phase: number | null;
225 names: string[];
226} {
227 return {
228 phase: bootstrapped ? 3 : null,
229 names: bootstrapped ? [...LOADED_RECIPES] : [],
230 };
231}