regstack 0.5.0__tar.gz → 0.5.9__tar.gz

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 (241) hide show
  1. {regstack-0.5.0 → regstack-0.5.9}/.github/workflows/publish.yml +6 -1
  2. regstack-0.5.9/CHANGELOG.md +374 -0
  3. {regstack-0.5.0 → regstack-0.5.9}/PKG-INFO +10 -5
  4. {regstack-0.5.0 → regstack-0.5.9}/docs/architecture.md +7 -3
  5. {regstack-0.5.0 → regstack-0.5.9}/docs/changelog.md +233 -0
  6. {regstack-0.5.0 → regstack-0.5.9}/docs/configuration.md +102 -4
  7. {regstack-0.5.0 → regstack-0.5.9}/docs/quickstart.md +8 -3
  8. {regstack-0.5.0 → regstack-0.5.9}/docs/security.md +57 -7
  9. {regstack-0.5.0 → regstack-0.5.9}/pyproject.toml +19 -5
  10. regstack-0.5.9/scripts/ccr_coverage_setup.py +242 -0
  11. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/app.py +55 -2
  12. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/auth/dependencies.py +12 -4
  13. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/auth/password.py +2 -20
  14. regstack-0.5.9/src/regstack/auth/rate_limit.py +141 -0
  15. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/mongo/backend.py +5 -6
  16. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/mongo/client.py +9 -3
  17. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/mongo/indexes.py +2 -1
  18. regstack-0.5.9/src/regstack/backends/mongo/repositories/blacklist_repo.py +43 -0
  19. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/mongo/repositories/login_attempt_repo.py +3 -1
  20. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/mongo/repositories/mfa_code_repo.py +2 -1
  21. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/mongo/repositories/oauth_identity_repo.py +3 -1
  22. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/mongo/repositories/oauth_state_repo.py +14 -3
  23. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/mongo/repositories/pending_repo.py +3 -16
  24. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/mongo/repositories/user_repo.py +3 -1
  25. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/protocols.py +14 -1
  26. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/migrations/__init__.py +3 -1
  27. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/migrations/env.py +5 -1
  28. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/repositories/mfa_code_repo.py +0 -2
  29. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/repositories/oauth_identity_repo.py +4 -6
  30. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/repositories/oauth_state_repo.py +14 -5
  31. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/repositories/pending_repo.py +4 -6
  32. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/repositories/user_repo.py +4 -4
  33. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/types.py +4 -2
  34. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/cli/doctor.py +9 -5
  35. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/config/schema.py +18 -1
  36. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/composer.py +9 -2
  37. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/ses.py +1 -1
  38. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/hooks/events.py +7 -0
  39. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/models/_objectid.py +1 -1
  40. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/models/user.py +14 -1
  41. regstack-0.5.9/src/regstack/routers/_helpers.py +28 -0
  42. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/routers/account.py +6 -24
  43. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/routers/admin.py +4 -0
  44. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/routers/login.py +12 -5
  45. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/routers/logout.py +3 -1
  46. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/routers/oauth.py +177 -16
  47. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/routers/phone.py +12 -3
  48. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/routers/verify.py +6 -2
  49. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/sms/sns.py +1 -1
  50. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/sms/twilio.py +1 -1
  51. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/ui/pages.py +9 -2
  52. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/ui/static/js/regstack.js +9 -0
  53. regstack-0.5.9/src/regstack/version.py +1 -0
  54. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/theme_designer/writer.py +1 -3
  55. {regstack-0.5.0 → regstack-0.5.9}/tasks.py +16 -1
  56. {regstack-0.5.0 → regstack-0.5.9}/tests/integration/test_admin_router.py +39 -0
  57. {regstack-0.5.0 → regstack-0.5.9}/tests/integration/test_happy_path.py +40 -0
  58. {regstack-0.5.0 → regstack-0.5.9}/tests/integration/test_mfa.py +110 -0
  59. {regstack-0.5.0 → regstack-0.5.9}/tests/integration/test_oauth_google_router.py +126 -0
  60. regstack-0.5.9/tests/integration/test_rate_limits.py +215 -0
  61. {regstack-0.5.0 → regstack-0.5.9}/uv.lock +54 -6
  62. regstack-0.5.0/CHANGELOG.md +0 -145
  63. regstack-0.5.0/src/regstack/backends/mongo/repositories/blacklist_repo.py +0 -28
  64. regstack-0.5.0/src/regstack/version.py +0 -1
  65. {regstack-0.5.0 → regstack-0.5.9}/.github/workflows/test.yml +0 -0
  66. {regstack-0.5.0 → regstack-0.5.9}/.gitignore +0 -0
  67. {regstack-0.5.0 → regstack-0.5.9}/.python-version +0 -0
  68. {regstack-0.5.0 → regstack-0.5.9}/.readthedocs.yaml +0 -0
  69. {regstack-0.5.0 → regstack-0.5.9}/CLAUDE.md +0 -0
  70. {regstack-0.5.0 → regstack-0.5.9}/LICENSE +0 -0
  71. {regstack-0.5.0 → regstack-0.5.9}/NOTICE +0 -0
  72. {regstack-0.5.0 → regstack-0.5.9}/README.md +0 -0
  73. {regstack-0.5.0 → regstack-0.5.9}/SECURITY.md +0 -0
  74. {regstack-0.5.0 → regstack-0.5.9}/docs/_static/.gitkeep +0 -0
  75. {regstack-0.5.0 → regstack-0.5.9}/docs/_templates/.gitkeep +0 -0
  76. {regstack-0.5.0 → regstack-0.5.9}/docs/api.md +0 -0
  77. {regstack-0.5.0 → regstack-0.5.9}/docs/cli.md +0 -0
  78. {regstack-0.5.0 → regstack-0.5.9}/docs/conf.py +0 -0
  79. {regstack-0.5.0 → regstack-0.5.9}/docs/embedding.md +0 -0
  80. {regstack-0.5.0 → regstack-0.5.9}/docs/index.md +0 -0
  81. {regstack-0.5.0 → regstack-0.5.9}/docs/oauth.md +0 -0
  82. {regstack-0.5.0 → regstack-0.5.9}/docs/security-reports/README.md +0 -0
  83. {regstack-0.5.0 → regstack-0.5.9}/docs/theming.md +0 -0
  84. {regstack-0.5.0 → regstack-0.5.9}/examples/_common/__init__.py +0 -0
  85. {regstack-0.5.0 → regstack-0.5.9}/examples/_common/app.py +0 -0
  86. {regstack-0.5.0 → regstack-0.5.9}/examples/mongo/README.md +0 -0
  87. {regstack-0.5.0 → regstack-0.5.9}/examples/mongo/branding/theme.css +0 -0
  88. {regstack-0.5.0 → regstack-0.5.9}/examples/mongo/main.py +0 -0
  89. {regstack-0.5.0 → regstack-0.5.9}/examples/mongo/regstack.toml +0 -0
  90. {regstack-0.5.0 → regstack-0.5.9}/examples/postgres/README.md +0 -0
  91. {regstack-0.5.0 → regstack-0.5.9}/examples/postgres/main.py +0 -0
  92. {regstack-0.5.0 → regstack-0.5.9}/examples/postgres/regstack.toml +0 -0
  93. {regstack-0.5.0 → regstack-0.5.9}/examples/sqlite/README.md +0 -0
  94. {regstack-0.5.0 → regstack-0.5.9}/examples/sqlite/main.py +0 -0
  95. {regstack-0.5.0 → regstack-0.5.9}/examples/sqlite/regstack.toml +0 -0
  96. {regstack-0.5.0 → regstack-0.5.9}/regstack.toml.example +0 -0
  97. {regstack-0.5.0 → regstack-0.5.9}/scripts/security-review-prompt.md +0 -0
  98. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/__init__.py +0 -0
  99. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/auth/__init__.py +0 -0
  100. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/auth/clock.py +0 -0
  101. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/auth/jwt.py +0 -0
  102. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/auth/lockout.py +0 -0
  103. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/auth/mfa.py +0 -0
  104. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/auth/tokens.py +0 -0
  105. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/__init__.py +0 -0
  106. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/base.py +0 -0
  107. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/factory.py +0 -0
  108. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/mongo/__init__.py +0 -0
  109. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/mongo/repositories/__init__.py +0 -0
  110. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/__init__.py +0 -0
  111. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/backend.py +0 -0
  112. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/migrations/script.py.mako +0 -0
  113. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/migrations/versions/0001_initial_schema.py +0 -0
  114. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/migrations/versions/0002_oauth.py +0 -0
  115. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/repositories/__init__.py +0 -0
  116. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/repositories/blacklist_repo.py +0 -0
  117. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/repositories/login_attempt_repo.py +0 -0
  118. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/backends/sql/schema.py +0 -0
  119. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/cli/__init__.py +0 -0
  120. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/cli/__main__.py +0 -0
  121. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/cli/_runtime.py +0 -0
  122. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/cli/admin.py +0 -0
  123. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/cli/init.py +0 -0
  124. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/cli/migrate.py +0 -0
  125. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/config/__init__.py +0 -0
  126. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/config/loader.py +0 -0
  127. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/config/secrets.py +0 -0
  128. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/__init__.py +0 -0
  129. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/base.py +0 -0
  130. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/console.py +0 -0
  131. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/factory.py +0 -0
  132. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/smtp.py +0 -0
  133. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/templates/email_change.html +0 -0
  134. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/templates/email_change.subject.txt +0 -0
  135. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/templates/email_change.txt +0 -0
  136. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/templates/password_reset.html +0 -0
  137. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/templates/password_reset.subject.txt +0 -0
  138. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/templates/password_reset.txt +0 -0
  139. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/templates/sms_login_mfa.txt +0 -0
  140. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/templates/sms_phone_setup.txt +0 -0
  141. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/templates/verification.html +0 -0
  142. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/templates/verification.subject.txt +0 -0
  143. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/email/templates/verification.txt +0 -0
  144. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/hooks/__init__.py +0 -0
  145. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/models/__init__.py +0 -0
  146. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/models/login_attempt.py +0 -0
  147. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/models/mfa_code.py +0 -0
  148. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/models/oauth_identity.py +0 -0
  149. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/models/oauth_state.py +0 -0
  150. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/models/pending_registration.py +0 -0
  151. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/oauth/__init__.py +0 -0
  152. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/oauth/base.py +0 -0
  153. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/oauth/errors.py +0 -0
  154. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/oauth/providers/__init__.py +0 -0
  155. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/oauth/providers/google.py +0 -0
  156. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/oauth/registry.py +0 -0
  157. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/routers/__init__.py +0 -0
  158. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/routers/_schemas.py +0 -0
  159. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/routers/password.py +0 -0
  160. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/routers/register.py +0 -0
  161. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/sms/__init__.py +0 -0
  162. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/sms/base.py +0 -0
  163. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/sms/factory.py +0 -0
  164. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/sms/null.py +0 -0
  165. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/ui/__init__.py +0 -0
  166. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/ui/static/css/core.css +0 -0
  167. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/ui/static/css/theme.css +0 -0
  168. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/ui/templates/auth/email_change_confirm.html +0 -0
  169. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/ui/templates/auth/forgot.html +0 -0
  170. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/ui/templates/auth/login.html +0 -0
  171. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/ui/templates/auth/me.html +0 -0
  172. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/ui/templates/auth/mfa_confirm.html +0 -0
  173. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/ui/templates/auth/oauth_complete.html +0 -0
  174. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/ui/templates/auth/register.html +0 -0
  175. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/ui/templates/auth/reset.html +0 -0
  176. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/ui/templates/auth/verify.html +0 -0
  177. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/ui/templates/base.html +0 -0
  178. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/__init__.py +0 -0
  179. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/oauth_google/__init__.py +0 -0
  180. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/oauth_google/cli.py +0 -0
  181. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/oauth_google/routes.py +0 -0
  182. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/oauth_google/server.py +0 -0
  183. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/oauth_google/static/wizard.css +0 -0
  184. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/oauth_google/static/wizard.js +0 -0
  185. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/oauth_google/templates/wizard.html +0 -0
  186. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/oauth_google/validators.py +0 -0
  187. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/oauth_google/window.py +0 -0
  188. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/oauth_google/writer.py +0 -0
  189. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/theme_designer/__init__.py +0 -0
  190. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/theme_designer/cli.py +0 -0
  191. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/theme_designer/routes.py +0 -0
  192. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/theme_designer/server.py +0 -0
  193. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/theme_designer/static/designer.css +0 -0
  194. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/theme_designer/static/designer.js +0 -0
  195. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/theme_designer/templates/designer.html +0 -0
  196. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/theme_designer/validators.py +0 -0
  197. {regstack-0.5.0 → regstack-0.5.9}/src/regstack/wizard/theme_designer/window.py +0 -0
  198. {regstack-0.5.0 → regstack-0.5.9}/tasks/oauth-design.md +0 -0
  199. {regstack-0.5.0 → regstack-0.5.9}/tests/__init__.py +0 -0
  200. {regstack-0.5.0 → regstack-0.5.9}/tests/_fake_google/__init__.py +0 -0
  201. {regstack-0.5.0 → regstack-0.5.9}/tests/_fake_google/provider.py +0 -0
  202. {regstack-0.5.0 → regstack-0.5.9}/tests/conftest.py +0 -0
  203. {regstack-0.5.0 → regstack-0.5.9}/tests/e2e/__init__.py +0 -0
  204. {regstack-0.5.0 → regstack-0.5.9}/tests/e2e/conftest.py +0 -0
  205. {regstack-0.5.0 → regstack-0.5.9}/tests/e2e/test_theme_designer.py +0 -0
  206. {regstack-0.5.0 → regstack-0.5.9}/tests/e2e/test_wizard_oauth_flow.py +0 -0
  207. {regstack-0.5.0 → regstack-0.5.9}/tests/integration/__init__.py +0 -0
  208. {regstack-0.5.0 → regstack-0.5.9}/tests/integration/test_account_management.py +0 -0
  209. {regstack-0.5.0 → regstack-0.5.9}/tests/integration/test_indexes.py +0 -0
  210. {regstack-0.5.0 → regstack-0.5.9}/tests/integration/test_login_lockout.py +0 -0
  211. {regstack-0.5.0 → regstack-0.5.9}/tests/integration/test_oauth_repos.py +0 -0
  212. {regstack-0.5.0 → regstack-0.5.9}/tests/integration/test_oauth_ui.py +0 -0
  213. {regstack-0.5.0 → regstack-0.5.9}/tests/integration/test_password_reset.py +0 -0
  214. {regstack-0.5.0 → regstack-0.5.9}/tests/integration/test_sql_migrations.py +0 -0
  215. {regstack-0.5.0 → regstack-0.5.9}/tests/integration/test_ui_router.py +0 -0
  216. {regstack-0.5.0 → regstack-0.5.9}/tests/integration/test_verification.py +0 -0
  217. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/__init__.py +0 -0
  218. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_base_install_imports.py +0 -0
  219. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_cli.py +0 -0
  220. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_cli_doctor.py +0 -0
  221. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_cli_init.py +0 -0
  222. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_cli_migrate.py +0 -0
  223. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_config_loader.py +0 -0
  224. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_jwt.py +0 -0
  225. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_lockout.py +0 -0
  226. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_mail_composer.py +0 -0
  227. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_mfa_code_repo.py +0 -0
  228. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_oauth_google.py +0 -0
  229. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_password.py +0 -0
  230. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_ses_backend.py +0 -0
  231. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_sms.py +0 -0
  232. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_smtp_backend.py +0 -0
  233. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_theme_designer_cli.py +0 -0
  234. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_theme_designer_routes.py +0 -0
  235. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_theme_designer_validators.py +0 -0
  236. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_theme_designer_writer.py +0 -0
  237. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_ui_env.py +0 -0
  238. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_wizard_oauth_cli.py +0 -0
  239. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_wizard_oauth_routes.py +0 -0
  240. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_wizard_oauth_validators.py +0 -0
  241. {regstack-0.5.0 → regstack-0.5.9}/tests/unit/test_wizard_oauth_writer.py +0 -0
@@ -61,4 +61,9 @@ jobs:
61
61
  with:
62
62
  name: dist
63
63
  path: dist/
64
- - uses: pypa/gh-action-pypi-publish@release/v1
64
+ # Pinned to a commit SHA, not the mutable `release/v1` branch — the
65
+ # publish job has `id-token: write`, so a tag/branch swap in the
66
+ # action repo would let an attacker push a malicious wheel under our
67
+ # OIDC identity. Update by resolving the latest SHA on `release/v1`
68
+ # and bumping the trailing comment to match.
69
+ - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
@@ -0,0 +1,374 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The
4
+ authoritative copy lives at
5
+ [`docs/changelog.md`](docs/changelog.md) and is rendered into the
6
+ Sphinx docs.
7
+
8
+ ## 0.5.9 — 2026-05-13
9
+
10
+ **`OAuthConfig.enforce_mfa_on_oauth_signin` is now wired.** The flag
11
+ has been on the config since the OAuth router shipped (0.3.0) and the
12
+ wizard surfaced it, but the callback never read it — operators who
13
+ flipped it on still got OAuth sign-ins that bypassed the SMS second
14
+ factor. The High #1 finding from the post-0.5.6 consistency audit.
15
+
16
+ Now, when the flag is `true` and the resolved user has SMS MFA set
17
+ up (`is_mfa_enabled=True` plus a `phone_number`):
18
+
19
+ - The OAuth callback sends the SMS code and stashes a short-lived
20
+ `login_mfa` pending JWT in the state row (instead of a session
21
+ token).
22
+ - `POST /oauth/exchange` returns `mfa_required=True` and
23
+ `mfa_pending_token=...` (with no `access_token`) so the SPA knows
24
+ to redirect to `/account/mfa-confirm`.
25
+ - The SPA's bundled `regstack.js` `oauth-complete` handler stashes
26
+ the pending token under `regstack.mfa_pending` (same key the
27
+ password-login MFA flow uses) and redirects.
28
+ - The user enters the SMS code and hits the existing
29
+ `POST /login/mfa-confirm` endpoint — same downstream path as the
30
+ password-login second factor.
31
+
32
+ Link flows (`mode="link"`) are exempt: the user was already
33
+ authenticated when they kicked off the link, so adding SMS friction
34
+ on top of an already-authenticated link operation has no
35
+ threat-model win.
36
+
37
+ The `ExchangeResponse` model grew two optional fields
38
+ (`mfa_required: bool = False`, `mfa_pending_token: str | None = None`)
39
+ and `access_token` is now defaulted to `""` so the MFA branch can
40
+ return cleanly. Existing handlers reading `access_token` keep
41
+ working — they just need to check `mfa_required` first.
42
+
43
+ ## 0.5.8 — 2026-05-13
44
+
45
+ Audit-driven consistency cleanup — small fixes across the API surface
46
+ flagged by the post-0.5.6 consistency review.
47
+
48
+ **Security**
49
+
50
+ - **`oauth.completion_ttl_seconds` is finally enforced.** The flag has
51
+ been on `OAuthConfig` since the OAuth router shipped, but the
52
+ callback never used it: a state row stayed valid for the full
53
+ `state_ttl_seconds` (300s default) between callback completion and
54
+ `/oauth/exchange`. Now `set_result_token(...)` bumps the row's
55
+ expiry down to `now + completion_ttl_seconds` (30s default), so the
56
+ blast radius of a stolen state_id post-callback is the documented
57
+ 30-second window. `OAuthStateRepoProtocol.set_result_token` grew an
58
+ optional `new_expires_at=` kwarg to make this atomic with the
59
+ token write.
60
+
61
+ **Changed (UserPublic surface)**
62
+
63
+ - `UserPublic` now serialises `updated_at` and
64
+ `tokens_invalidated_after`. SPAs comparing the latter against their
65
+ cached session JWT's `iat` can detect a forced sign-out after a
66
+ password / email change without an extra round-trip.
67
+
68
+ **Changed (hook payloads)**
69
+
70
+ - `oauth_signin_started` in `mode="link"` now carries the
71
+ authenticated `user=` kwarg, matching `oauth_signin_completed` and
72
+ `oauth_account_linked`. The `mode="signin"` call site stays without
73
+ `user=` (there isn't one yet — sign-in is what produces it).
74
+
75
+ **Internal**
76
+
77
+ - `OAuthConfig.completion_ttl_seconds` config field is now load-bearing
78
+ (was previously declared-but-unread).
79
+ - `MessageResponse` in `routers/oauth.py` deleted; the router now uses
80
+ the shared one from `routers/_schemas.py`. OpenAPI no longer carries
81
+ two identically-named schemas.
82
+ - `MongoOAuthStateRepo` / `SqlOAuthStateRepo` `set_result_token` grew
83
+ a `new_expires_at` parameter (default `None`, so existing callers
84
+ see no change).
85
+ - `MongoBlacklistRepo.purge_expired` switched from `$lte` to `$lt` to
86
+ match the rest of the `purge_expired` family across both backends.
87
+ Edge-instant tokens get one more microsecond of life — the
88
+ bulk-revoke check (which DOES use `<=`) is unchanged.
89
+ - Dead `create()` and `delete_by_id()` methods removed from
90
+ `MongoPendingRepo` — neither was in the protocol or the SQL impl,
91
+ and nothing in src or tests called them.
92
+ - OAuth `start` and `callback` endpoints now declare
93
+ `response_class=RedirectResponse` and `status_code=302`. OpenAPI
94
+ surfaces the redirect intent properly.
95
+ - Custom-claim JWT encoder in `routers/account.py` (email-change
96
+ token) now emits `iat` as a float instead of `int`, matching the
97
+ three other custom-claim encoders and the bulk-revoke contract.
98
+ - `routers/verify.py` `created_at` for resent pending registrations
99
+ now goes through `rs.clock.now()` instead of wall-clock
100
+ `datetime.now(UTC)` — keeps `FrozenClock`-driven tests
101
+ deterministic.
102
+ - `BaseUser.model_config = ConfigDict(extra="allow")` got a
103
+ comment explaining why it's the only model in the package that
104
+ doesn't `extra="forbid"`.
105
+
106
+ ## 0.5.7 — 2026-05-13
107
+
108
+ Documentation-only follow-up to 0.5.6.
109
+
110
+ - `docs/configuration.md` now documents the per-route `*_rate_limit`
111
+ family (added in 0.5.4) instead of pointing at
112
+ `login_max_per_minute` / `login_max_per_hour` as reserved future
113
+ fields.
114
+ - `docs/security.md` no longer references
115
+ `PasswordHasher.needs_rehash` (removed in 0.5.6). Replacement
116
+ guidance points hosts at `pwdlib.PasswordHash.verify_and_update`.
117
+ - Root `CHANGELOG.md` backfilled with 0.4.0 and 0.5.0 entries so it
118
+ matches `docs/changelog.md`.
119
+
120
+ ## 0.5.6 — 2026-05-13
121
+
122
+ Eleven days of security-review remediation, supply-chain hardening,
123
+ a full `mypy --strict` cleanup pass, and the per-route rate-limits
124
+ feature rolled up into a single release.
125
+
126
+ **Per-route IP rate limits.** Opt-in via the new `rate_limit` extra
127
+ (or a host-supplied `slowapi.Limiter`) plus any of the new
128
+ `RegStackConfig.*_rate_limit` fields (`login_rate_limit`,
129
+ `register_rate_limit`, `forgot_password_rate_limit`,
130
+ `reset_password_rate_limit`, `verify_rate_limit`,
131
+ `resend_verification_rate_limit`, `change_password_rate_limit`,
132
+ `change_email_rate_limit`, `confirm_email_change_rate_limit`,
133
+ `delete_account_rate_limit`). Each accepts a slowapi-syntax string
134
+ (`"5/minute"`, `"5/minute;20/hour"`). Empty / unset means no limit
135
+ on that route — `LockoutService` still defends `/login` against
136
+ credential stuffing per-account. When `*_rate_limit` strings are
137
+ configured but neither a `rate_limiter=` argument is passed nor
138
+ the `rate_limit` extra is installed, `RegStack.router` raises
139
+ `RuntimeError` on first access — failing closed beats silently
140
+ disabling the protection. Hosts remain responsible for
141
+ `app.state.limiter` and `app.add_exception_handler(RateLimitExceeded, ...)`;
142
+ slowapi owns the 429 response shape. The previously-reserved
143
+ `login_max_per_minute` / `login_max_per_hour` fields are kept for
144
+ back-compat but unwired.
145
+
146
+ **Security fixes.**
147
+
148
+ - JWT 401 detail now returns a static `"Invalid or expired token."`;
149
+ no longer leaks the pyjwt failure reason (signature mismatch /
150
+ expired / malformed / audience mismatch).
151
+ - OAuth sign-in now honours `allow_registration=False`. Previously,
152
+ `/register` respected the flag but the OAuth `_resolve_user`
153
+ "brand-new account" branch did not, creating accounts even when
154
+ self-service signup was disabled.
155
+ - Admin `DELETE /admin/users/{id}` now cascades `oauth_identities`,
156
+ matching the user-initiated `DELETE /account` path. Previously
157
+ left orphan rows that blocked re-registration of the same Google
158
+ subject.
159
+ - `POST /phone/start` and `DELETE /phone` now return 400 (not crash
160
+ with HTTP 500) for OAuth-only users who have no `hashed_password`.
161
+
162
+ **Breaking change — hook contracts.** `mfa_login_started` and
163
+ `phone_setup_started` no longer include the raw OTP code in their
164
+ kwargs. Hooks are best-effort observability and are the documented
165
+ integration surface for analytics / logging / Slack notifications,
166
+ so a plaintext OTP in `**kwargs` is a leak waiting to happen.
167
+ Hosts that subscribed to either event to take over SMS delivery
168
+ should migrate to a custom `SmsService` subclass — the supported
169
+ delivery override.
170
+
171
+ **Dependency floors raised for CVEs.**
172
+
173
+ - `pyjwt>=2.12.1` for CVE-2026-32597 (`crit` header bypass, CVSS 7.5).
174
+ - `cryptography>=46.0.7` added explicitly to the `oauth` extra for
175
+ CVE-2026-26007 (ECC subgroup attack on the JWKS code path, CVSS
176
+ 8.2) plus CVE-2026-34073 and CVE-2026-39892.
177
+ - `python-multipart>=0.0.26` for CVE-2026-40347 (DoS via oversized
178
+ multipart preamble).
179
+
180
+ **Supply chain.** `pypa/gh-action-pypi-publish` in `publish.yml`
181
+ pinned to a commit SHA instead of the mutable `release/v1` branch.
182
+ The publish job holds `id-token: write`, so a tag/branch swap
183
+ upstream would let an attacker push a malicious wheel under our
184
+ OIDC identity.
185
+
186
+ **Removed.** `PasswordHasher.needs_rehash` — called pwdlib's
187
+ non-existent `check_needs_rehash` and would `AttributeError` if
188
+ anyone invoked it. No callers in src or tests. If you were planning
189
+ to use it, call `pwdlib.PasswordHash.verify_and_update` directly.
190
+
191
+ **Internal.** 72 `mypy --strict` errors cleared across 35 files;
192
+ `inv lint` is now green end-to-end. Mongo
193
+ `BlacklistRepo.purge_expired` added (protocol parity with SQL).
194
+ `KNOWN_EVENTS` reconciled — 7 previously-undeclared events added
195
+ (`verification_requested`, `email_change_requested`, `email_changed`,
196
+ `phone_setup_started`, `mfa_login_started`, `mfa_enabled`,
197
+ `mfa_disabled`). `user_logged_out` now actually fires from
198
+ `routers/logout.py` (was listed in `KNOWN_EVENTS` but no router
199
+ emitted it).
200
+
201
+ ## 0.5.0 — 2026-05-02
202
+
203
+ **Theme designer.** `regstack theme design` opens a native pywebview
204
+ window with controls for every `--rs-*` CSS custom property and a
205
+ real-time preview of the bundled SSR widgets (sign-in form, success /
206
+ error banners, danger-zone button). Saving writes `regstack-theme.css`;
207
+ the designer round-trips values back into the form on next launch so
208
+ iteration is non-destructive. `--print-only` mode takes repeatable
209
+ `--var NAME=VALUE` pairs (with a `dark:` prefix for dark-scheme
210
+ overrides) and writes the file headlessly. Lives in
211
+ `regstack.wizard.theme_designer`; registered as a lazy Click subgroup
212
+ so `regstack init` / `doctor` don't pay the pywebview/uvicorn import
213
+ cost.
214
+
215
+ **Docs.** New "About the examples" convention block at the top of
216
+ `docs/index.md`. Every URL, email, smtp host, and admin command across
217
+ the docs now extrapolates from the same fictional app at
218
+ `app.example.com` with `<username>` / `<password>` placeholders.
219
+
220
+ ## 0.4.0 — 2026-05-02
221
+
222
+ **OAuth setup wizard.** `regstack oauth setup` opens a native webview
223
+ window that walks an operator through registering a Google OAuth 2.0
224
+ client and merges the credentials into `regstack.toml` +
225
+ `regstack.secrets.env` non-destructively (preserves comments, other
226
+ tables, unrelated keys). 12-step SPA inside a local-only 127.0.0.1
227
+ FastAPI server, gated by a per-launch random token. Each "Next" click
228
+ hits a server-side validator so the Write step can never be reached
229
+ with bad data. `--print-only` mode skips the GUI for headless / CI
230
+ use.
231
+
232
+ Three new base dependencies — `pywebview>=5.0`, `tomlkit>=0.13`,
233
+ `uvicorn[standard]>=0.29` — for the wizard's local server.
234
+ `pytest-playwright` added to the `dev` extra; new `inv test-e2e` task
235
+ chained into `inv test-all`.
236
+
237
+ ## 0.3.0 — 2026-04-30
238
+
239
+ **OAuth — Sign in with Google.** Opt-in via the new `oauth` extra
240
+ and `enable_oauth=True`. Five JSON endpoints, an SSR
241
+ `/account/oauth-complete` page, "Sign in with Google" button on the
242
+ login page, and a Connected-accounts panel on `/account/me`.
243
+
244
+ Schema migration `0002_oauth.py` creates `oauth_identities` +
245
+ `oauth_states` and makes `users.hashed_password` nullable
246
+ (OAuth-only users have no password). Roll forward via
247
+ `regstack migrate` or first-boot `install_schema()` — no manual
248
+ intervention.
249
+
250
+ Account-linking policy defaults to **refuse**: if a Google sign-in
251
+ arrives carrying an email that already belongs to a password-
252
+ registered user, the callback returns `?error=email_in_use` and the
253
+ user must sign in then explicitly link from `/account/me`. Hosts
254
+ that consciously accept the email-recycling threat for UX can flip
255
+ `oauth.auto_link_verified_emails = true`. See
256
+ [`docs/oauth.md`](https://regstack.readthedocs.io/en/latest/oauth.html)
257
+ and [`tasks/oauth-design.md`](https://github.com/jdrumgoole/regstack/blob/main/tasks/oauth-design.md)
258
+ for the full threat model.
259
+
260
+ **Migration**
261
+
262
+ - Install the new extra: `uv add 'regstack[oauth]'`.
263
+ - Set `enable_oauth = true` and provide `oauth.google_client_id` +
264
+ `oauth.google_client_secret`.
265
+ - Run `regstack migrate` (SQL backends only) or rely on
266
+ `install_schema()` at first boot.
267
+
268
+ `BaseUser.hashed_password` is now `str | None`. Code that imported
269
+ the field type explicitly will need to widen it.
270
+
271
+ ## 0.2.6 — 2026-04-28
272
+
273
+ Bug fix.
274
+
275
+ - **Fix:** `/admin/stats` reported `pending_registrations: 0` on
276
+ every SQL backend. The route reached into the Mongo repo's private
277
+ `_collection` attribute and silently fell back to `0` when the
278
+ attribute was absent. Added `count_unexpired(now=None)` to
279
+ `PendingRepoProtocol` with Mongo + SQL implementations and routed
280
+ through `rs.clock.now()` so the count respects the injected clock.
281
+ New parametrized integration test exercises the count on every
282
+ backend.
283
+
284
+ ## 0.2.5 — 2026-04-28
285
+
286
+ Bug fix + tooling.
287
+
288
+ - **Fix:** `regstack doctor` against a SQL backend crashed with
289
+ `asyncio.run() cannot be called from a running event loop`. The
290
+ schema check called `regstack.backends.sql.migrations.current()`,
291
+ which used `asyncio.run()` internally — invalid inside doctor's own
292
+ `asyncio.run`. Added `current_async()` and switched the doctor
293
+ command to use it. Sync `current()` is preserved for the migrate
294
+ CLI.
295
+ - **New:** `inv coverage [--no-html] [--fail-under=N]` runs the full
296
+ three-backend matrix under coverage and writes term + HTML reports.
297
+ Branch coverage is on by default.
298
+ - Test coverage uplift on the CLI: `cli/init.py` 14% → 88%,
299
+ `cli/doctor.py` 61% → 87%. Total: **85% → 87.1%**.
300
+
301
+ ## 0.2.4 — 2026-04-28
302
+
303
+ **Breaking** — back-compat shims removed:
304
+
305
+ - `RegStack.install_indexes()` (alias for `install_schema()`).
306
+ - `ObjectIdStr` alias for `IdStr` in `regstack.models._objectid`.
307
+ - Re-exports of `UserAlreadyExistsError`,
308
+ `PendingAlreadyExistsError`, `MfaVerifyOutcome`, and
309
+ `MfaVerifyResult` from `regstack.backends.mongo.repositories.*`.
310
+ Their canonical home is `regstack.backends.protocols`.
311
+
312
+ If you import any of these from the old paths, switch to:
313
+ - `RegStack.install_schema()`
314
+ - `from regstack.models._objectid import IdStr`
315
+ - `from regstack.backends.protocols import UserAlreadyExistsError`
316
+ (and friends).
317
+
318
+ The internal mongo `install_indexes(db, config)` function is unchanged.
319
+
320
+ ## 0.2.3 — 2026-04-28
321
+
322
+ Docs-only release. Restructured the API reference around the current
323
+ package layout (post multi-backend refactor) and added Google-style
324
+ docstrings (Args / Returns / Raises) to the public surface — RegStack,
325
+ JwtCodec, PasswordHasher, LockoutService, AuthDependencies,
326
+ HookRegistry, EmailService, SmsService, the router builders, and the
327
+ Clock implementations. Dataclass field docs moved to PEP 258
328
+ attribute docstrings. Sphinx builds clean under `-W` again.
329
+
330
+ ## 0.2.2 — 2026-04-28
331
+
332
+ Docs-only release. The README and Sphinx docs landing page now lead
333
+ with the same pitch (problem framing, "Why not just use…?" comparison
334
+ vs Auth0 / Clerk / Keycloak / fastapi-users) before diving into
335
+ architecture. Hyperlink density trimmed back: only major external
336
+ packages, products, and JWT (RFC 7519) are linked — Wikipedia trivia,
337
+ MDN basics, OWASP article links, and deep-dependency helper-class
338
+ docs were removed.
339
+
340
+ ## 0.2.1 — 2026-04-28
341
+
342
+ Hotfix for 0.2.0: `import regstack` failed on a base install because
343
+ several modules in the import path (`models/_objectid.py`,
344
+ `backends/protocols.py`, four routers, and the SQL `mfa_code_repo`)
345
+ had unconditional `from bson …` / `from regstack.backends.mongo …`
346
+ imports — but `pymongo` became an optional `mongo` extra in 0.2.0.
347
+ Added a CI smoketest that builds the wheel and imports it in a
348
+ no-extras venv, plus an in-process regression test that blocks `bson`
349
+ / `pymongo` via `sys.meta_path`.
350
+
351
+ ## 0.2.0 — 2026-04-28
352
+
353
+ Multi-backend support — SQLite (default), Postgres, MongoDB — switched
354
+ by `database_url` URL scheme. Bundled Alembic migrations for SQL
355
+ backends. Embedding API change: `RegStack(config=, db=)` →
356
+ `RegStack(config=, backend=None)`. README + core docs rewritten for
357
+ less-expert readers (problem framing, hyperlinks to external
358
+ standards, comparison vs Auth0/Clerk/Keycloak/fastapi-users).
359
+
360
+ See [`docs/changelog.md`](docs/changelog.md) for the full per-feature
361
+ breakdown.
362
+
363
+ ## 0.1.1 — 2026-04-27
364
+
365
+ - Rewrite README relative links as absolute URLs so they resolve on the
366
+ PyPI project page. README-only release.
367
+
368
+ ## 0.1.0 — 2026-04-27
369
+
370
+ First tagged release. Bundles M1–M6 from the development plan into a
371
+ single Apache-2.0 package on PyPI.
372
+
373
+ See [`docs/changelog.md`](docs/changelog.md) for the per-milestone
374
+ breakdown of M1 through M6.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: regstack
3
- Version: 0.5.0
3
+ Version: 0.5.9
4
4
  Summary: Embeddable user registration, login, and account management for FastAPI apps. SQLite / Postgres / MongoDB.
5
5
  Project-URL: Homepage, https://github.com/jdrumgoole/regstack
6
6
  Project-URL: Repository, https://github.com/jdrumgoole/regstack
@@ -27,8 +27,8 @@ Requires-Dist: jinja2>=3.1
27
27
  Requires-Dist: pwdlib[argon2]>=0.2.1
28
28
  Requires-Dist: pydantic-settings>=2.2
29
29
  Requires-Dist: pydantic>=2.6
30
- Requires-Dist: pyjwt>=2.8
31
- Requires-Dist: python-multipart>=0.0.9
30
+ Requires-Dist: pyjwt>=2.12.1
31
+ Requires-Dist: python-multipart>=0.0.26
32
32
  Requires-Dist: pywebview>=5.0
33
33
  Requires-Dist: sqlalchemy[asyncio]>=2.0
34
34
  Requires-Dist: tomlkit>=0.13
@@ -36,10 +36,11 @@ Requires-Dist: uvicorn[standard]>=0.29
36
36
  Provides-Extra: dev
37
37
  Requires-Dist: anyio>=4.3; extra == 'dev'
38
38
  Requires-Dist: asyncpg>=0.29; extra == 'dev'
39
+ Requires-Dist: cryptography>=46.0.7; extra == 'dev'
39
40
  Requires-Dist: httpx>=0.27; extra == 'dev'
40
41
  Requires-Dist: invoke>=2.2; extra == 'dev'
41
42
  Requires-Dist: mypy>=1.10; extra == 'dev'
42
- Requires-Dist: pyjwt[crypto]>=2.8; extra == 'dev'
43
+ Requires-Dist: pyjwt[crypto]>=2.12.1; extra == 'dev'
43
44
  Requires-Dist: pymongo>=4.9; extra == 'dev'
44
45
  Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
45
46
  Requires-Dist: pytest-cov>=5.0; extra == 'dev'
@@ -47,6 +48,7 @@ Requires-Dist: pytest-playwright>=0.5; extra == 'dev'
47
48
  Requires-Dist: pytest-xdist>=3.5; extra == 'dev'
48
49
  Requires-Dist: pytest>=8.0; extra == 'dev'
49
50
  Requires-Dist: ruff>=0.4; extra == 'dev'
51
+ Requires-Dist: slowapi>=0.1.9; extra == 'dev'
50
52
  Requires-Dist: uvicorn[standard]>=0.29; extra == 'dev'
51
53
  Provides-Extra: docs
52
54
  Requires-Dist: furo>=2024.1; extra == 'docs'
@@ -58,9 +60,12 @@ Requires-Dist: sphinx>=7.3; extra == 'docs'
58
60
  Provides-Extra: mongo
59
61
  Requires-Dist: pymongo>=4.9; extra == 'mongo'
60
62
  Provides-Extra: oauth
61
- Requires-Dist: pyjwt[crypto]>=2.8; extra == 'oauth'
63
+ Requires-Dist: cryptography>=46.0.7; extra == 'oauth'
64
+ Requires-Dist: pyjwt[crypto]>=2.12.1; extra == 'oauth'
62
65
  Provides-Extra: postgres
63
66
  Requires-Dist: asyncpg>=0.29; extra == 'postgres'
67
+ Provides-Extra: rate-limit
68
+ Requires-Dist: slowapi>=0.1.9; extra == 'rate-limit'
64
69
  Provides-Extra: ses
65
70
  Requires-Dist: aioboto3>=12.3; extra == 'ses'
66
71
  Provides-Extra: sns
@@ -54,9 +54,13 @@ multi-tenant deployments where a single FastAPI app serves multiple
54
54
  The backend is auto-built from `config.database_url` if not supplied
55
55
  explicitly. URL scheme decides:
56
56
 
57
- - `sqlite+aiosqlite://` → SQLAlchemy backend in SQLite mode.
58
- - `postgresql+asyncpg://` SQLAlchemy backend in Postgres mode.
59
- - `mongodb://` / `mongodb+srv://` Mongo backend.
57
+ - `sqlite+aiosqlite:///PATH` → SQLAlchemy backend in SQLite mode.
58
+ `PATH` is `./dbname.db` for a relative file, `/var/lib/app/dbname.db`
59
+ for an absolute file, or `:memory:` for an ephemeral in-process DB.
60
+ - `postgresql+asyncpg://<username>:<password>@dbhost.example.com:5432/dbname`
61
+ → SQLAlchemy backend in Postgres mode.
62
+ - `mongodb://<username>:<password>@dbhost.example.com:27017/dbname`
63
+ (or `mongodb+srv://…`) → Mongo backend.
60
64
 
61
65
  The façade exposes:
62
66