@riffdb/better-auth 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/DESIGN.md +273 -0
  2. package/LICENSE-APACHE +201 -0
  3. package/LICENSE-MIT +27 -0
  4. package/README.md +323 -2
  5. package/dist/adapter.d.ts +34 -0
  6. package/dist/adapter.d.ts.map +1 -0
  7. package/dist/adapter.js +1600 -0
  8. package/dist/adapter.js.map +1 -0
  9. package/dist/generated/client.d.ts +2166 -0
  10. package/dist/generated/client.d.ts.map +1 -0
  11. package/dist/generated/client.js +2454 -0
  12. package/dist/generated/client.js.map +1 -0
  13. package/dist/index.d.ts +7 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +7 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/plugin-dispatch.d.ts +37 -0
  18. package/dist/plugin-dispatch.d.ts.map +1 -0
  19. package/dist/plugin-dispatch.js +293 -0
  20. package/dist/plugin-dispatch.js.map +1 -0
  21. package/dist/profile-contract.d.ts +63 -0
  22. package/dist/profile-contract.d.ts.map +1 -0
  23. package/dist/profile-contract.js +543 -0
  24. package/dist/profile-contract.js.map +1 -0
  25. package/dist/profile-plan.d.ts +69 -0
  26. package/dist/profile-plan.d.ts.map +1 -0
  27. package/dist/profile-plan.js +180 -0
  28. package/dist/profile-plan.js.map +1 -0
  29. package/dist/profile.d.ts +48 -0
  30. package/dist/profile.d.ts.map +1 -0
  31. package/dist/profile.js +132 -0
  32. package/dist/profile.js.map +1 -0
  33. package/dist/profiles/custom-fields/profile.d.ts +3 -0
  34. package/dist/profiles/custom-fields/profile.d.ts.map +1 -0
  35. package/dist/profiles/custom-fields/profile.js +475 -0
  36. package/dist/profiles/custom-fields/profile.js.map +1 -0
  37. package/dist/profiles/custom-fields/src/generated/client.d.ts +2710 -0
  38. package/dist/profiles/custom-fields/src/generated/client.d.ts.map +1 -0
  39. package/dist/profiles/custom-fields/src/generated/client.js +2663 -0
  40. package/dist/profiles/custom-fields/src/generated/client.js.map +1 -0
  41. package/dist/profiles/runtime/profile.d.ts +3 -0
  42. package/dist/profiles/runtime/profile.d.ts.map +1 -0
  43. package/dist/profiles/runtime/profile.js +1065 -0
  44. package/dist/profiles/runtime/profile.js.map +1 -0
  45. package/dist/profiles/runtime/src/generated/client.d.ts +4274 -0
  46. package/dist/profiles/runtime/src/generated/client.d.ts.map +1 -0
  47. package/dist/profiles/runtime/src/generated/client.js +4607 -0
  48. package/dist/profiles/runtime/src/generated/client.js.map +1 -0
  49. package/dist/runtime-materialize.d.ts +10 -0
  50. package/dist/runtime-materialize.d.ts.map +1 -0
  51. package/dist/runtime-materialize.js +344 -0
  52. package/dist/runtime-materialize.js.map +1 -0
  53. package/dist/runtime-profile.d.ts +34 -0
  54. package/dist/runtime-profile.d.ts.map +1 -0
  55. package/dist/runtime-profile.js +203 -0
  56. package/dist/runtime-profile.js.map +1 -0
  57. package/dist/testing.d.ts +11 -0
  58. package/dist/testing.d.ts.map +1 -0
  59. package/dist/testing.js +12 -0
  60. package/dist/testing.js.map +1 -0
  61. package/generated/go/client.go +2250 -0
  62. package/generated/mcp/tools.json +13922 -0
  63. package/generated/python/client.py +4454 -0
  64. package/generated/riffdb.application.exact.json +1 -0
  65. package/generated/riffdb.contract.bundle +0 -0
  66. package/generated/rust/client.rs +6524 -0
  67. package/package.json +83 -4
  68. package/profiles/custom-fields/generated/go/client.go +2578 -0
  69. package/profiles/custom-fields/generated/mcp/tools.json +15998 -0
  70. package/profiles/custom-fields/generated/python/client.py +5141 -0
  71. package/profiles/custom-fields/generated/riffdb.application.exact.json +1 -0
  72. package/profiles/custom-fields/generated/riffdb.contract.bundle +0 -0
  73. package/profiles/custom-fields/generated/rust/client.rs +7980 -0
  74. package/profiles/custom-fields/profile.ts +474 -0
  75. package/profiles/custom-fields/riffdb/contract.riff +568 -0
  76. package/profiles/custom-fields/riffdb/queries/admin_users_all_email_asc.riffq +24 -0
  77. package/profiles/custom-fields/riffdb/queries/admin_users_contains_asc.riffq +24 -0
  78. package/profiles/custom-fields/riffdb/queries/admin_users_contains_desc.riffq +24 -0
  79. package/profiles/custom-fields/riffdb/queries/admin_users_email_contains_created_desc.riffq +24 -0
  80. package/profiles/custom-fields/riffdb/queries/admin_users_email_contains_image_asc.riffq +24 -0
  81. package/profiles/custom-fields/riffdb/queries/admin_users_ends_with_asc.riffq +24 -0
  82. package/profiles/custom-fields/riffdb/queries/admin_users_ends_with_desc.riffq +24 -0
  83. package/profiles/custom-fields/riffdb/queries/admin_users_starts_with_asc.riffq +24 -0
  84. package/profiles/custom-fields/riffdb/queries/admin_users_starts_with_desc.riffq +24 -0
  85. package/profiles/custom-fields/riffdb/queries/find_account_by_id.riffq +24 -0
  86. package/profiles/custom-fields/riffdb/queries/find_account_by_provider.riffq +26 -0
  87. package/profiles/custom-fields/riffdb/queries/find_accounts_by_user_id.riffq +25 -0
  88. package/profiles/custom-fields/riffdb/queries/find_accounts_by_user_provider.riffq +27 -0
  89. package/profiles/custom-fields/riffdb/queries/find_active_sessions_by_user_id.riffq +23 -0
  90. package/profiles/custom-fields/riffdb/queries/find_credential_account.riffq +25 -0
  91. package/profiles/custom-fields/riffdb/queries/find_expired_verification_ids.riffq +17 -0
  92. package/profiles/custom-fields/riffdb/queries/find_latest_verification.riffq +20 -0
  93. package/profiles/custom-fields/riffdb/queries/find_session_by_id.riffq +20 -0
  94. package/profiles/custom-fields/riffdb/queries/find_session_by_token.riffq +20 -0
  95. package/profiles/custom-fields/riffdb/queries/find_sessions_by_token_set.riffq +20 -0
  96. package/profiles/custom-fields/riffdb/queries/find_sessions_by_user_id.riffq +21 -0
  97. package/profiles/custom-fields/riffdb/queries/find_user_by_email.riffq +16 -0
  98. package/profiles/custom-fields/riffdb/queries/find_user_by_id.riffq +15 -0
  99. package/profiles/custom-fields/riffdb/queries/find_verification_by_id.riffq +19 -0
  100. package/profiles/custom-fields/riffdb/queries/find_verifications_by_identifier.riffq +21 -0
  101. package/profiles/custom-fields/riffdb/queries/profile_account_with_user_by_provider.riffq +31 -0
  102. package/profiles/custom-fields/riffdb/queries/profile_session_with_user_by_id.riffq +14 -0
  103. package/profiles/custom-fields/riffdb/queries/profile_session_with_user_by_token.riffq +14 -0
  104. package/profiles/custom-fields/riffdb/queries/profile_sessions_with_users_by_token_set.riffq +25 -0
  105. package/profiles/custom-fields/riffdb/queries/profile_user_with_accounts_by_email.riffq +19 -0
  106. package/profiles/custom-fields/riffdb/queries/profile_user_with_accounts_by_id.riffq +19 -0
  107. package/profiles/custom-fields/riffdb.application.json +217 -0
  108. package/profiles/custom-fields/riffdb.application.lock.json +1 -0
  109. package/profiles/custom-fields/riffdb.toml +3 -0
  110. package/profiles/custom-fields/src/generated/client.ts +3096 -0
  111. package/profiles/organization-security/materialization.json +51 -0
  112. package/profiles/organization-security/riffdb/contract.riff +778 -0
  113. package/profiles/organization-security/riffdb/queries/find_account_by_id.riffq +18 -0
  114. package/profiles/organization-security/riffdb/queries/find_invitation_by_email.riffq +17 -0
  115. package/profiles/organization-security/riffdb/queries/find_invitation_by_id.riffq +18 -0
  116. package/profiles/organization-security/riffdb/queries/find_invitation_by_organization_id.riffq +17 -0
  117. package/profiles/organization-security/riffdb/queries/find_member_by_id.riffq +18 -0
  118. package/profiles/organization-security/riffdb/queries/find_member_by_user_id.riffq +17 -0
  119. package/profiles/organization-security/riffdb/queries/find_organization_by_id.riffq +16 -0
  120. package/profiles/organization-security/riffdb/queries/find_organization_by_slug.riffq +16 -0
  121. package/profiles/organization-security/riffdb/queries/find_session_by_id.riffq +18 -0
  122. package/profiles/organization-security/riffdb/queries/find_session_by_token.riffq +16 -0
  123. package/profiles/organization-security/riffdb/queries/find_team_by_id.riffq +18 -0
  124. package/profiles/organization-security/riffdb/queries/find_team_member_by_id.riffq +18 -0
  125. package/profiles/organization-security/riffdb/queries/find_team_member_by_membership_key.riffq +16 -0
  126. package/profiles/organization-security/riffdb/queries/find_team_member_by_team_id.riffq +17 -0
  127. package/profiles/organization-security/riffdb/queries/find_two_factor_by_id.riffq +18 -0
  128. package/profiles/organization-security/riffdb/queries/find_two_factor_by_secret.riffq +17 -0
  129. package/profiles/organization-security/riffdb/queries/find_user_by_email.riffq +16 -0
  130. package/profiles/organization-security/riffdb/queries/find_user_by_id.riffq +16 -0
  131. package/profiles/organization-security/riffdb/queries/find_verification_by_id.riffq +16 -0
  132. package/profiles/organization-security/riffdb/queries/find_verification_by_identifier.riffq +17 -0
  133. package/profiles/organization-security/riffdb.application.json +172 -0
  134. package/profiles/organization-security/riffdb.toml +7 -0
  135. package/profiles/organization-security.profile.json +980 -0
  136. package/profiles/runtime/generated/go/client.go +4511 -0
  137. package/profiles/runtime/generated/mcp/tools.json +30004 -0
  138. package/profiles/runtime/generated/python/client.py +9337 -0
  139. package/profiles/runtime/generated/riffdb.application.exact.json +1 -0
  140. package/profiles/runtime/generated/riffdb.contract.bundle +0 -0
  141. package/profiles/runtime/generated/rust/client.rs +13297 -0
  142. package/profiles/runtime/profile.ts +1064 -0
  143. package/profiles/runtime/riffdb/contract.riff +1053 -0
  144. package/profiles/runtime/riffdb/queries/admin_users_all_email_asc.riffq +24 -0
  145. package/profiles/runtime/riffdb/queries/admin_users_contains_asc.riffq +24 -0
  146. package/profiles/runtime/riffdb/queries/admin_users_contains_desc.riffq +24 -0
  147. package/profiles/runtime/riffdb/queries/admin_users_email_contains_created_desc.riffq +24 -0
  148. package/profiles/runtime/riffdb/queries/admin_users_email_contains_image_asc.riffq +24 -0
  149. package/profiles/runtime/riffdb/queries/admin_users_ends_with_asc.riffq +24 -0
  150. package/profiles/runtime/riffdb/queries/admin_users_ends_with_desc.riffq +24 -0
  151. package/profiles/runtime/riffdb/queries/admin_users_starts_with_asc.riffq +24 -0
  152. package/profiles/runtime/riffdb/queries/admin_users_starts_with_desc.riffq +24 -0
  153. package/profiles/runtime/riffdb/queries/find_account_by_id.riffq +24 -0
  154. package/profiles/runtime/riffdb/queries/find_account_by_provider.riffq +26 -0
  155. package/profiles/runtime/riffdb/queries/find_accounts_by_user_id.riffq +25 -0
  156. package/profiles/runtime/riffdb/queries/find_accounts_by_user_provider.riffq +27 -0
  157. package/profiles/runtime/riffdb/queries/find_active_sessions_by_user_id.riffq +23 -0
  158. package/profiles/runtime/riffdb/queries/find_credential_account.riffq +25 -0
  159. package/profiles/runtime/riffdb/queries/find_expired_verification_ids.riffq +17 -0
  160. package/profiles/runtime/riffdb/queries/find_latest_verification.riffq +20 -0
  161. package/profiles/runtime/riffdb/queries/find_session_by_id.riffq +20 -0
  162. package/profiles/runtime/riffdb/queries/find_session_by_token.riffq +20 -0
  163. package/profiles/runtime/riffdb/queries/find_sessions_by_token_set.riffq +20 -0
  164. package/profiles/runtime/riffdb/queries/find_sessions_by_user_id.riffq +21 -0
  165. package/profiles/runtime/riffdb/queries/find_user_by_email.riffq +16 -0
  166. package/profiles/runtime/riffdb/queries/find_user_by_id.riffq +15 -0
  167. package/profiles/runtime/riffdb/queries/find_verification_by_id.riffq +19 -0
  168. package/profiles/runtime/riffdb/queries/find_verifications_by_identifier.riffq +21 -0
  169. package/profiles/runtime/riffdb/queries/profile_account_with_user_by_provider.riffq +31 -0
  170. package/profiles/runtime/riffdb/queries/profile_invitation_by_email.riffq +17 -0
  171. package/profiles/runtime/riffdb/queries/profile_invitation_by_id.riffq +17 -0
  172. package/profiles/runtime/riffdb/queries/profile_invitation_by_organization_email_status.riffq +19 -0
  173. package/profiles/runtime/riffdb/queries/profile_invitation_by_organization_id.riffq +17 -0
  174. package/profiles/runtime/riffdb/queries/profile_invitation_by_organization_status.riffq +18 -0
  175. package/profiles/runtime/riffdb/queries/profile_member_by_id.riffq +27 -0
  176. package/profiles/runtime/riffdb/queries/profile_member_by_organization.riffq +20 -0
  177. package/profiles/runtime/riffdb/queries/profile_member_by_organization_created_at_asc.riffq +20 -0
  178. package/profiles/runtime/riffdb/queries/profile_member_by_organization_created_at_desc.riffq +20 -0
  179. package/profiles/runtime/riffdb/queries/profile_member_by_organization_role_asc.riffq +20 -0
  180. package/profiles/runtime/riffdb/queries/profile_member_by_organization_role_desc.riffq +20 -0
  181. package/profiles/runtime/riffdb/queries/profile_member_by_organization_user.riffq +28 -0
  182. package/profiles/runtime/riffdb/queries/profile_member_by_user.riffq +27 -0
  183. package/profiles/runtime/riffdb/queries/profile_organization_by_id.riffq +27 -0
  184. package/profiles/runtime/riffdb/queries/profile_organization_by_slug.riffq +27 -0
  185. package/profiles/runtime/riffdb/queries/profile_session_with_user_by_id.riffq +14 -0
  186. package/profiles/runtime/riffdb/queries/profile_session_with_user_by_token.riffq +14 -0
  187. package/profiles/runtime/riffdb/queries/profile_sessions_with_users_by_token_set.riffq +25 -0
  188. package/profiles/runtime/riffdb/queries/profile_two_factor_by_id.riffq +17 -0
  189. package/profiles/runtime/riffdb/queries/profile_two_factor_by_user.riffq +17 -0
  190. package/profiles/runtime/riffdb/queries/profile_user_with_accounts_by_email.riffq +19 -0
  191. package/profiles/runtime/riffdb/queries/profile_user_with_accounts_by_id.riffq +19 -0
  192. package/profiles/runtime/riffdb/queries/profile_users_by_id_set.riffq +8 -0
  193. package/profiles/runtime/riffdb.application.json +324 -0
  194. package/profiles/runtime/riffdb.application.lock.json +1 -0
  195. package/profiles/runtime/riffdb.toml +3 -0
  196. package/profiles/runtime/src/generated/client.ts +5186 -0
  197. package/riffdb/contract.riff +547 -0
  198. package/riffdb/queries/admin_users_all_email_asc.riffq +24 -0
  199. package/riffdb/queries/admin_users_contains_asc.riffq +24 -0
  200. package/riffdb/queries/admin_users_contains_desc.riffq +24 -0
  201. package/riffdb/queries/admin_users_email_contains_created_desc.riffq +24 -0
  202. package/riffdb/queries/admin_users_email_contains_image_asc.riffq +24 -0
  203. package/riffdb/queries/admin_users_ends_with_asc.riffq +24 -0
  204. package/riffdb/queries/admin_users_ends_with_desc.riffq +24 -0
  205. package/riffdb/queries/admin_users_starts_with_asc.riffq +24 -0
  206. package/riffdb/queries/admin_users_starts_with_desc.riffq +24 -0
  207. package/riffdb/queries/find_account_by_id.riffq +24 -0
  208. package/riffdb/queries/find_account_by_provider.riffq +26 -0
  209. package/riffdb/queries/find_accounts_by_user_id.riffq +25 -0
  210. package/riffdb/queries/find_accounts_by_user_provider.riffq +27 -0
  211. package/riffdb/queries/find_active_sessions_by_user_id.riffq +23 -0
  212. package/riffdb/queries/find_credential_account.riffq +25 -0
  213. package/riffdb/queries/find_expired_verification_ids.riffq +17 -0
  214. package/riffdb/queries/find_latest_verification.riffq +20 -0
  215. package/riffdb/queries/find_session_by_id.riffq +20 -0
  216. package/riffdb/queries/find_session_by_token.riffq +20 -0
  217. package/riffdb/queries/find_sessions_by_token_set.riffq +20 -0
  218. package/riffdb/queries/find_sessions_by_user_id.riffq +21 -0
  219. package/riffdb/queries/find_user_by_email.riffq +16 -0
  220. package/riffdb/queries/find_user_by_id.riffq +15 -0
  221. package/riffdb/queries/find_verification_by_id.riffq +19 -0
  222. package/riffdb/queries/find_verifications_by_identifier.riffq +21 -0
  223. package/riffdb.application.json +187 -0
  224. package/riffdb.application.lock.json +1 -0
  225. package/scripts/real-riffdb-runner.mjs +333 -0
  226. package/src/generated/client.ts +2746 -0
package/README.md CHANGED
@@ -1,3 +1,324 @@
1
- # Temporary Holding Version
1
+ # `@riffdb/better-auth`
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ An external Better Auth database adapter for RiffDB.
4
+
5
+ Status: core and compiled runtime profiles are implemented for Better Auth
6
+ `1.7.18`. The default core profile, a custom-field profile, and an organization
7
+ plus 2FA profile run through Better Auth's official authentication-flow suite
8
+ against a real TLS loopback RiffDB service. The custom-field profile also runs
9
+ the official additional-fields test.
10
+
11
+ `nextCookies()` is supported with each profile. Organizations and 2FA require
12
+ an explicitly selected, deployed compiled profile. Teams and dynamic access
13
+ control remain unsupported. The organization profile also rejects user deletion;
14
+ its stored member and invitation references remain enforced.
15
+
16
+ ## Architecture boundary
17
+
18
+ Better Auth-specific code lives in this repository, never in RiffDB. The
19
+ adapter dispatches only to generated named RiffDB commands and queries from a
20
+ reviewed compiled profile. It does not expose generic CRUD, accept arbitrary
21
+ transaction callbacks, perform client-side authorization filtering, or
22
+ silently fall back for unknown models, fields, joins, predicates, or plugins.
23
+
24
+ The default core runtime profile is deliberately exact:
25
+
26
+ - Better Auth `1.7.18`;
27
+ - the four default core models and their default field names;
28
+ - UUID identifiers for every model;
29
+ - the reviewed `nextCookies()` integration, with no storage plugins or additional fields; and
30
+ - the bundled `RiffDBBetterAuthCore` contract and `BetterAuthServer` role.
31
+
32
+ Unsupported configuration fails before any RiffDB request. A custom or plugin
33
+ profile requires its own generated contract, named operations, client facade,
34
+ and conformance tests. `materializeRiffDBBetterAuthRuntimeProfile` emits the
35
+ reviewable runtime sources and binding; RiffDB compiles and locks their client.
36
+ Adapter construction also verifies that the resolved
37
+ `better-auth` installation is exactly `1.7.18`; package-manager overrides do
38
+ not bypass that runtime check. Secret-bearing Better Auth debug logging is
39
+ rejected rather than forwarded to the factory logger.
40
+
41
+ ## Runtime use
42
+
43
+ ```ts
44
+ import { DriverApplicationTransport } from "@riffdb/client";
45
+ import { betterAuth } from "better-auth";
46
+ import { riffdbAdapter } from "@riffdb/better-auth";
47
+
48
+ const driver = await DriverApplicationTransport.connect({
49
+ socketPath,
50
+ identity,
51
+ });
52
+
53
+ export const auth = betterAuth({
54
+ database: riffdbAdapter({
55
+ driver,
56
+ organizationId: "018f5f7e-7b0c-7d9a-8f14-77f42f53c851",
57
+ }),
58
+ advanced: { database: { generateId: "uuid" } },
59
+ emailAndPassword: { enabled: true },
60
+ });
61
+ ```
62
+
63
+ The caller owns the RiffDB driver connection and application-defined
64
+ organization ID. Better Auth continues to own users, sessions, passwords,
65
+ sign-in, OAuth, and reset flows. The package contains the exact RiffDB
66
+ application source, lock, contract bundle, and generated artifacts required to
67
+ deploy the core profile. Better Auth development dependencies stay exactly
68
+ pinned. Supported versions are explicit; a scheduled registry check flags new
69
+ stable releases for schema, call-shape, and conformance review.
70
+
71
+ For Next.js Server Actions, import `nextCookies` from `better-auth/next-js`
72
+ and add `nextCookies()` as the last plugin in the Better Auth configuration.
73
+ It can accompany the default core profile or the compiled profiles below.
74
+ Cookie propagation, RSC refresh suppression, and Server Action refresh behavior
75
+ are covered by integration tests using the official plugin and mocked Next.js
76
+ request helpers.
77
+
78
+ ## Organization and 2FA runtime profile
79
+
80
+ Deploy `profiles/runtime/riffdb.application.json` and its exact lock before
81
+ connecting the driver. Select its binding explicitly:
82
+
83
+ ```ts
84
+ import profile from "@riffdb/better-auth/profiles/organization-security";
85
+ import { organization, twoFactor } from "better-auth/plugins";
86
+
87
+ const auth = betterAuth({
88
+ database: riffdbAdapter({ driver, organizationId: tenantId, profile }),
89
+ advanced: { database: { generateId: "uuid" } },
90
+ emailAndPassword: { enabled: true },
91
+ user: {
92
+ additionalFields: {
93
+ locale: { type: "string", required: false },
94
+ dateField: { type: "date", required: false },
95
+ },
96
+ },
97
+ plugins: [
98
+ organization({ membershipLimit: 100, invitationLimit: 100 }),
99
+ twoFactor(),
100
+ ],
101
+ });
102
+ ```
103
+
104
+ These are example profile fields; `dateField` also serves the upstream
105
+ conformance test. Generate a separate reviewed profile to use a different field
106
+ inventory. The standalone custom-field example is available at
107
+ `@riffdb/better-auth/profiles/custom-fields` and deploys from
108
+ `profiles/custom-fields/` without storage plugins.
109
+
110
+ The adapter's `organizationId` is the application's RiffDB tenant partition.
111
+ The organization plugin's `organizationId` identifies an auth organization
112
+ inside that partition. They are separate identifiers.
113
+
114
+ The organization profile supports organization creation/update/listing, active
115
+ organization sessions, membership listing/counts/roles/removal, invitations and
116
+ guarded status transitions, and atomic organization/dependent deletion.
117
+ Membership creation and session activation during invitation acceptance share
118
+ one compiled command. Native capacity guards admit at most 100 live memberships
119
+ and 100 retained invitations per organization; deleting an individual record
120
+ releases its capacity. Lists are bounded at 100, membership offsets at 4096,
121
+ and user hydration sets at 32. Member pages support the compiled default role
122
+ order, role ascending/descending, and creation-time ascending/descending.
123
+ Other member filters and sorts are rejected.
124
+
125
+ 2FA supports TOTP enrollment/verification, OTP challenges, backup codes,
126
+ challenge consumption, and account lockout. Failure increments, expired-lock
127
+ reset, backup-code replacement, and invitation status transitions use native
128
+ compiled commands. Tests cover TOTP enrollment, backup-code and invitation
129
+ races, challenge replay, lockout counters, capacity contention, and reset rollback.
130
+
131
+ Teams, dynamic access control, organization mutation hooks, renamed storage,
132
+ plugin-model custom fields, user deletion in organization profiles, and
133
+ `revokeSessionsOnPasswordReset` are rejected. Password changes that request
134
+ `revokeOtherSessions` also require a separately compiled transaction graph.
135
+ Organization limits must be integers from 1 through 100. Custom runtime fields
136
+ currently support scalar strings, booleans, safe integers, and dates on `user`;
137
+ custom indexes, uniqueness,
138
+ and references require a separately implemented profile. Credential password
139
+ reset supports an existing credential account and one verification row per
140
+ identifier. Additional transaction graphs fail closed.
141
+
142
+ ## Implemented core surface
143
+
144
+ The core profile supports:
145
+
146
+ - atomic email/password signup and OAuth user/account creation;
147
+ - user, account, session, and verification create/read/update/delete paths;
148
+ - revision-guarded partial updates with bounded stale retries;
149
+ - atomic delete-and-return single-use verification consumption;
150
+ - bounded dependent and cascade deletion;
151
+ - active-session, token-set, provider-account, and verification queries;
152
+ - exact user counts with an optional UUID or bounded email text predicate; and
153
+ - bounded user windows with numeric offsets and compiled email, creation-time,
154
+ or nullable-image ordering.
155
+
156
+ User windows accept at most 100 results and an offset no greater than 4096.
157
+ Email search supports `contains`, `starts_with`, and `ends_with`; every accepted
158
+ filter/order combination maps to a distinct generated RiffQL query. Unsupported
159
+ counts, fields, filters, orderings, joins, transaction graphs, and increments
160
+ fail closed.
161
+
162
+ Token-set session reads use one bounded compiled membership query. Combining a
163
+ token set with an expiry cutoff remains unsupported until that exact compound
164
+ predicate has its own reviewed named query. Verification listing accepts its
165
+ compiled default order, plus `createdAt desc` with `limit: 1`; every other
166
+ verification sort is rejected before a RiffDB request.
167
+
168
+ Secret account credentials, session tokens, and verification values are read
169
+ by named RiffQL queries whose exact returned leaves carry compiler-checked
170
+ `reveals` declarations. The generated role receives authority only for those
171
+ leaves. These reads remain queries, while commands are reserved for writes and
172
+ atomic state transitions. Secret-output queries are excluded from generated
173
+ MCP tools by RiffDB.
174
+
175
+ ## Planning a custom profile
176
+
177
+ `riffdbProfileAdapter` integrates with Better Auth's `createSchema` hook and
178
+ does not require a live database. It resolves the complete Better Auth schema,
179
+ including plugin models and additional fields, into a deterministic bounded
180
+ profile plan:
181
+
182
+ ```ts
183
+ import { betterAuth } from "better-auth";
184
+ import { organization, twoFactor } from "better-auth/plugins";
185
+ import { riffdbProfileAdapter } from "@riffdb/better-auth";
186
+
187
+ export const auth = betterAuth({
188
+ database: riffdbProfileAdapter({
189
+ name: "BetterAuthOrganizationSecurity",
190
+ contractLineage: "BetterAuthOrganizationSecurity",
191
+ applicationRole: "BetterAuthServer",
192
+ output: "riffdb/better-auth.profile.json",
193
+ }),
194
+ advanced: { database: { generateId: "uuid" } },
195
+ user: {
196
+ additionalFields: {
197
+ locale: { type: "string", required: false },
198
+ },
199
+ },
200
+ plugins: [organization({ teams: { enabled: true } }), twoFactor()],
201
+ });
202
+ ```
203
+
204
+ With that generation-only config, run the Better Auth CLI version matching the
205
+ pinned runtime:
206
+
207
+ ```bash
208
+ npx auth@1.7.18 generate --config ./auth.profile.ts --yes
209
+ ```
210
+
211
+ The output records resolved storage names, field types, required/unique/index
212
+ metadata, references, secret classification, default/update behavior, plugin
213
+ IDs, and finite profile bounds. It is an input to reviewed RiffDB profile
214
+ materialization, not a dynamically executable schema or migration. Runtime
215
+ methods on the generation-only adapter always fail.
216
+
217
+ The checked-in
218
+ [`profiles/organization-security.profile.json`](./profiles/organization-security.profile.json)
219
+ is a reproducible organization, teams, two-factor, and custom-user-field
220
+ example. Regenerate or verify it with:
221
+
222
+ ```bash
223
+ npm run profiles:write
224
+ npm run profiles:check
225
+ ```
226
+
227
+ ## Materializing an executable profile
228
+
229
+ Pass the reviewed plan to `materializeRiffDBBetterAuthRuntimeProfile(plan)`.
230
+ Write the returned `files` under a new application directory, review the
231
+ contract, queries, and `profile.ts`, then run `riffdb application lock --write`
232
+ in that directory. Compile `profile.ts` and the generated TypeScript client
233
+ with your application and pass its default export as `riffdbAdapter({ profile,
234
+ driver, organizationId })`. A JSON plan alone cannot be passed to the adapter.
235
+ Runtime construction compares the resolved schema and reviewed plugins with
236
+ the bound plan before requests. Binding also checks compiler-owned command and
237
+ result schemas, so a stale client cannot silently omit a new optional field.
238
+ The generated client and driver enforce the exact compiled application and
239
+ operation identities.
240
+
241
+ The planning fixture also reports its explicit non-null requirement for the
242
+ optional unique team membership key; it cannot accept legacy null-key rows.
243
+
244
+ The executable generator accepts the core schema plus the reviewed organization
245
+ and 2FA schemas. The broader `materializeRiffDBBetterAuthProfile` remains a
246
+ planning tool: its teams example reports unenforced references and is not an
247
+ executable runtime profile.
248
+
249
+ Regenerate the executable examples with `npm run profiles:runtime`, review
250
+ the source diff, and write each application's lock explicitly. `npm run
251
+ profiles:check` verifies source freshness, compiler locks, and generated clients.
252
+
253
+ A different profile is a different compiled application identity. There is no
254
+ automatic switch or reinterpretation of existing core data. For existing
255
+ installations, plan and review an explicit data migration, including identifier
256
+ mapping, required/default fields, references, secrets, and capacity counts.
257
+ Deploy the matching contract, operations, role, and client together. Never point
258
+ a new binding at an old lock. Runtime profile examples are intended for fresh
259
+ installations until an application-specific migration has been reviewed.
260
+
261
+ ## Local development
262
+
263
+ ```bash
264
+ npm ci --ignore-scripts
265
+ npm run check
266
+ ```
267
+
268
+ The lock is pinned to `@riffdb/client@0.2.1` and `@riffdb/cli@0.2.1`.
269
+
270
+ `npm run check` performs strict type checking, semantic and upstream tests,
271
+ profile-artifact freshness, a production build, and a package dry run.
272
+
273
+ ## Real loopback conformance
274
+
275
+ Run the official suite against isolated TLS services and Rust driver hosts:
276
+
277
+ ```bash
278
+ npm run test:loopback
279
+ ```
280
+
281
+ This runs the core, custom-field, and organization/2FA profiles. It requires
282
+ `riffdbd` and `riffdb-driverd` from the immutable RiffDB build in
283
+ `.github/riffdb-revision` on PATH. `RIFFDB_CLI`, `RIFFDB_SERVER_BIN`, and
284
+ `RIFFDB_DRIVERD` can select exact local binaries. The published RiffDB `0.2.1`
285
+ release bundle includes the CLI and service, but omits the driver host. Build
286
+ that host from a separate RiffDB checkout at the full revision recorded in
287
+ `.github/riffdb-revision`:
288
+
289
+ ```bash
290
+ # In that separate, pinned RiffDB checkout:
291
+ RUSTC_WRAPPER= cargo build --locked --release -p riffdb-driver-host --bin riffdb-driverd
292
+
293
+ # Back in this adapter checkout, with riffdbd on PATH:
294
+ RIFFDB_DRIVERD=/path/to/riffdb/target/release/riffdb-driverd npm run test:loopback
295
+ ```
296
+
297
+ Release CI verifies the bundle checksum and revision, then builds the driver
298
+ from that same revision. Certificates, capabilities, sockets, and databases
299
+ are disposable and removed at the end. The official
300
+ harness's cleanup hook is scoped to that disposable service; it is never a
301
+ public delete-all operation.
302
+
303
+ The original `scripts/real-riffdb-runner.mjs` also remains available to an
304
+ external harness supplying its ten exact driver-identity arguments.
305
+
306
+ See [DESIGN.md](./DESIGN.md) for the method and safety inventory. The adapter
307
+ follows Better Auth's
308
+ [database-adapter guide](https://better-auth.com/docs/guides/create-a-db-adapter)
309
+ and uses its official pinned conformance utilities.
310
+
311
+
312
+ ## RiffDB compatibility CI
313
+
314
+ This adapter owns its RiffDB compatibility checks. Pull requests compile the
315
+ application sources and verify existing locks and generated artifacts against
316
+ the full commit in `.github/riffdb-revision`. Scheduled checks use RiffDB
317
+ `main`; manual dispatch accepts a full commit or `main`. Failures are reported
318
+ here and do not block RiffDB itself. The job also runs `npm run check` and
319
+ `npm run test:loopback`, including the official Better Auth suite for every
320
+ advertised runtime profile.
321
+
322
+ To upgrade, update the pin, migrate sources as needed, review the result of
323
+ `riffdb application lock --write`, and verify with `riffdb application check`.
324
+ Commit the pin and migration together in this repository.
@@ -0,0 +1,34 @@
1
+ import { type DriverApplicationTransport } from "@riffdb/client";
2
+ import { type AdapterFactory, type DBAdapterDebugLogOption } from "better-auth/adapters";
3
+ import type { BetterAuthOptions } from "better-auth";
4
+ import { RiffDBBetterAuthCoreClient } from "./generated/client.js";
5
+ import { type RiffDBCompiledProfile } from "./runtime-profile.js";
6
+ export type RiffDBBetterAuthCallErrorCode = "invalid_configuration" | "unsupported_model" | "unsupported_call_shape" | "unsupported_transaction_shape" | "result_bound_exceeded" | "invalid_record_shape" | "declared_outcome";
7
+ export declare class RiffDBBetterAuthCallError extends Error {
8
+ readonly code: RiffDBBetterAuthCallErrorCode;
9
+ readonly operation: string;
10
+ readonly outcome?: string | undefined;
11
+ constructor(code: RiffDBBetterAuthCallErrorCode, operation: string, outcome?: string | undefined);
12
+ }
13
+ export interface RiffDBBetterAuthAdapterConfig {
14
+ readonly driver: DriverApplicationTransport;
15
+ readonly organizationId: string;
16
+ readonly commandAttemptBudget?: number;
17
+ readonly debugLogs?: DBAdapterDebugLogOption;
18
+ readonly profile?: RiffDBCompiledProfile;
19
+ }
20
+ type CoreClient = Omit<RiffDBBetterAuthCoreClient, "causalSession" | "withReadAfterCommit"> & {
21
+ causalSession(): CoreClient;
22
+ };
23
+ interface InternalAdapterConfig {
24
+ readonly client: CoreClient;
25
+ readonly organizationId: string;
26
+ readonly debugLogs?: DBAdapterDebugLogOption;
27
+ readonly resetModelForTesting?: (model: string) => number | Promise<number>;
28
+ readonly profile?: RiffDBCompiledProfile;
29
+ }
30
+ export declare function assertSupportedBetterAuthInstallation(installedVersion?: string): void;
31
+ export declare function buildRiffDBBetterAuthAdapter(config: InternalAdapterConfig): AdapterFactory<BetterAuthOptions>;
32
+ export declare function riffdbAdapter(config: RiffDBBetterAuthAdapterConfig): AdapterFactory<BetterAuthOptions>;
33
+ export {};
34
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEL,KAAK,cAAc,EAInB,KAAK,uBAAuB,EAE7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EACL,0BAA0B,EAe3B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAkD3D,MAAM,MAAM,6BAA6B,GACrC,uBAAuB,GACvB,mBAAmB,GACnB,wBAAwB,GACxB,+BAA+B,GAC/B,uBAAuB,GACvB,sBAAsB,GACtB,kBAAkB,CAAC;AAEvB,qBAAa,yBAA0B,SAAQ,KAAK;aAEhC,IAAI,EAAE,6BAA6B;aACnC,SAAS,EAAE,MAAM;aACjB,OAAO,CAAC,EAAE,MAAM;IAHlC,YACkB,IAAI,EAAE,6BAA6B,EACnC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,YAAA,EAQjC;CACF;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAC5C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,uBAAuB,CAAC;IAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC;CAC1C;AAED,KAAK,UAAU,GAAG,IAAI,CAAC,0BAA0B,EAAE,eAAe,GAAG,qBAAqB,CAAC,GAAG;IAC5F,aAAa,IAAI,UAAU,CAAC;CAC7B,CAAC;AAEF,UAAU,qBAAqB;IAC7B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,uBAAuB,CAAC;IAC7C,QAAQ,CAAC,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5E,QAAQ,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC;CAC1C;AAuBD,wBAAgB,qCAAqC,CACnD,gBAAgB,SAAsC,GACrD,IAAI,CAIN;AAqnDD,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,qBAAqB,GAC5B,cAAc,CAAC,iBAAiB,CAAC,CA2CnC;AAED,wBAAgB,aAAa,CAC3B,MAAM,EAAE,6BAA6B,GACpC,cAAc,CAAC,iBAAiB,CAAC,CAanC"}