kstlib 2.2.0__tar.gz → 2.3.0__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 (189) hide show
  1. {kstlib-2.2.0/src/kstlib.egg-info → kstlib-2.3.0}/PKG-INFO +2 -2
  2. {kstlib-2.2.0 → kstlib-2.3.0}/pyproject.toml +1 -1
  3. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/__init__.py +5 -0
  4. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/config/__init__.py +2 -0
  5. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/config/loader.py +42 -0
  6. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/kstlib.conf.yml +23 -1
  7. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/limits.py +39 -0
  8. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/mail/builder.py +362 -6
  9. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/meta.py +1 -1
  10. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/websocket/manager.py +128 -5
  11. {kstlib-2.2.0 → kstlib-2.3.0/src/kstlib.egg-info}/PKG-INFO +2 -2
  12. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib.egg-info/requires.txt +1 -1
  13. {kstlib-2.2.0 → kstlib-2.3.0}/LICENSE.md +0 -0
  14. {kstlib-2.2.0 → kstlib-2.3.0}/MANIFEST.in +0 -0
  15. {kstlib-2.2.0 → kstlib-2.3.0}/README.md +0 -0
  16. {kstlib-2.2.0 → kstlib-2.3.0}/setup.cfg +0 -0
  17. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/__main__.py +0 -0
  18. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/alerts/__init__.py +0 -0
  19. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/alerts/channels/__init__.py +0 -0
  20. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/alerts/channels/base.py +0 -0
  21. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/alerts/channels/email.py +0 -0
  22. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/alerts/channels/slack.py +0 -0
  23. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/alerts/exceptions.py +0 -0
  24. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/alerts/manager.py +0 -0
  25. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/alerts/models.py +0 -0
  26. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/alerts/throttle.py +0 -0
  27. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/auth/__init__.py +0 -0
  28. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/auth/callback.py +0 -0
  29. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/auth/check.py +0 -0
  30. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/auth/config.py +0 -0
  31. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/auth/errors.py +0 -0
  32. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/auth/models.py +0 -0
  33. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/auth/providers/__init__.py +0 -0
  34. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/auth/providers/base.py +0 -0
  35. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/auth/providers/oauth2.py +0 -0
  36. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/auth/providers/oidc.py +0 -0
  37. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/auth/session.py +0 -0
  38. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/auth/token.py +0 -0
  39. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cache/__init__.py +0 -0
  40. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cache/decorator.py +0 -0
  41. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cache/strategies.py +0 -0
  42. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/__init__.py +0 -0
  43. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/app.py +0 -0
  44. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/__init__.py +0 -0
  45. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/auth/__init__.py +0 -0
  46. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/auth/check.py +0 -0
  47. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/auth/common.py +0 -0
  48. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/auth/login.py +0 -0
  49. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/auth/logout.py +0 -0
  50. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/auth/providers.py +0 -0
  51. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/auth/status.py +0 -0
  52. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/auth/token.py +0 -0
  53. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/auth/whoami.py +0 -0
  54. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/config.py +0 -0
  55. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/ops/__init__.py +0 -0
  56. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/ops/attach.py +0 -0
  57. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/ops/common.py +0 -0
  58. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/ops/list_sessions.py +0 -0
  59. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/ops/logs.py +0 -0
  60. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/ops/start.py +0 -0
  61. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/ops/status.py +0 -0
  62. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/ops/stop.py +0 -0
  63. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/rapi/__init__.py +0 -0
  64. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/rapi/call.py +0 -0
  65. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/rapi/list.py +0 -0
  66. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/rapi/show.py +0 -0
  67. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/secrets/__init__.py +0 -0
  68. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/secrets/common.py +0 -0
  69. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/secrets/decrypt.py +0 -0
  70. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/secrets/doctor.py +0 -0
  71. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/secrets/encrypt.py +0 -0
  72. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/commands/secrets/shred.py +0 -0
  73. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/cli/common.py +0 -0
  74. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/config/exceptions.py +0 -0
  75. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/config/export.py +0 -0
  76. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/config/sops.py +0 -0
  77. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/db/__init__.py +0 -0
  78. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/db/aiosqlcipher.py +0 -0
  79. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/db/cipher.py +0 -0
  80. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/db/database.py +0 -0
  81. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/db/exceptions.py +0 -0
  82. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/db/pool.py +0 -0
  83. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/helpers/__init__.py +0 -0
  84. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/helpers/exceptions.py +0 -0
  85. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/helpers/time_trigger.py +0 -0
  86. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/logging/__init__.py +0 -0
  87. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/logging/manager.py +0 -0
  88. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/mail/__init__.py +0 -0
  89. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/mail/exceptions.py +0 -0
  90. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/mail/filesystem.py +0 -0
  91. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/mail/transport.py +0 -0
  92. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/mail/transports/__init__.py +0 -0
  93. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/mail/transports/gmail.py +0 -0
  94. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/mail/transports/resend.py +0 -0
  95. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/mail/transports/ses.py +0 -0
  96. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/mail/transports/smtp.py +0 -0
  97. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/metrics/__init__.py +0 -0
  98. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/metrics/decorators.py +0 -0
  99. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/metrics/exceptions.py +0 -0
  100. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/monitoring/__init__.py +0 -0
  101. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/monitoring/_styles.py +0 -0
  102. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/monitoring/cell.py +0 -0
  103. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/monitoring/config.py +0 -0
  104. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/monitoring/delivery.py +0 -0
  105. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/monitoring/exceptions.py +0 -0
  106. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/monitoring/image.py +0 -0
  107. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/monitoring/kv.py +0 -0
  108. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/monitoring/list.py +0 -0
  109. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/monitoring/metric.py +0 -0
  110. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/monitoring/monitoring.py +0 -0
  111. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/monitoring/renderer.py +0 -0
  112. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/monitoring/service.py +0 -0
  113. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/monitoring/table.py +0 -0
  114. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/monitoring/types.py +0 -0
  115. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/ops/__init__.py +0 -0
  116. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/ops/base.py +0 -0
  117. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/ops/container.py +0 -0
  118. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/ops/exceptions.py +0 -0
  119. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/ops/manager.py +0 -0
  120. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/ops/models.py +0 -0
  121. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/ops/tmux.py +0 -0
  122. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/ops/validators.py +0 -0
  123. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/pipeline/__init__.py +0 -0
  124. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/pipeline/base.py +0 -0
  125. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/pipeline/exceptions.py +0 -0
  126. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/pipeline/models.py +0 -0
  127. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/pipeline/runner.py +0 -0
  128. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/pipeline/steps/__init__.py +0 -0
  129. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/pipeline/steps/_base.py +0 -0
  130. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/pipeline/steps/callable.py +0 -0
  131. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/pipeline/steps/python.py +0 -0
  132. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/pipeline/steps/shell.py +0 -0
  133. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/pipeline/validators.py +0 -0
  134. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/py.typed +0 -0
  135. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/rapi/__init__.py +0 -0
  136. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/rapi/client.py +0 -0
  137. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/rapi/config.py +0 -0
  138. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/rapi/credentials.py +0 -0
  139. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/rapi/exceptions.py +0 -0
  140. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/resilience/__init__.py +0 -0
  141. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/resilience/circuit_breaker.py +0 -0
  142. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/resilience/exceptions.py +0 -0
  143. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/resilience/heartbeat.py +0 -0
  144. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/resilience/rate_limiter.py +0 -0
  145. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/resilience/shutdown.py +0 -0
  146. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/resilience/watchdog.py +0 -0
  147. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/secrets/__init__.py +0 -0
  148. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/secrets/exceptions.py +0 -0
  149. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/secrets/models.py +0 -0
  150. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/secrets/providers/__init__.py +0 -0
  151. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/secrets/providers/base.py +0 -0
  152. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/secrets/providers/environment.py +0 -0
  153. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/secrets/providers/keyring.py +0 -0
  154. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/secrets/providers/kms.py +0 -0
  155. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/secrets/providers/kwargs.py +0 -0
  156. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/secrets/providers/sops.py +0 -0
  157. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/secrets/resolver.py +0 -0
  158. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/secrets/sensitive.py +0 -0
  159. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/secure/__init__.py +0 -0
  160. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/secure/fs.py +0 -0
  161. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/secure/permissions.py +0 -0
  162. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/ssl.py +0 -0
  163. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/transform/__init__.py +0 -0
  164. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/transform/chain.py +0 -0
  165. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/transform/config.py +0 -0
  166. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/transform/exceptions.py +0 -0
  167. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/transform/primitives.py +0 -0
  168. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/transform/validators.py +0 -0
  169. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/ui/__init__.py +0 -0
  170. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/ui/exceptions.py +0 -0
  171. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/ui/panels.py +0 -0
  172. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/ui/spinner.py +0 -0
  173. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/ui/tables.py +0 -0
  174. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/utils/__init__.py +0 -0
  175. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/utils/dict.py +0 -0
  176. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/utils/formatting.py +0 -0
  177. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/utils/http_trace.py +0 -0
  178. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/utils/lazy.py +0 -0
  179. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/utils/secure_delete.py +0 -0
  180. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/utils/serialization.py +0 -0
  181. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/utils/text.py +0 -0
  182. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/utils/validators.py +0 -0
  183. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/websocket/__init__.py +0 -0
  184. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/websocket/exceptions.py +0 -0
  185. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib/websocket/models.py +0 -0
  186. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib.egg-info/SOURCES.txt +0 -0
  187. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib.egg-info/dependency_links.txt +0 -0
  188. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib.egg-info/entry_points.txt +0 -0
  189. {kstlib-2.2.0 → kstlib-2.3.0}/src/kstlib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kstlib
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Config-driven helpers for Python projects (dynamic config, secure secrets, preset logging, and more…)
5
5
  Author-email: Michel TRUONG <michel.truong@gmail.com>
6
6
  Maintainer-email: Michel TRUONG <michel.truong@gmail.com>
@@ -42,7 +42,7 @@ Requires-Dist: websockets<16,>=15.0
42
42
  Requires-Dist: jinja2<4,>=3.1.5
43
43
  Requires-Dist: humanize<5,>=4.11
44
44
  Requires-Dist: httpx<1,>=0.28
45
- Requires-Dist: authlib<2,>=1.6.9
45
+ Requires-Dist: authlib<2,>=1.6.11
46
46
  Requires-Dist: pendulum<4,>=3.0
47
47
  Requires-Dist: cryptography>=46.0.7
48
48
  Requires-Dist: requests>=2.33.0
@@ -56,7 +56,7 @@ dependencies = [
56
56
 
57
57
  # --- HTTP Client & Auth ---
58
58
  "httpx>=0.28,<1", # Modern async HTTP client (OAuth2/OIDC flows)
59
- "authlib>=1.6.9,<2", # OAuth2/OIDC client + JWT signature verification
59
+ "authlib>=1.6.11,<2", # OAuth2/OIDC client + JWT signature verification (GHSA-jj8c-mmj3-mmgv: CSRF in cache-backed OAuth state)
60
60
 
61
61
  # --- Time & Scheduling ---
62
62
  "pendulum>=3.0,<4", # Modern datetime library with timezone support
@@ -69,6 +69,7 @@ __all__ = [
69
69
  "ops",
70
70
  "pipeline",
71
71
  "rapi",
72
+ "reload_config",
72
73
  "require_config",
73
74
  "resilience",
74
75
  "secrets",
@@ -117,6 +118,7 @@ _LAZY_IMPORTS: dict[str, tuple[str, str]] = {
117
118
  "load_config": ("kstlib.config.loader", "load_config"),
118
119
  "load_from_env": ("kstlib.config.loader", "load_from_env"),
119
120
  "load_from_file": ("kstlib.config.loader", "load_from_file"),
121
+ "reload_config": ("kstlib.config.loader", "reload_config"),
120
122
  "require_config": ("kstlib.config.loader", "require_config"),
121
123
  # Logging
122
124
  "LogManager": ("kstlib.logging", "LogManager"),
@@ -250,6 +252,9 @@ if TYPE_CHECKING:
250
252
  from kstlib.config.loader import (
251
253
  load_from_file as load_from_file,
252
254
  )
255
+ from kstlib.config.loader import (
256
+ reload_config as reload_config,
257
+ )
253
258
  from kstlib.config.loader import (
254
259
  require_config as require_config,
255
260
  )
@@ -34,6 +34,7 @@ from kstlib.config.loader import (
34
34
  load_config,
35
35
  load_from_env,
36
36
  load_from_file,
37
+ reload_config,
37
38
  require_config,
38
39
  )
39
40
  from kstlib.config.sops import (
@@ -71,6 +72,7 @@ __all__ = [
71
72
  "load_config",
72
73
  "load_from_env",
73
74
  "load_from_file",
75
+ "reload_config",
74
76
  "require_config",
75
77
  "reset_decryptor",
76
78
  ]
@@ -1064,6 +1064,47 @@ def clear_config() -> None:
1064
1064
  _default_loader.cache = None
1065
1065
 
1066
1066
 
1067
+ def reload_config(filename: str = CONFIG_FILENAME) -> Box:
1068
+ """Force-reload the singleton configuration from disk.
1069
+
1070
+ Equivalent to ``clear_config()`` followed by ``get_config()``, but explicit
1071
+ and discoverable in a single import. Designed for interactive sessions
1072
+ (Jupyter, REPL) where the underlying YAML files have been edited and the
1073
+ cached config needs to be refreshed without restarting the kernel.
1074
+
1075
+ Args:
1076
+ filename: Config filename to search for. Defaults to
1077
+ ``kstlib.conf.yml``.
1078
+
1079
+ Returns:
1080
+ Freshly loaded ``Box`` configuration object. The singleton cache is
1081
+ updated in-place, so subsequent ``get_config()`` calls return the same
1082
+ fresh object.
1083
+
1084
+ Raises:
1085
+ ConfigFileNotFoundError: If no configuration file is found in any
1086
+ search location.
1087
+
1088
+ Note:
1089
+ When to use which:
1090
+
1091
+ - ``reload_config()``: explicit one-shot refresh (Jupyter/REPL).
1092
+ - ``get_config(force_reload=True)``: same effect, but the intent is
1093
+ hidden in a kwarg.
1094
+ - ``clear_config()``: only flushes the cache; the next
1095
+ ``get_config()`` call triggers the actual reload. Useful in tests
1096
+ that want to isolate the cache boundary.
1097
+
1098
+ Examples:
1099
+ >>> from kstlib.config import reload_config
1100
+ >>> cfg = reload_config() # doctest: +SKIP
1101
+ >>> cfg.mail.default # doctest: +SKIP
1102
+ 'corporate'
1103
+
1104
+ """
1105
+ return get_config(filename=filename, force_reload=True)
1106
+
1107
+
1067
1108
  __all__ = [
1068
1109
  "AutoDiscoveryConfig",
1069
1110
  "ConfigLoader",
@@ -1072,5 +1113,6 @@ __all__ = [
1072
1113
  "load_config",
1073
1114
  "load_from_env",
1074
1115
  "load_from_file",
1116
+ "reload_config",
1075
1117
  "require_config",
1076
1118
  ]
@@ -358,6 +358,21 @@ mail:
358
358
  # Leave null to force callers to pass transport= or preset= explicitly.
359
359
  default: null
360
360
 
361
+ # SSL/TLS configuration for mail transports.
362
+ #
363
+ # Values here override the root ``ssl:`` section (bottom of this file)
364
+ # for mail only, and are themselves overridden by ``ssl_verify`` and
365
+ # ``ssl_ca_bundle`` keys set inside an individual preset. Each key
366
+ # cascades independently: you can set ``verify: false`` here and still
367
+ # provide ``ssl_ca_bundle`` at the preset level.
368
+ #
369
+ # Security: setting ``verify: false`` at any level emits a WARNING log
370
+ # at transport build time. Prefer ``ca_bundle: /path/to/private-ca.pem``
371
+ # for internal PKI rather than disabling verification outright.
372
+ ssl:
373
+ verify: true
374
+ ca_bundle: null
375
+
361
376
  # Named transport presets. Each preset declares a "transport" field
362
377
  # (smtp or resend) and backend-specific parameters. Define your own
363
378
  # presets here and reference them via MailBuilder(preset="name").
@@ -368,11 +383,18 @@ mail:
368
383
  # transport: smtp
369
384
  # host: smtp-secure.corp.local
370
385
  # port: 25
371
- # login: svc_user
386
+ # login: svc_mail
372
387
  # password: "secret"
373
388
  # starttls: false
374
389
  # ssl: false
375
390
  # timeout: 30
391
+ # # Optional SSL overrides for this preset (highest priority in the cascade):
392
+ # ssl_verify: false
393
+ # ssl_ca_bundle: /etc/ssl/certs/corp-ca.pem
394
+ # # Optional envelope defaults (sender / reply_to only, never to/cc/bcc):
395
+ # defaults:
396
+ # sender: "Service Notifications <notify@corp.local>"
397
+ # reply_to: "Service Notifications <notify@corp.local>"
376
398
  #
377
399
  # transactional:
378
400
  # transport: resend
@@ -200,6 +200,18 @@ HARD_MAX_WS_RECONNECT_CHECK = 60.0
200
200
  HARD_MIN_WS_DISCONNECT_MARGIN = 60.0
201
201
  HARD_MAX_WS_DISCONNECT_MARGIN = 3600.0
202
202
 
203
+ #: WebSocket stable connection time bounds (seconds) - delay before resetting reconnect counter.
204
+ HARD_MIN_WS_STABLE_CONNECTION_TIME = 10.0
205
+ HARD_MAX_WS_STABLE_CONNECTION_TIME = 300.0
206
+
207
+ #: WebSocket server unavailable (code 1013) backoff bounds (seconds).
208
+ HARD_MIN_WS_SERVER_UNAVAILABLE_DELAY = 10.0
209
+ HARD_MAX_WS_SERVER_UNAVAILABLE_DELAY = 300.0
210
+
211
+ #: WebSocket disconnect alert interval bounds (seconds) - throttle window for alerts.
212
+ HARD_MIN_WS_DISCONNECT_ALERT_INTERVAL = 30.0
213
+ HARD_MAX_WS_DISCONNECT_ALERT_INTERVAL = 3600.0
214
+
203
215
  #: Maximum endpoint reference length (api.endpoint format) - protects against DoS.
204
216
  HARD_MAX_ENDPOINT_REF_LENGTH = 256
205
217
 
@@ -272,6 +284,9 @@ DEFAULT_WS_QUEUE_SIZE = 1000 # messages
272
284
  DEFAULT_WS_DISCONNECT_CHECK = 10.0 # seconds
273
285
  DEFAULT_WS_RECONNECT_CHECK = 5.0 # seconds
274
286
  DEFAULT_WS_DISCONNECT_MARGIN = 300.0 # seconds (5 minutes before 24h limit)
287
+ DEFAULT_WS_STABLE_CONNECTION_TIME = 60.0 # seconds before resetting reconnect counter
288
+ DEFAULT_WS_SERVER_UNAVAILABLE_DELAY = 30.0 # seconds to wait on code 1013
289
+ DEFAULT_WS_DISCONNECT_ALERT_INTERVAL = 300.0 # seconds between throttled alerts
275
290
 
276
291
  DEFAULT_PIPELINE_TIMEOUT = 300.0 # seconds (5 minutes)
277
292
  DEFAULT_PIPELINE_ON_ERROR = "fail_fast"
@@ -925,6 +940,9 @@ class WebSocketLimits:
925
940
  disconnect_check_interval: Seconds between should_disconnect checks.
926
941
  reconnect_check_interval: Seconds between should_reconnect checks.
927
942
  disconnect_margin: Seconds before platform limit to disconnect.
943
+ stable_connection_time: Seconds of stable connection before resetting reconnect counter.
944
+ server_unavailable_delay: Seconds to wait on server code 1013 before reconnect.
945
+ disconnect_alert_interval: Seconds between throttled disconnect alerts.
928
946
 
929
947
  """
930
948
 
@@ -938,6 +956,9 @@ class WebSocketLimits:
938
956
  disconnect_check_interval: float
939
957
  reconnect_check_interval: float
940
958
  disconnect_margin: float
959
+ stable_connection_time: float
960
+ server_unavailable_delay: float
961
+ disconnect_alert_interval: float
941
962
 
942
963
 
943
964
  def get_websocket_limits(
@@ -1023,6 +1044,24 @@ def get_websocket_limits(
1023
1044
  HARD_MIN_WS_DISCONNECT_MARGIN,
1024
1045
  HARD_MAX_WS_DISCONNECT_MARGIN,
1025
1046
  ),
1047
+ stable_connection_time=_parse_float_config(
1048
+ _get_nested(config, "websocket", "reconnect", "stable_connection_time"),
1049
+ DEFAULT_WS_STABLE_CONNECTION_TIME,
1050
+ HARD_MIN_WS_STABLE_CONNECTION_TIME,
1051
+ HARD_MAX_WS_STABLE_CONNECTION_TIME,
1052
+ ),
1053
+ server_unavailable_delay=_parse_float_config(
1054
+ _get_nested(config, "websocket", "reconnect", "server_unavailable_delay"),
1055
+ DEFAULT_WS_SERVER_UNAVAILABLE_DELAY,
1056
+ HARD_MIN_WS_SERVER_UNAVAILABLE_DELAY,
1057
+ HARD_MAX_WS_SERVER_UNAVAILABLE_DELAY,
1058
+ ),
1059
+ disconnect_alert_interval=_parse_float_config(
1060
+ _get_nested(config, "websocket", "alert", "disconnect_interval"),
1061
+ DEFAULT_WS_DISCONNECT_ALERT_INTERVAL,
1062
+ HARD_MIN_WS_DISCONNECT_ALERT_INTERVAL,
1063
+ HARD_MAX_WS_DISCONNECT_ALERT_INTERVAL,
1064
+ ),
1026
1065
  )
1027
1066
 
1028
1067