svc-infra 0.1.633__tar.gz → 0.1.635__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.

Potentially problematic release.


This version of svc-infra might be problematic. Click here for more details.

Files changed (349) hide show
  1. {svc_infra-0.1.633 → svc_infra-0.1.635}/PKG-INFO +1 -1
  2. {svc_infra-0.1.633 → svc_infra-0.1.635}/pyproject.toml +2 -1
  3. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/middleware/timeout.py +6 -2
  4. svc_infra-0.1.635/src/svc_infra/docs/admin.md +425 -0
  5. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/http/client.py +10 -2
  6. {svc_infra-0.1.633 → svc_infra-0.1.635}/README.md +0 -0
  7. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/__init__.py +0 -0
  8. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/apf_payments/README.md +0 -0
  9. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/apf_payments/__init__.py +0 -0
  10. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/apf_payments/alembic.py +0 -0
  11. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/apf_payments/models.py +0 -0
  12. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/apf_payments/provider/__init__.py +0 -0
  13. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/apf_payments/provider/aiydan.py +0 -0
  14. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/apf_payments/provider/base.py +0 -0
  15. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/apf_payments/provider/registry.py +0 -0
  16. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/apf_payments/provider/stripe.py +0 -0
  17. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/apf_payments/schemas.py +0 -0
  18. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/apf_payments/service.py +0 -0
  19. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/apf_payments/settings.py +0 -0
  20. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/__init__.py +0 -0
  21. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/__init__.py +0 -0
  22. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/admin/__init__.py +0 -0
  23. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/admin/add.py +0 -0
  24. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/apf_payments/__init__.py +0 -0
  25. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/apf_payments/router.py +0 -0
  26. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/apf_payments/setup.py +0 -0
  27. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/__init__.py +0 -0
  28. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/_cookies.py +0 -0
  29. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/add.py +0 -0
  30. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/gaurd.py +0 -0
  31. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/mfa/__init__.py +0 -0
  32. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/mfa/models.py +0 -0
  33. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/mfa/pre_auth.py +0 -0
  34. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/mfa/router.py +0 -0
  35. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/mfa/security.py +0 -0
  36. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/mfa/utils.py +0 -0
  37. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/mfa/verify.py +0 -0
  38. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/policy.py +0 -0
  39. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/providers.py +0 -0
  40. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/routers/__init__.py +0 -0
  41. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/routers/account.py +0 -0
  42. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/routers/apikey_router.py +0 -0
  43. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/routers/oauth_router.py +0 -0
  44. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/routers/session_router.py +0 -0
  45. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/security.py +0 -0
  46. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/sender.py +0 -0
  47. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/settings.py +0 -0
  48. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/auth/state.py +0 -0
  49. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/billing/router.py +0 -0
  50. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/billing/setup.py +0 -0
  51. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/cache/__init__.py +0 -0
  52. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/cache/add.py +0 -0
  53. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/db/__init__.py +0 -0
  54. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/db/http.py +0 -0
  55. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/db/nosql/__init__.py +0 -0
  56. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/db/nosql/mongo/__init__.py +0 -0
  57. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/db/nosql/mongo/add.py +0 -0
  58. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/db/nosql/mongo/crud_router.py +0 -0
  59. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/db/nosql/mongo/health.py +0 -0
  60. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/db/sql/README.md +0 -0
  61. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/db/sql/__init__.py +0 -0
  62. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/db/sql/add.py +0 -0
  63. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/db/sql/crud_router.py +0 -0
  64. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/db/sql/health.py +0 -0
  65. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/db/sql/session.py +0 -0
  66. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/db/sql/users.py +0 -0
  67. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/dependencies/ratelimit.py +0 -0
  68. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/docs/__init__.py +0 -0
  69. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/docs/add.py +0 -0
  70. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/docs/landing.py +0 -0
  71. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/docs/scoped.py +0 -0
  72. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/dual/__init__.py +0 -0
  73. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/dual/dualize.py +0 -0
  74. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/dual/protected.py +0 -0
  75. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/dual/public.py +0 -0
  76. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/dual/router.py +0 -0
  77. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/dual/utils.py +0 -0
  78. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/dx.py +0 -0
  79. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/ease.py +0 -0
  80. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/http/__init__.py +0 -0
  81. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/http/concurrency.py +0 -0
  82. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/http/conditional.py +0 -0
  83. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/http/deprecation.py +0 -0
  84. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/middleware/__init__.py +0 -0
  85. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/middleware/debug.py +0 -0
  86. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/middleware/errors/__init__.py +0 -0
  87. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/middleware/errors/catchall.py +0 -0
  88. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/middleware/errors/exceptions.py +0 -0
  89. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/middleware/errors/handlers.py +0 -0
  90. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/middleware/graceful_shutdown.py +0 -0
  91. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/middleware/idempotency.py +0 -0
  92. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/middleware/idempotency_store.py +0 -0
  93. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/middleware/optimistic_lock.py +0 -0
  94. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/middleware/ratelimit.py +0 -0
  95. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/middleware/ratelimit_store.py +0 -0
  96. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/middleware/request_id.py +0 -0
  97. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/middleware/request_size_limit.py +0 -0
  98. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/openapi/__init__.py +0 -0
  99. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/openapi/apply.py +0 -0
  100. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/openapi/conventions.py +0 -0
  101. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/openapi/models.py +0 -0
  102. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/openapi/mutators.py +0 -0
  103. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/openapi/pipeline.py +0 -0
  104. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/openapi/responses.py +0 -0
  105. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/openapi/security.py +0 -0
  106. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/ops/add.py +0 -0
  107. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/pagination.py +0 -0
  108. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/paths/__init__.py +0 -0
  109. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/paths/auth.py +0 -0
  110. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/paths/generic.py +0 -0
  111. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/paths/prefix.py +0 -0
  112. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/paths/user.py +0 -0
  113. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/routers/__init__.py +0 -0
  114. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/routers/ping.py +0 -0
  115. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/setup.py +0 -0
  116. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/tenancy/add.py +0 -0
  117. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/api/fastapi/tenancy/context.py +0 -0
  118. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/app/README.md +0 -0
  119. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/app/__init__.py +0 -0
  120. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/app/env.py +0 -0
  121. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/app/logging/__init__.py +0 -0
  122. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/app/logging/add.py +0 -0
  123. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/app/logging/filter.py +0 -0
  124. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/app/logging/formats.py +0 -0
  125. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/app/root.py +0 -0
  126. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/billing/__init__.py +0 -0
  127. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/billing/async_service.py +0 -0
  128. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/billing/jobs.py +0 -0
  129. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/billing/models.py +0 -0
  130. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/billing/quotas.py +0 -0
  131. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/billing/schemas.py +0 -0
  132. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/billing/service.py +0 -0
  133. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/bundled_docs/README.md +0 -0
  134. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/bundled_docs/__init__.py +0 -0
  135. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/bundled_docs/getting-started.md +0 -0
  136. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cache/README.md +0 -0
  137. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cache/__init__.py +0 -0
  138. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cache/add.py +0 -0
  139. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cache/backend.py +0 -0
  140. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cache/decorators.py +0 -0
  141. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cache/demo.py +0 -0
  142. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cache/keys.py +0 -0
  143. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cache/recache.py +0 -0
  144. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cache/resources.py +0 -0
  145. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cache/tags.py +0 -0
  146. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cache/ttl.py +0 -0
  147. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cache/utils.py +0 -0
  148. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/__init__.py +0 -0
  149. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/__main__.py +0 -0
  150. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/__init__.py +0 -0
  151. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/db/__init__.py +0 -0
  152. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/db/nosql/__init__.py +0 -0
  153. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/db/nosql/mongo/README.md +0 -0
  154. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/db/nosql/mongo/__init__.py +0 -0
  155. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/db/nosql/mongo/mongo_cmds.py +0 -0
  156. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/db/nosql/mongo/mongo_scaffold_cmds.py +0 -0
  157. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/db/sql/__init__.py +0 -0
  158. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/db/sql/alembic_cmds.py +0 -0
  159. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/db/sql/sql_export_cmds.py +0 -0
  160. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/db/sql/sql_scaffold_cmds.py +0 -0
  161. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/docs/docs_cmds.py +0 -0
  162. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/dx/__init__.py +0 -0
  163. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/dx/dx_cmds.py +0 -0
  164. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/help.py +0 -0
  165. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/jobs/__init__.py +0 -0
  166. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/jobs/jobs_cmds.py +0 -0
  167. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/obs/__init__.py +0 -0
  168. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/obs/obs_cmds.py +0 -0
  169. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/sdk/__init__.py +0 -0
  170. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/cmds/sdk/sdk_cmds.py +0 -0
  171. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/foundation/__init__.py +0 -0
  172. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/foundation/runner.py +0 -0
  173. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/cli/foundation/typer_bootstrap.py +0 -0
  174. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/data/add.py +0 -0
  175. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/data/backup.py +0 -0
  176. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/data/erasure.py +0 -0
  177. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/data/fixtures.py +0 -0
  178. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/data/retention.py +0 -0
  179. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/__init__.py +0 -0
  180. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/crud_schema.py +0 -0
  181. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/inbox.py +0 -0
  182. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/__init__.py +0 -0
  183. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/base.py +0 -0
  184. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/constants.py +0 -0
  185. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/core.py +0 -0
  186. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/indexes.py +0 -0
  187. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/management.py +0 -0
  188. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/mongo/README.md +0 -0
  189. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/mongo/__init__.py +0 -0
  190. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/mongo/client.py +0 -0
  191. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/mongo/settings.py +0 -0
  192. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/mongo/templates/__init__.py +0 -0
  193. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/mongo/templates/documents.py.tmpl +0 -0
  194. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/mongo/templates/resources.py.tmpl +0 -0
  195. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/mongo/templates/schemas.py.tmpl +0 -0
  196. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/repository.py +0 -0
  197. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/resource.py +0 -0
  198. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/scaffold.py +0 -0
  199. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/service.py +0 -0
  200. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/service_with_hooks.py +0 -0
  201. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/types.py +0 -0
  202. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/nosql/utils.py +0 -0
  203. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/outbox.py +0 -0
  204. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/README.md +0 -0
  205. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/__init__.py +0 -0
  206. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/apikey.py +0 -0
  207. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/authref.py +0 -0
  208. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/base.py +0 -0
  209. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/constants.py +0 -0
  210. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/core.py +0 -0
  211. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/management.py +0 -0
  212. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/repository.py +0 -0
  213. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/resource.py +0 -0
  214. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/scaffold.py +0 -0
  215. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/service.py +0 -0
  216. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/service_with_hooks.py +0 -0
  217. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/templates/__init__.py +0 -0
  218. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/templates/models_schemas/__init__.py +0 -0
  219. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/templates/models_schemas/auth/models.py.tmpl +0 -0
  220. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/templates/models_schemas/auth/schemas.py.tmpl +0 -0
  221. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/templates/models_schemas/entity/models.py.tmpl +0 -0
  222. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/templates/models_schemas/entity/schemas.py.tmpl +0 -0
  223. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/templates/setup/__init__.py +0 -0
  224. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/templates/setup/alembic.ini.tmpl +0 -0
  225. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/templates/setup/env_async.py.tmpl +0 -0
  226. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/templates/setup/env_sync.py.tmpl +0 -0
  227. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/templates/setup/script.py.mako.tmpl +0 -0
  228. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/tenant.py +0 -0
  229. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/types.py +0 -0
  230. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/uniq.py +0 -0
  231. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/uniq_hooks.py +0 -0
  232. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/utils.py +0 -0
  233. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/sql/versioning.py +0 -0
  234. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/db/utils.py +0 -0
  235. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/acceptance-matrix.md +0 -0
  236. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/acceptance.md +0 -0
  237. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/adr/0002-background-jobs-and-scheduling.md +0 -0
  238. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/adr/0003-webhooks-framework.md +0 -0
  239. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/adr/0004-tenancy-model.md +0 -0
  240. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/adr/0005-data-lifecycle.md +0 -0
  241. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/adr/0006-ops-slos-and-metrics.md +0 -0
  242. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/adr/0007-docs-and-sdks.md +0 -0
  243. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/adr/0008-billing-primitives.md +0 -0
  244. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/adr/0009-acceptance-harness.md +0 -0
  245. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/adr/0010-timeouts-and-resource-limits.md +0 -0
  246. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/adr/0011-admin-scope-and-impersonation.md +0 -0
  247. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/api.md +0 -0
  248. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/auth.md +0 -0
  249. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/billing.md +0 -0
  250. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/cache.md +0 -0
  251. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/cli.md +0 -0
  252. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/contributing.md +0 -0
  253. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/data-lifecycle.md +0 -0
  254. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/database.md +0 -0
  255. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/docs-and-sdks.md +0 -0
  256. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/environment.md +0 -0
  257. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/getting-started.md +0 -0
  258. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/idempotency.md +0 -0
  259. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/jobs.md +0 -0
  260. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/observability.md +0 -0
  261. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/ops.md +0 -0
  262. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/rate-limiting.md +0 -0
  263. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/repo-review.md +0 -0
  264. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/security.md +0 -0
  265. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/tenancy.md +0 -0
  266. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/timeouts-and-resource-limits.md +0 -0
  267. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/docs/webhooks.md +0 -0
  268. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/dx/add.py +0 -0
  269. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/dx/changelog.py +0 -0
  270. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/dx/checks.py +0 -0
  271. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/http/__init__.py +0 -0
  272. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/jobs/builtins/outbox_processor.py +0 -0
  273. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/jobs/builtins/webhook_delivery.py +0 -0
  274. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/jobs/easy.py +0 -0
  275. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/jobs/loader.py +0 -0
  276. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/jobs/queue.py +0 -0
  277. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/jobs/redis_queue.py +0 -0
  278. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/jobs/runner.py +0 -0
  279. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/jobs/scheduler.py +0 -0
  280. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/jobs/worker.py +0 -0
  281. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/mcp/__init__.py +0 -0
  282. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/mcp/svc_infra_mcp.py +0 -0
  283. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/README.md +0 -0
  284. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/__init__.py +0 -0
  285. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/add.py +0 -0
  286. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/cloud_dash.py +0 -0
  287. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/grafana/dashboards/http-overview.json +0 -0
  288. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/metrics/__init__.py +0 -0
  289. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/metrics/asgi.py +0 -0
  290. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/metrics/base.py +0 -0
  291. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/metrics/http.py +0 -0
  292. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/metrics/sqlalchemy.py +0 -0
  293. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/metrics.py +0 -0
  294. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/__init__.py +0 -0
  295. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/compose_cloud/__init__.py +0 -0
  296. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/compose_cloud/templates/__init__.py +0 -0
  297. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/compose_cloud/templates/agent.yaml.tmpl +0 -0
  298. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/compose_cloud/templates/docker-compose.cloud.yml.tmpl +0 -0
  299. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/grafana/__init__.py +0 -0
  300. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/grafana/dashboards/00_overview.json +0 -0
  301. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/grafana/dashboards/10_http.json +0 -0
  302. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/grafana/dashboards/20_db.json +0 -0
  303. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/grafana/dashboards/30_runtime.json +0 -0
  304. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/grafana/dashboards/40_clients.json +0 -0
  305. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/grafana/dashboards/__init__.py +0 -0
  306. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/grafana/templates/__init__.py +0 -0
  307. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/grafana/templates/docker-compose.yml.tmpl +0 -0
  308. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/grafana/templates/prometheus.yml.tmpl +0 -0
  309. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/grafana/templates/provisioning/__init__.py +0 -0
  310. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/grafana/templates/provisioning/dashboards.yml +0 -0
  311. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/providers/grafana/templates/provisioning/datasource.yml +0 -0
  312. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/settings.py +0 -0
  313. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/grafana_dashboard.json +0 -0
  314. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/prometheus_rules.yml +0 -0
  315. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/sidecars/__init__.py +0 -0
  316. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/sidecars/compose/__init__.py +0 -0
  317. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/sidecars/compose/agent.yaml +0 -0
  318. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/sidecars/compose/docker-compose.yml +0 -0
  319. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/sidecars/fly/__init__.py +0 -0
  320. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/sidecars/fly/agent.yaml +0 -0
  321. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/sidecars/fly/fly.toml.fragment +0 -0
  322. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/sidecars/k8s/__init__.py +0 -0
  323. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/sidecars/k8s/configmap.yaml +0 -0
  324. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/sidecars/k8s/deployment.yaml +0 -0
  325. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/sidecars/railway/Dockerfile +0 -0
  326. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/sidecars/railway/README.md +0 -0
  327. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/sidecars/railway/__init__.py +0 -0
  328. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/obs/templates/sidecars/railway/agent.yaml +0 -0
  329. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/py.typed +0 -0
  330. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/security/add.py +0 -0
  331. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/security/audit.py +0 -0
  332. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/security/audit_service.py +0 -0
  333. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/security/headers.py +0 -0
  334. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/security/hibp.py +0 -0
  335. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/security/jwt_rotation.py +0 -0
  336. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/security/lockout.py +0 -0
  337. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/security/models.py +0 -0
  338. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/security/org_invites.py +0 -0
  339. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/security/passwords.py +0 -0
  340. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/security/permissions.py +0 -0
  341. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/security/session.py +0 -0
  342. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/security/signed_cookies.py +0 -0
  343. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/utils.py +0 -0
  344. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/webhooks/__init__.py +0 -0
  345. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/webhooks/add.py +0 -0
  346. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/webhooks/fastapi.py +0 -0
  347. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/webhooks/router.py +0 -0
  348. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/webhooks/service.py +0 -0
  349. {svc_infra-0.1.633 → svc_infra-0.1.635}/src/svc_infra/webhooks/signing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: svc-infra
3
- Version: 0.1.633
3
+ Version: 0.1.635
4
4
  Summary: Infrastructure for building and deploying prod-ready services
5
5
  License: MIT
6
6
  Keywords: fastapi,sqlalchemy,alembic,auth,infra,async,pydantic
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "svc-infra"
3
- version = "0.1.633"
3
+ version = "0.1.635"
4
4
  description = "Infrastructure for building and deploying prod-ready services"
5
5
  authors = ["Ali Khatami <aliikhatami94@gmail.com>"]
6
6
  license = "MIT"
@@ -134,6 +134,7 @@ markers = [
134
134
  "data_lifecycle: Data lifecycle (fixtures, retention, erasure, backups)",
135
135
  "ops: SLOs & Ops tests (probes, breaker, instrumentation)",
136
136
  "dx: Developer experience and quality gates tests",
137
+ "admin: Admin scope and impersonation tests",
137
138
  ]
138
139
  filterwarnings = [
139
140
  "ignore:The `route` decorator is deprecated:DeprecationWarning:starlette.*",
@@ -37,7 +37,9 @@ class HandlerTimeoutMiddleware:
37
37
 
38
38
  def __init__(self, app: ASGIApp, timeout_seconds: int | None = None) -> None:
39
39
  self.app = app
40
- self.timeout_seconds = timeout_seconds or REQUEST_TIMEOUT_SECONDS
40
+ self.timeout_seconds = (
41
+ timeout_seconds if timeout_seconds is not None else REQUEST_TIMEOUT_SECONDS
42
+ )
41
43
 
42
44
  async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
43
45
  if scope.get("type") != "http":
@@ -77,7 +79,9 @@ class BodyReadTimeoutMiddleware:
77
79
 
78
80
  def __init__(self, app: ASGIApp, timeout_seconds: int | None = None) -> None:
79
81
  self.app = app
80
- self.timeout_seconds = timeout_seconds or REQUEST_BODY_TIMEOUT_SECONDS
82
+ self.timeout_seconds = (
83
+ timeout_seconds if timeout_seconds is not None else REQUEST_BODY_TIMEOUT_SECONDS
84
+ )
81
85
 
82
86
  async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
83
87
  if scope.get("type") != "http":
@@ -0,0 +1,425 @@
1
+ # Admin Scope & Operations
2
+
3
+ This guide covers the admin subsystem: admin-only routes, permissions, impersonation, and operational guardrails.
4
+
5
+ ## Overview
6
+
7
+ The admin module provides:
8
+ - **Admin router pattern**: Role-gated endpoints under `/admin` with fine-grained permission checks
9
+ - **Impersonation**: Controlled user impersonation for support and debugging with full audit trails
10
+ - **Permission alignment**: `admin.impersonate` permission integrated with the RBAC system
11
+ - **Easy integration**: One-line setup via `add_admin(app, ...)`
12
+
13
+ ## Quick Start
14
+
15
+ ### Basic Setup
16
+
17
+ ```python
18
+ from fastapi import FastAPI
19
+ from svc_infra.api.fastapi.admin import add_admin
20
+
21
+ app = FastAPI()
22
+
23
+ # Mount admin endpoints with defaults
24
+ add_admin(app)
25
+
26
+ # Endpoints are now available:
27
+ # POST /admin/impersonate/start
28
+ # POST /admin/impersonate/stop
29
+ ```
30
+
31
+ ### Custom User Loader
32
+
33
+ If you have a custom user model or retrieval logic:
34
+
35
+ ```python
36
+ from fastapi import Request
37
+
38
+ async def my_user_getter(request: Request, user_id: str):
39
+ # Your custom user loading logic
40
+ user = await my_user_service.get_user(user_id)
41
+ if not user:
42
+ raise HTTPException(404, "user_not_found")
43
+ return user
44
+
45
+ add_admin(app, impersonation_user_getter=my_user_getter)
46
+ ```
47
+
48
+ ### Configuration
49
+
50
+ Environment variables:
51
+
52
+ - `ADMIN_IMPERSONATION_SECRET`: Secret for signing impersonation tokens (falls back to `APP_SECRET` or `"dev-secret"`)
53
+ - `ADMIN_IMPERSONATION_TTL`: Token TTL in seconds (default: 900 = 15 minutes)
54
+ - `ADMIN_IMPERSONATION_COOKIE`: Cookie name (default: `"impersonation"`)
55
+
56
+ Function parameters:
57
+
58
+ ```python
59
+ add_admin(
60
+ app,
61
+ base_path="/admin", # Base path for admin routes
62
+ enable_impersonation=True, # Enable impersonation endpoints
63
+ secret=None, # Override token signing secret
64
+ ttl_seconds=15 * 60, # Token TTL (15 minutes)
65
+ cookie_name="impersonation", # Cookie name
66
+ impersonation_user_getter=None, # Custom user loader
67
+ )
68
+ ```
69
+
70
+ ## Permissions & RBAC
71
+
72
+ ### Admin Role
73
+
74
+ The `admin` role includes the following permissions by default:
75
+
76
+ - `user.read`, `user.write`: User management
77
+ - `billing.read`, `billing.write`: Billing operations
78
+ - `security.session.list`, `security.session.revoke`: Session management
79
+ - `admin.impersonate`: User impersonation
80
+
81
+ ### Permission Guards
82
+
83
+ Admin endpoints use layered guards:
84
+
85
+ 1. **Role gate** at router level: `RequireRoles("admin")`
86
+ 2. **Permission gate** at endpoint level: `RequirePermission("admin.impersonate")`
87
+
88
+ This ensures both coarse-grained role membership and fine-grained permission enforcement.
89
+
90
+ ### Custom Admin Routes
91
+
92
+ ```python
93
+ from svc_infra.api.fastapi.admin import admin_router
94
+ from svc_infra.security.permissions import RequirePermission
95
+
96
+ # Create an admin-only router
97
+ router = admin_router(prefix="/admin", tags=["admin"])
98
+
99
+ @router.get("/analytics", dependencies=[RequirePermission("analytics.read")])
100
+ async def admin_analytics():
101
+ return {"data": "..."}
102
+
103
+ app.include_router(router)
104
+ ```
105
+
106
+ ## Impersonation
107
+
108
+ ### Use Cases
109
+
110
+ - **Customer support**: Debug issues as the affected user
111
+ - **Testing**: Verify permission boundaries and user-specific behavior
112
+ - **Compliance**: Audit access patterns under controlled conditions
113
+
114
+ ### Workflow
115
+
116
+ #### 1. Start Impersonation
117
+
118
+ ```bash
119
+ POST /admin/impersonate/start
120
+ Content-Type: application/json
121
+
122
+ {
123
+ "user_id": "u-12345",
124
+ "reason": "Investigating billing issue #789"
125
+ }
126
+ ```
127
+
128
+ **Requirements:**
129
+ - Authenticated user must have `admin` role
130
+ - User must have `admin.impersonate` permission
131
+ - `reason` field is mandatory
132
+
133
+ **Response:** `204 No Content` with impersonation cookie set
134
+
135
+ #### 2. Make Requests as Impersonated User
136
+
137
+ All subsequent requests will be made as the target user while preserving the admin's permissions for authorization checks:
138
+
139
+ ```bash
140
+ GET /api/v1/profile
141
+ Cookie: impersonation=<token>
142
+
143
+ # Returns the impersonated user's profile
144
+ ```
145
+
146
+ **Behavior:**
147
+ - `request.user` reflects the impersonated user
148
+ - `request.user.roles` inherits the actor's roles (admin maintains permissions)
149
+ - `principal.via` is set to `"impersonated"` for tracking
150
+
151
+ #### 3. Stop Impersonation
152
+
153
+ ```bash
154
+ POST /admin/impersonate/stop
155
+
156
+ # Response: 204 No Content
157
+ # Cookie deleted, subsequent requests use original identity
158
+ ```
159
+
160
+ ### Security Guardrails
161
+
162
+ #### Short TTL
163
+ - Default: 15 minutes
164
+ - No sliding refresh: token expires after TTL regardless of activity
165
+ - Rationale: Minimize blast radius of compromised impersonation sessions
166
+
167
+ #### Explicit Reason
168
+ - Required for every impersonation start
169
+ - Logged in audit trail for compliance and forensics
170
+
171
+ #### Audit Trail
172
+ Every impersonation action is logged with:
173
+ - `admin.impersonation.started`: actor, target, reason, expiry
174
+ - `admin.impersonation.stopped`: termination reason (manual/expired)
175
+
176
+ Example log entry:
177
+ ```json
178
+ {
179
+ "message": "admin.impersonation.started",
180
+ "actor_id": "u-admin-42",
181
+ "target_id": "u-12345",
182
+ "reason": "Investigating billing issue #789",
183
+ "expires_in": 900,
184
+ "timestamp": "2025-11-01T12:00:00Z"
185
+ }
186
+ ```
187
+
188
+ #### Token Security
189
+ - HMAC-SHA256 signed tokens with nonce
190
+ - Includes: actor_id, target_id, issued_at, expires_at, nonce
191
+ - Tamper detection via signature verification
192
+ - Cookie attributes:
193
+ - `httponly=true`: No JavaScript access
194
+ - `samesite=lax`: CSRF protection
195
+ - `secure=true` in production: HTTPS only
196
+
197
+ #### Permission Preservation
198
+ - Impersonated requests maintain the actor's permissions
199
+ - Prevents privilege escalation by impersonating a higher-privileged user
200
+ - Target user context for data scoping, actor permissions for authorization
201
+
202
+ ### Operational Recommendations
203
+
204
+ #### Development
205
+ ```python
206
+ # Relaxed for local testing
207
+ add_admin(
208
+ app,
209
+ secret="dev-secret",
210
+ ttl_seconds=60 * 60, # 1 hour for convenience
211
+ )
212
+ ```
213
+
214
+ #### Production
215
+ ```python
216
+ # Strict settings
217
+ add_admin(
218
+ app,
219
+ secret=os.environ["ADMIN_IMPERSONATION_SECRET"], # Strong secret from vault
220
+ ttl_seconds=15 * 60, # 15 minutes max
221
+ )
222
+ ```
223
+
224
+ **Best practices:**
225
+ - Rotate `ADMIN_IMPERSONATION_SECRET` periodically
226
+ - Monitor impersonation logs for anomalies
227
+ - Set up alerts for frequent impersonation by the same actor
228
+ - Consider org/tenant scoping for multi-tenant systems
229
+ - Document allowed impersonation reasons in your runbook
230
+
231
+ ## Monitoring & Observability
232
+
233
+ ### Metrics
234
+
235
+ Label admin routes with `route_class=admin` for SLO tracking:
236
+
237
+ ```python
238
+ from svc_infra.obs.add import add_observability
239
+
240
+ def route_classifier(path: str) -> str:
241
+ if path.startswith("/admin"):
242
+ return "admin"
243
+ # ... other classifications
244
+ return "public"
245
+
246
+ add_observability(app, route_classifier=route_classifier)
247
+ ```
248
+
249
+ ### Audit Log Queries
250
+
251
+ Search for impersonation events:
252
+ ```python
253
+ # Example: Query structured logs
254
+ logs.filter(message="admin.impersonation.started") \
255
+ .filter(actor_id="u-admin-42") \
256
+ .order_by(timestamp.desc()) \
257
+ .limit(100)
258
+ ```
259
+
260
+ Compliance report:
261
+ ```python
262
+ # Generate monthly impersonation summary
263
+ impersonations = audit_log.filter(
264
+ event_type__in=["admin.impersonation.started", "admin.impersonation.stopped"],
265
+ timestamp__gte=start_of_month,
266
+ )
267
+ report = impersonations.group_by("actor_id").agg(count="id", targets=unique("target_id"))
268
+ ```
269
+
270
+ ## Testing
271
+
272
+ ### Unit Tests
273
+
274
+ ```python
275
+ import pytest
276
+ from svc_infra.api.fastapi.admin import add_admin
277
+
278
+ @pytest.mark.admin
279
+ def test_impersonation_requires_permission():
280
+ app = make_test_app()
281
+ add_admin(app, impersonation_user_getter=lambda req, uid: User(id=uid))
282
+
283
+ # Without admin role → 403
284
+ client = TestClient(app)
285
+ r = client.post("/admin/impersonate/start", json={"user_id": "u-2", "reason": "test"})
286
+ assert r.status_code == 403
287
+ ```
288
+
289
+ ### Acceptance Tests
290
+
291
+ ```python
292
+ @pytest.mark.acceptance
293
+ @pytest.mark.admin
294
+ def test_impersonation_lifecycle(admin_client):
295
+ # Start impersonation
296
+ r = admin_client.post(
297
+ "/admin/impersonate/start",
298
+ json={"user_id": "u-target", "reason": "acceptance test"}
299
+ )
300
+ assert r.status_code == 204
301
+
302
+ # Verify impersonated context
303
+ profile = admin_client.get("/api/v1/profile")
304
+ assert profile.json()["id"] == "u-target"
305
+
306
+ # Stop impersonation
307
+ r = admin_client.post("/admin/impersonate/stop")
308
+ assert r.status_code == 204
309
+ ```
310
+
311
+ Run admin tests:
312
+ ```bash
313
+ pytest -m admin
314
+ ```
315
+
316
+ ## Troubleshooting
317
+
318
+ ### Impersonation Not Working
319
+
320
+ **Symptom:** Impersonation cookie set but requests still use original identity
321
+
322
+ **Check:**
323
+ 1. Cookie is being sent: verify `Cookie: impersonation=<token>` in request headers
324
+ 2. Token is valid: check signature and expiry
325
+ 3. User getter succeeds: ensure `impersonation_user_getter` doesn't raise exceptions
326
+ 4. Dependency override is active: `add_admin` registers a global override on startup
327
+
328
+ **Debug:**
329
+ ```python
330
+ # Enable debug logging
331
+ import logging
332
+ logging.getLogger("svc_infra.api.fastapi.admin").setLevel(logging.DEBUG)
333
+ ```
334
+
335
+ ### Permission Denied
336
+
337
+ **Symptom:** 403 when calling `/admin/impersonate/start`
338
+
339
+ **Check:**
340
+ 1. User has `admin` role: verify `user.roles` includes `"admin"`
341
+ 2. Permission registered: ensure `admin.impersonate` is in the permission registry
342
+ 3. Permission assigned to role: check `PERMISSION_REGISTRY["admin"]` includes `"admin.impersonate"`
343
+
344
+ ### Token Expired Too Soon
345
+
346
+ **Symptom:** Impersonation session ends before expected TTL
347
+
348
+ **Possible causes:**
349
+ 1. TTL misconfigured: check `ADMIN_IMPERSONATION_TTL` environment variable
350
+ 2. Server time skew: verify system clock is synchronized (NTP)
351
+ 3. Cookie attributes: ensure `max_age` matches TTL
352
+
353
+ ## Security Considerations
354
+
355
+ ### Threat Model
356
+
357
+ | Threat | Mitigation |
358
+ |--------|-----------|
359
+ | Token theft (XSS) | `httponly=true` cookie prevents JavaScript access |
360
+ | Token theft (network) | `secure=true` requires HTTPS in production |
361
+ | CSRF attacks | `samesite=lax` prevents cross-site cookie sending |
362
+ | Privilege escalation | Actor permissions preserved during impersonation |
363
+ | Prolonged access | Short TTL (15 min default) with no refresh |
364
+ | Abuse detection | Audit logs with reason, actor, and target tracking |
365
+ | Insider threat | Required reason and comprehensive audit trail |
366
+
367
+ ### Compliance
368
+
369
+ **SOC 2 / ISO 27001:**
370
+ - Audit trail requirement: ✅ All impersonation events logged
371
+ - Access justification: ✅ Mandatory `reason` field
372
+ - Time-bound access: ✅ Short TTL with no renewal
373
+ - Least privilege: ✅ Permission-based access control
374
+
375
+ **GDPR / Data Protection:**
376
+ - Lawful basis: Support/debugging under legitimate interest or contract performance
377
+ - Data minimization: Only necessary user context loaded
378
+ - Transparency: Log access for data subject access requests (DSAR)
379
+ - Documentation: This guide serves as basis for DPA documentation
380
+
381
+ ## API Reference
382
+
383
+ ### `add_admin(app, **kwargs)`
384
+
385
+ Wire admin endpoints and impersonation to a FastAPI app.
386
+
387
+ **Parameters:**
388
+ - `app` (FastAPI): Target application
389
+ - `base_path` (str): Admin router base path (default: `"/admin"`)
390
+ - `enable_impersonation` (bool): Enable impersonation endpoints (default: `True`)
391
+ - `secret` (str | None): Token signing secret (default: env `ADMIN_IMPERSONATION_SECRET`)
392
+ - `ttl_seconds` (int): Token TTL (default: `900` = 15 minutes)
393
+ - `cookie_name` (str): Cookie name (default: `"impersonation"`)
394
+ - `impersonation_user_getter` (Callable | None): Custom user loader `(request, user_id) -> user`
395
+
396
+ **Returns:** None (modifies app in place)
397
+
398
+ **Idempotency:** Safe to call multiple times; only wires once per app instance
399
+
400
+ ### `admin_router(**kwargs)`
401
+
402
+ Create an admin-only router with role gate.
403
+
404
+ **Parameters:** Same as `APIRouter` (FastAPI)
405
+
406
+ **Returns:** APIRouter with `RequireRoles("admin")` dependency
407
+
408
+ **Example:**
409
+ ```python
410
+ from svc_infra.api.fastapi.admin import admin_router
411
+
412
+ router = admin_router(prefix="/admin/reports", tags=["admin-reports"])
413
+
414
+ @router.get("/summary")
415
+ async def admin_summary():
416
+ return {"total_users": 1234}
417
+ ```
418
+
419
+ ## Further Reading
420
+
421
+ - [ADR 0011: Admin scope and impersonation](../src/svc_infra/docs/adr/0011-admin-scope-and-impersonation.md)
422
+ - [Security & Auth Hardening](./security.md)
423
+ - [Permissions & RBAC](./security.md#permissions-and-rbac)
424
+ - [Audit Logging](./security.md#audit-logging)
425
+ - [Observability](./observability.md)
@@ -46,7 +46,11 @@ def new_httpx_client(
46
46
  Callers can override timeout_seconds; remaining kwargs are forwarded to httpx.Client.
47
47
  """
48
48
  timeout = make_timeout(timeout_seconds)
49
- return httpx.Client(timeout=timeout, headers=headers, base_url=base_url, **kwargs)
49
+ # httpx doesn't accept base_url=None; only pass if non-None
50
+ client_kwargs = {"timeout": timeout, "headers": headers, **kwargs}
51
+ if base_url is not None:
52
+ client_kwargs["base_url"] = base_url
53
+ return httpx.Client(**client_kwargs)
50
54
 
51
55
 
52
56
  def new_async_httpx_client(
@@ -61,4 +65,8 @@ def new_async_httpx_client(
61
65
  Callers can override timeout_seconds; remaining kwargs are forwarded to httpx.AsyncClient.
62
66
  """
63
67
  timeout = make_timeout(timeout_seconds)
64
- return httpx.AsyncClient(timeout=timeout, headers=headers, base_url=base_url, **kwargs)
68
+ # httpx doesn't accept base_url=None; only pass if non-None
69
+ client_kwargs = {"timeout": timeout, "headers": headers, **kwargs}
70
+ if base_url is not None:
71
+ client_kwargs["base_url"] = base_url
72
+ return httpx.AsyncClient(**client_kwargs)
File without changes