nene2-python 1.8.35__tar.gz → 1.8.36__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 (386) hide show
  1. {nene2_python-1.8.35 → nene2_python-1.8.36}/PKG-INFO +1 -2
  2. nene2_python-1.8.36/docs/field-trials/2026-05-field-trial-165.md +389 -0
  3. {nene2_python-1.8.35 → nene2_python-1.8.36}/pyproject.toml +1 -2
  4. {nene2_python-1.8.35 → nene2_python-1.8.36}/uv.lock +1 -3
  5. {nene2_python-1.8.35 → nene2_python-1.8.36}/.env.example +0 -0
  6. {nene2_python-1.8.35 → nene2_python-1.8.36}/.github/workflows/ci.yml +0 -0
  7. {nene2_python-1.8.35 → nene2_python-1.8.36}/.github/workflows/docs.yml +0 -0
  8. {nene2_python-1.8.35 → nene2_python-1.8.36}/.github/workflows/publish.yml +0 -0
  9. {nene2_python-1.8.35 → nene2_python-1.8.36}/.gitignore +0 -0
  10. {nene2_python-1.8.35 → nene2_python-1.8.36}/.vitepress/config.mts +0 -0
  11. {nene2_python-1.8.35 → nene2_python-1.8.36}/.vitepress/theme/custom.css +0 -0
  12. {nene2_python-1.8.35 → nene2_python-1.8.36}/.vitepress/theme/index.ts +0 -0
  13. {nene2_python-1.8.35 → nene2_python-1.8.36}/AGENTS.md +0 -0
  14. {nene2_python-1.8.35 → nene2_python-1.8.36}/CHANGELOG.md +0 -0
  15. {nene2_python-1.8.35 → nene2_python-1.8.36}/CLAUDE.md +0 -0
  16. {nene2_python-1.8.35 → nene2_python-1.8.36}/Dockerfile +0 -0
  17. {nene2_python-1.8.35 → nene2_python-1.8.36}/LICENSE +0 -0
  18. {nene2_python-1.8.35 → nene2_python-1.8.36}/README.md +0 -0
  19. {nene2_python-1.8.35 → nene2_python-1.8.36}/alembic/README +0 -0
  20. {nene2_python-1.8.35 → nene2_python-1.8.36}/alembic/env.py +0 -0
  21. {nene2_python-1.8.35 → nene2_python-1.8.36}/alembic/script.py.mako +0 -0
  22. {nene2_python-1.8.35 → nene2_python-1.8.36}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
  23. {nene2_python-1.8.35 → nene2_python-1.8.36}/alembic.ini +0 -0
  24. {nene2_python-1.8.35 → nene2_python-1.8.36}/compose.yaml +0 -0
  25. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/adr/0001-toolchain.md +0 -0
  26. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/adr/0002-clean-architecture.md +0 -0
  27. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/adr/0003-security-first.md +0 -0
  28. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/adr/0004-ai-first-design.md +0 -0
  29. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/adr/0005-logging.md +0 -0
  30. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/adr/0006-rate-limiting.md +0 -0
  31. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/adr/0009-mcp-design.md +0 -0
  32. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/adr/0010-async-use-case.md +0 -0
  33. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
  34. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/de/index.md +0 -0
  35. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/de/tutorials/getting-started.md +0 -0
  36. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/explanation/architecture.md +0 -0
  37. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/explanation/design-philosophy.md +0 -0
  38. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-1.md +0 -0
  39. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-10.md +0 -0
  40. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-100.md +0 -0
  41. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-101.md +0 -0
  42. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-102.md +0 -0
  43. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-103.md +0 -0
  44. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-104.md +0 -0
  45. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-105.md +0 -0
  46. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-106.md +0 -0
  47. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-107.md +0 -0
  48. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-108.md +0 -0
  49. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-109.md +0 -0
  50. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-11.md +0 -0
  51. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-110.md +0 -0
  52. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-111.md +0 -0
  53. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-112.md +0 -0
  54. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-113.md +0 -0
  55. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-114.md +0 -0
  56. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-115.md +0 -0
  57. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-116.md +0 -0
  58. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-117.md +0 -0
  59. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-118.md +0 -0
  60. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-119.md +0 -0
  61. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-12.md +0 -0
  62. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-120.md +0 -0
  63. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-121.md +0 -0
  64. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-122.md +0 -0
  65. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-123.md +0 -0
  66. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-124.md +0 -0
  67. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-125.md +0 -0
  68. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-126.md +0 -0
  69. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-127.md +0 -0
  70. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-128.md +0 -0
  71. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-129.md +0 -0
  72. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-13.md +0 -0
  73. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-130.md +0 -0
  74. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-131.md +0 -0
  75. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-132.md +0 -0
  76. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-133.md +0 -0
  77. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-134.md +0 -0
  78. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-135.md +0 -0
  79. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-136.md +0 -0
  80. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-137.md +0 -0
  81. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-138.md +0 -0
  82. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-139.md +0 -0
  83. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-14.md +0 -0
  84. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-140.md +0 -0
  85. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-141.md +0 -0
  86. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-142.md +0 -0
  87. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-143.md +0 -0
  88. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-144.md +0 -0
  89. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-145.md +0 -0
  90. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-146.md +0 -0
  91. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-147.md +0 -0
  92. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-148.md +0 -0
  93. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-149.md +0 -0
  94. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-15.md +0 -0
  95. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-150.md +0 -0
  96. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-151.md +0 -0
  97. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-152.md +0 -0
  98. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-153.md +0 -0
  99. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-154.md +0 -0
  100. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-155.md +0 -0
  101. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-156.md +0 -0
  102. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-157.md +0 -0
  103. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-158.md +0 -0
  104. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-159.md +0 -0
  105. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-16.md +0 -0
  106. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-160.md +0 -0
  107. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-161.md +0 -0
  108. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-162.md +0 -0
  109. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-163.md +0 -0
  110. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-164.md +0 -0
  111. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-17.md +0 -0
  112. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-18.md +0 -0
  113. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-19.md +0 -0
  114. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-2.md +0 -0
  115. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-20.md +0 -0
  116. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-21.md +0 -0
  117. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-22.md +0 -0
  118. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-23.md +0 -0
  119. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-24.md +0 -0
  120. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-25.md +0 -0
  121. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-26.md +0 -0
  122. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-27.md +0 -0
  123. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-28.md +0 -0
  124. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-29.md +0 -0
  125. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-3.md +0 -0
  126. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-30.md +0 -0
  127. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-31.md +0 -0
  128. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-32.md +0 -0
  129. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-33.md +0 -0
  130. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-34.md +0 -0
  131. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-35.md +0 -0
  132. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-36.md +0 -0
  133. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-37.md +0 -0
  134. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-38.md +0 -0
  135. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-39.md +0 -0
  136. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-4.md +0 -0
  137. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-40.md +0 -0
  138. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-41.md +0 -0
  139. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-42.md +0 -0
  140. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-43.md +0 -0
  141. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-44.md +0 -0
  142. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-45.md +0 -0
  143. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-46.md +0 -0
  144. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-47.md +0 -0
  145. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-48.md +0 -0
  146. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-49.md +0 -0
  147. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-5.md +0 -0
  148. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-50.md +0 -0
  149. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-51.md +0 -0
  150. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-52.md +0 -0
  151. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-53.md +0 -0
  152. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-54.md +0 -0
  153. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-55.md +0 -0
  154. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-56.md +0 -0
  155. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-57.md +0 -0
  156. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-58.md +0 -0
  157. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-59.md +0 -0
  158. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-6.md +0 -0
  159. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-60.md +0 -0
  160. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-61.md +0 -0
  161. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-62.md +0 -0
  162. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-63.md +0 -0
  163. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-64.md +0 -0
  164. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-65.md +0 -0
  165. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-66.md +0 -0
  166. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-67.md +0 -0
  167. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-68.md +0 -0
  168. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-69.md +0 -0
  169. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-7.md +0 -0
  170. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-70.md +0 -0
  171. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-71.md +0 -0
  172. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-72.md +0 -0
  173. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-73.md +0 -0
  174. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-74.md +0 -0
  175. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-75.md +0 -0
  176. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-76.md +0 -0
  177. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-77.md +0 -0
  178. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-78.md +0 -0
  179. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-79.md +0 -0
  180. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-8.md +0 -0
  181. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-80.md +0 -0
  182. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-81.md +0 -0
  183. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-82.md +0 -0
  184. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-83.md +0 -0
  185. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-84.md +0 -0
  186. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-85.md +0 -0
  187. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-86.md +0 -0
  188. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-87.md +0 -0
  189. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-88.md +0 -0
  190. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-89.md +0 -0
  191. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-9.md +0 -0
  192. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-90.md +0 -0
  193. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-91.md +0 -0
  194. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-92.md +0 -0
  195. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-93.md +0 -0
  196. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-94.md +0 -0
  197. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-95.md +0 -0
  198. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-96.md +0 -0
  199. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-97.md +0 -0
  200. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-98.md +0 -0
  201. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/field-trials/2026-05-field-trial-99.md +0 -0
  202. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/fr/index.md +0 -0
  203. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/fr/tutorials/getting-started.md +0 -0
  204. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/add-new-domain.md +0 -0
  205. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/api-versioning.md +0 -0
  206. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/async-use-case.md +0 -0
  207. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/background-tasks.md +0 -0
  208. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/configure-auth.md +0 -0
  209. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/cors.md +0 -0
  210. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/custom-auth-middleware.md +0 -0
  211. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/dependency-injection.md +0 -0
  212. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/domain-events.md +0 -0
  213. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/file-upload.md +0 -0
  214. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/lifespan-and-app-state.md +0 -0
  215. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/middleware-stack.md +0 -0
  216. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/new-project.md +0 -0
  217. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/problem-details.md +0 -0
  218. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/response-patterns.md +0 -0
  219. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/run-tests.md +0 -0
  220. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/soft-delete.md +0 -0
  221. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/sqlalchemy-repository.md +0 -0
  222. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/streaming.md +0 -0
  223. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/structured-logging.md +0 -0
  224. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/validation.md +0 -0
  225. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/how-to/webhook.md +0 -0
  226. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/howto/mcp-setup.md +0 -0
  227. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/index.md +0 -0
  228. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/ja/explanation/architecture.md +0 -0
  229. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/ja/explanation/design-philosophy.md +0 -0
  230. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/ja/how-to/add-new-domain.md +0 -0
  231. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/ja/how-to/configure-auth.md +0 -0
  232. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/ja/how-to/new-project.md +0 -0
  233. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/ja/how-to/run-tests.md +0 -0
  234. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
  235. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/ja/howto/mcp-setup.md +0 -0
  236. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/ja/index.md +0 -0
  237. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/ja/reference/api.md +0 -0
  238. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/ja/reference/configuration.md +0 -0
  239. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/ja/reference/framework-modules.md +0 -0
  240. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/ja/tutorials/first-domain.md +0 -0
  241. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/ja/tutorials/getting-started.md +0 -0
  242. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/pt-br/index.md +0 -0
  243. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/pt-br/tutorials/getting-started.md +0 -0
  244. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/reference/api.md +0 -0
  245. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/reference/configuration.md +0 -0
  246. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/reference/framework-modules.md +0 -0
  247. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/roadmap.md +0 -0
  248. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/templates/field-trial-report.md +0 -0
  249. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/todo/current.md +0 -0
  250. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/tutorials/first-domain.md +0 -0
  251. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/tutorials/getting-started.md +0 -0
  252. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/zh/index.md +0 -0
  253. {nene2_python-1.8.35 → nene2_python-1.8.36}/docs/zh/tutorials/getting-started.md +0 -0
  254. {nene2_python-1.8.35 → nene2_python-1.8.36}/package-lock.json +0 -0
  255. {nene2_python-1.8.35 → nene2_python-1.8.36}/package.json +0 -0
  256. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/__init__.py +0 -0
  257. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/__main__.py +0 -0
  258. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/app.py +0 -0
  259. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/comment/__init__.py +0 -0
  260. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/comment/entity.py +0 -0
  261. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/comment/exceptions.py +0 -0
  262. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/comment/handler.py +0 -0
  263. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/comment/repository.py +0 -0
  264. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/comment/sqlalchemy_repository.py +0 -0
  265. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/comment/use_case.py +0 -0
  266. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/mcp.py +0 -0
  267. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/note/__init__.py +0 -0
  268. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/note/async_use_case.py +0 -0
  269. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/note/entity.py +0 -0
  270. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/note/exceptions.py +0 -0
  271. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/note/handler.py +0 -0
  272. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/note/repository.py +0 -0
  273. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/note/sqlalchemy_repository.py +0 -0
  274. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/note/use_case.py +0 -0
  275. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/schema.py +0 -0
  276. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/tag/__init__.py +0 -0
  277. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/tag/entity.py +0 -0
  278. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/tag/exceptions.py +0 -0
  279. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/tag/handler.py +0 -0
  280. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/tag/repository.py +0 -0
  281. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/tag/sqlalchemy_repository.py +0 -0
  282. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/example/tag/use_case.py +0 -0
  283. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/__init__.py +0 -0
  284. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/auth/__init__.py +0 -0
  285. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/auth/api_key.py +0 -0
  286. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/auth/bearer_token.py +0 -0
  287. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/auth/deps.py +0 -0
  288. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/auth/exceptions.py +0 -0
  289. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/auth/interfaces.py +0 -0
  290. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/auth/local_verifier.py +0 -0
  291. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/cache/__init__.py +0 -0
  292. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/cache/ttl.py +0 -0
  293. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/config/__init__.py +0 -0
  294. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/config/settings.py +0 -0
  295. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/database/__init__.py +0 -0
  296. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/database/exceptions.py +0 -0
  297. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/database/health.py +0 -0
  298. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/database/interfaces.py +0 -0
  299. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/database/sqlalchemy_executor.py +0 -0
  300. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/database/utils.py +0 -0
  301. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/http/__init__.py +0 -0
  302. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/http/etag.py +0 -0
  303. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/http/health.py +0 -0
  304. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/http/pagination.py +0 -0
  305. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/http/problem_details.py +0 -0
  306. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/log/__init__.py +0 -0
  307. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/log/setup.py +0 -0
  308. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/mcp/__init__.py +0 -0
  309. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/mcp/http_client.py +0 -0
  310. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/mcp/server.py +0 -0
  311. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/middleware/__init__.py +0 -0
  312. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/middleware/domain_exception.py +0 -0
  313. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/middleware/error_handler.py +0 -0
  314. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/middleware/request_id.py +0 -0
  315. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/middleware/request_logging.py +0 -0
  316. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/middleware/request_size_limit.py +0 -0
  317. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/middleware/security_headers.py +0 -0
  318. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/middleware/setup.py +0 -0
  319. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/middleware/throttle.py +0 -0
  320. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/py.typed +0 -0
  321. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/security/__init__.py +0 -0
  322. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/security/webhook.py +0 -0
  323. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/use_case/__init__.py +0 -0
  324. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/use_case/protocols.py +0 -0
  325. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/validation/__init__.py +0 -0
  326. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/nene2/validation/exceptions.py +0 -0
  327. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/scripts/__init__.py +0 -0
  328. {nene2_python-1.8.35 → nene2_python-1.8.36}/src/scripts/export_openapi.py +0 -0
  329. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/__init__.py +0 -0
  330. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/conftest.py +0 -0
  331. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/example/__init__.py +0 -0
  332. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/example/comment/__init__.py +0 -0
  333. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/example/comment/test_comment_http.py +0 -0
  334. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/example/comment/test_comment_repository.py +0 -0
  335. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/example/comment/test_comment_use_case.py +0 -0
  336. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/example/conftest.py +0 -0
  337. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/example/note/__init__.py +0 -0
  338. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/example/note/test_async_note_use_case.py +0 -0
  339. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/example/note/test_list_notes.py +0 -0
  340. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/example/note/test_note_repository.py +0 -0
  341. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/example/tag/__init__.py +0 -0
  342. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/example/tag/test_tag_repository.py +0 -0
  343. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/example/tag/test_tags.py +0 -0
  344. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/example/test_cors.py +0 -0
  345. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/example/test_mcp.py +0 -0
  346. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/__init__.py +0 -0
  347. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/auth/__init__.py +0 -0
  348. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/auth/test_api_key.py +0 -0
  349. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/auth/test_bearer_token.py +0 -0
  350. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/auth/test_make_require_auth.py +0 -0
  351. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/auth/test_token_issuer.py +0 -0
  352. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/cache/__init__.py +0 -0
  353. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/cache/test_ttl.py +0 -0
  354. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/config/__init__.py +0 -0
  355. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/config/test_settings.py +0 -0
  356. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/database/__init__.py +0 -0
  357. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/database/test_transaction.py +0 -0
  358. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/database/test_utils.py +0 -0
  359. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/http/__init__.py +0 -0
  360. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/http/test_etag.py +0 -0
  361. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/http/test_health.py +0 -0
  362. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/http/test_pagination.py +0 -0
  363. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/http/test_problem_details.py +0 -0
  364. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/log/__init__.py +0 -0
  365. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/log/test_setup.py +0 -0
  366. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/mcp/__init__.py +0 -0
  367. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/mcp/test_http_client.py +0 -0
  368. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/mcp/test_server.py +0 -0
  369. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/middleware/__init__.py +0 -0
  370. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/middleware/test_error_handler.py +0 -0
  371. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/middleware/test_request_id.py +0 -0
  372. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/middleware/test_request_logging.py +0 -0
  373. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/middleware/test_request_size_limit.py +0 -0
  374. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/middleware/test_security_headers.py +0 -0
  375. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
  376. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
  377. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/middleware/test_throttle.py +0 -0
  378. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/security/__init__.py +0 -0
  379. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/security/test_webhook.py +0 -0
  380. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/use_case/__init__.py +0 -0
  381. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/use_case/test_protocols.py +0 -0
  382. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
  383. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/validation/__init__.py +0 -0
  384. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/nene2/validation/test_exceptions.py +0 -0
  385. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/scripts/__init__.py +0 -0
  386. {nene2_python-1.8.35 → nene2_python-1.8.36}/tests/scripts/test_export_openapi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nene2-python
3
- Version: 1.8.35
3
+ Version: 1.8.36
4
4
  Summary: NENE2 Python — minimal API framework following NENE2's design philosophy
5
5
  Project-URL: Homepage, https://github.com/hideyukiMORI/nene2-python
6
6
  Project-URL: Repository, https://github.com/hideyukiMORI/nene2-python
@@ -28,7 +28,6 @@ Requires-Dist: httpx>=0.27
28
28
  Requires-Dist: mcp>=1.0
29
29
  Requires-Dist: pydantic-settings>=2.6
30
30
  Requires-Dist: pydantic>=2.9
31
- Requires-Dist: pyjwt>=2.12.0
32
31
  Requires-Dist: python-multipart>=0.0.12
33
32
  Requires-Dist: pyyaml>=6.0
34
33
  Requires-Dist: sqlalchemy>=2.0.49
@@ -0,0 +1,389 @@
1
+ # FT165: secrets モジュール
2
+
3
+ **日付**: 2026-05-21
4
+ **テーマ**: `secrets` モジュール — 暗号学的安全な乱数・タイミング安全比較・OTP 生成
5
+ **セキュリティ診断**: **あり**(165 % 3 = 0)
6
+
7
+ ---
8
+
9
+ ## 概要
10
+
11
+ Python 標準ライブラリの `secrets` モジュールを nene2-python フレームワーク上で検証した。
12
+ `secrets` は OS の乱数源(`/dev/urandom` 相当)を使用して暗号学的に安全なトークンを生成するモジュール。
13
+ セキュリティトークン・OTP・API キー生成のベストプラクティスであり、
14
+ `random` モジュールの代替として NIST SP 800-63B が推奨する。
15
+
16
+ ---
17
+
18
+ ## 実装したサンプルアプリ
19
+
20
+ **場所**: `/home/xi/docker/nene2-python-FT/ft165-secrets/`
21
+
22
+ ### 主要機能
23
+
24
+ | 関数 | 概要 |
25
+ |---|---|
26
+ | `generate_token_hex(nbytes)` | `secrets.token_hex()` で hex トークン生成、最低 128 bit を保証 |
27
+ | `generate_token_urlsafe(nbytes)` | `secrets.token_urlsafe()` で URL 安全トークン生成 |
28
+ | `generate_token_bytes_b64(nbytes)` | `secrets.token_bytes()` で raw バイト生成(Base64 エンコード返却) |
29
+ | `timing_safe_compare(a, b)` | `hmac.compare_digest()` でタイミング安全比較 |
30
+ | `randbelow_demo(upper)` | `secrets.randbelow()` で範囲内乱数生成 |
31
+ | `generate_otp(length)` | `secrets.choice()` で紛らわしい文字を除いた OTP 生成 |
32
+ | `random_module_usage_check()` | nene2-python ソースで `random` モジュール使用なしを検証 |
33
+
34
+ ### HTTP エンドポイント
35
+
36
+ | メソッド | パス | 概要 |
37
+ |---|---|---|
38
+ | GET | `/secrets/token-hex` | hex トークン生成 |
39
+ | GET | `/secrets/token-urlsafe` | URL 安全トークン生成 |
40
+ | GET | `/secrets/token-bytes` | バイトトークン(Base64)生成 |
41
+ | POST | `/secrets/compare` | タイミング安全比較 |
42
+ | GET | `/secrets/randbelow` | 範囲内乱数 |
43
+ | POST | `/secrets/otp` | OTP 生成 |
44
+ | GET | `/secrets/audit/random-module-check` | セキュリティ自己診断 |
45
+
46
+ ---
47
+
48
+ ## テスト結果
49
+
50
+ **32 passed(摩擦ゼロ)**
51
+
52
+ ```
53
+ 32 passed in 0.87s
54
+ ```
55
+
56
+ ---
57
+
58
+ ## 摩擦ポイント
59
+
60
+ **今回の FT では実装上の摩擦はゼロだった。**
61
+
62
+ ---
63
+
64
+ ## 観察点
65
+
66
+ ### 観察1: `MIN_TOKEN_BYTES = 16` を強制してセキュリティ下限を守る
67
+
68
+ ```python
69
+ MIN_TOKEN_BYTES = 16 # NIST SP 800-63B: 最低 128 bit
70
+
71
+ def generate_token_hex(nbytes: int = 32) -> TokenResult:
72
+ safe_nbytes = max(MIN_TOKEN_BYTES, nbytes)
73
+ return TokenResult(value=secrets.token_hex(safe_nbytes), ...)
74
+ ```
75
+
76
+ 呼び出し元が `nbytes=1` を渡しても 128 bit 以上を保証する。
77
+ nene2-python のトークン生成 API はこのパターンを標準にすべき。
78
+
79
+ ### 観察2: `hmac.compare_digest` vs `secrets.compare_digest`
80
+
81
+ Python 3.12+ では `hmac.compare_digest` と `secrets.compare_digest` はどちらも定時間比較。
82
+ `secrets.compare_digest` は Python 3.14 で追加されたが、3.12 では `hmac.compare_digest` を使う。
83
+ nene2-python の `auth/local_verifier.py` は `secrets.compare_digest` を使っており正しい(3.14 環境)。
84
+
85
+ ### 観察3: OTP のアルファベットから紛らわしい文字を除外
86
+
87
+ ```python
88
+ OTP_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" # 0/O, 1/I/L を除外
89
+ code = "".join(secrets.choice(OTP_ALPHABET) for _ in range(length))
90
+ ```
91
+
92
+ `0` と `O`、`1` と `I` と `L` はフォントによって区別しにくい。
93
+ 人間が読む OTP では除外が必須。`secrets.choice()` は毎回独立した乱数を引くため分布が均等。
94
+
95
+ ### 観察4: `token_hex(n)` の文字列長は `2n`
96
+
97
+ ```python
98
+ secrets.token_hex(32) # → 64文字の hex 文字列
99
+ ```
100
+
101
+ `n` バイト = `2n` 文字(1 バイト = 2 hex 文字)。
102
+ ドキュメントに書いてあるが初心者が `len(token) == nbytes` と誤解しやすい。
103
+
104
+ ### 観察5: `token_urlsafe(n)` の長さは可変(Base64 パディング依存)
105
+
106
+ ```python
107
+ secrets.token_urlsafe(32) # → 43文字(32バイトを Base64url でエンコード)
108
+ ```
109
+
110
+ Base64url は 3 バイトごとに 4 文字になるためパディングで長さが変わる。
111
+ DB カラムのサイズ設計時は `len(token_urlsafe(n))` を実測すること。
112
+
113
+ ---
114
+
115
+ ## nene2-python フレームワークとの統合
116
+
117
+ - `auth/local_verifier.py` がすでに `secrets.compare_digest()` を使用:正しい実装
118
+ - `security/webhook.py` が `hmac.compare_digest()` を使用:正しい実装
119
+ - `random` モジュールはソース全体で使用ゼロ:PASS
120
+ - トークン生成ヘルパーを `nene2.security` に追加するとユーザーが安全なデフォルトを使いやすくなる
121
+
122
+ ---
123
+
124
+ ## Developer Experience (DX) Review
125
+
126
+ ### ペルソナ1: 初心者(Python 歴1年・独学中・女性・バックエンド志望)
127
+
128
+ セキュリティモジュールについて「とりあえず動くもの」を求める段階。
129
+ `random.token_hex()` が存在しないことを知らず、`random.random()` でトークンを作ってしまうリスクが高い。
130
+
131
+ **ドキュメント理解**: `secrets` vs `random` の使い分けがドキュメントにないと区別できない。
132
+ 「API キー生成には必ず secrets モジュールを使う」という1文があれば十分。
133
+
134
+ **事故リスク**: **高**。`random.getrandbits(128)` で作ったトークンは予測可能。
135
+ 攻撃者はシードを推測して有効なセッショントークンを列挙できる(実際の攻撃例あり)。
136
+
137
+ **規約の使いやすさ**: `secrets.token_urlsafe()` のシグネチャは直感的。
138
+ 「`n` バイト → `2n` 文字」だけ補足があれば使える。
139
+
140
+ ### ペルソナ2: ロースキル経験者(Python 歴3-4年・スクリプト系・男性・SES)
141
+
142
+ `uuid.uuid4()` をトークンとして使っているケースが多い(UUID v4 は 122 bit のエントロピーがあるが、
143
+ ライブラリによっては擬似乱数源を使う)。
144
+
145
+ **コピペ可能性**: `secrets.token_urlsafe(32)` のワンライナーはコピペしやすい。
146
+ サンプルに「これが正しいやり方」と明示されていれば従う。
147
+
148
+ **拡張時の罠**: タイミング攻撃が理解されていない。
149
+ `if user_token == db_token:` という `==` 比較を「動いているから OK」と思いがち。
150
+ `hmac.compare_digest` が必要な理由を 1 行コメントで説明する価値がある。
151
+
152
+ **セキュリティ的な事故リスク**: **高**。タイミング攻撃によるトークン列挙は実際の攻撃手法。
153
+
154
+ ### ペルソナ3: フロントエンド寄り経験者(React/TS 歴4年・バックエンド転向中・ノンバイナリ)
155
+
156
+ Node.js の `crypto.randomBytes()` / `crypto.timingSafeEqual()` と同等の概念を理解している。
157
+
158
+ **Python 固有概念の学習コスト**: `hmac.compare_digest` が `secrets.compare_digest` と違うモジュールにあることが混乱点。
159
+ Python 3.14+ では `secrets.compare_digest` も使える。
160
+
161
+ **事故リスク**: 低。暗号トークンの概念は既知。
162
+
163
+ ### ペルソナ4: バックエンド経験者(Django/FastAPI 歴5-6年・男性・リードエンジニア)
164
+
165
+ Django の `get_random_string()` / `constant_time_compare()` との対応がわかれば即座に使える。
166
+
167
+ **他フレームワークとの差異**: Django は `django.utils.crypto.constant_time_compare()` でラップ済み。
168
+ nene2-python は `hmac.compare_digest()` を直接使う必要がある(より透明性が高い)。
169
+
170
+ **本番投入可能性**: 問題なし。`secrets` モジュールは stdlib で外部依存なし。
171
+
172
+ ### ペルソナ5: シニアエンジニア(設計・コードレビュー担当・女性・10-12年)
173
+
174
+ **コードレビューチェックポイント**:
175
+ - [ ] トークン生成に `random` / `uuid.uuid4()` ではなく `secrets` を使っているか
176
+ - [ ] トークン比較に `==` ではなく `hmac.compare_digest` / `secrets.compare_digest` を使っているか
177
+ - [ ] `nbytes` が最低 16 以上(128 bit)か
178
+ - [ ] OTP のアルファベットに紛らわしい文字(0/O/1/I/L)が含まれていないか
179
+ - [ ] `token_hex(n)` の長さが `n` ではなく `2n` 文字であることをカラムサイズ設計に反映しているか
180
+
181
+ **チームでの安全なパターン**: `nene2.security.generate_token()` ヘルパーに最低ビット強度を組み込むことで、
182
+ チームメンバーが誤って短いトークンを生成するリスクを排除できる。
183
+
184
+ ### ペルソナ6: 設計者・ポリシー照合(nene2-python 設計ポリシー目線)
185
+
186
+ **ポリシー達成度**: 高(`secrets` モジュール使用はCLAUDE.mdで必須化済み)
187
+
188
+ **「初心者でも安全な API」達成度**: 中
189
+ - `nene2.security` に最低 128 bit を保証するトークン生成ヘルパーがあれば「高」になる
190
+ - 現状は「`secrets` を使え」というポリシーだけで、安全なデフォルトが提供されていない
191
+
192
+ **設計上の負債**: `nene2.security` に `generate_api_key()`、`generate_session_token()` などの
193
+ 安全なデフォルトを持つ高レベルヘルパーが未実装。
194
+
195
+ **Follow-up Issue 候補**: `feat: nene2.security に generate_token() ヘルパーを追加(最低 128 bit 保証)`
196
+
197
+ ---
198
+
199
+ ## セキュリティ診断(FT165 — 165 % 3 = 0)
200
+
201
+ > **診断方針**: Django・FastAPI・SQLAlchemy 本体でも CVE が報告されてきたレベルの
202
+ > 攻撃ベクターを対象とする。「動いているから安全」は不正解。
203
+
204
+ ### 1. OWASP API Security Top 10 (2023)
205
+
206
+ #### API1: BOLA / IDOR
207
+ - ユーザー所有リソースがないため対象外(トークン生成 API)
208
+ - **結果**: ✅ 対象外
209
+
210
+ #### API2: 認証の破損
211
+ - [ ] `auth/local_verifier.py:36` で `secrets.compare_digest()` 使用確認 → **✅ PASS**
212
+ - [ ] 保護エンドポイント: FT165 アプリは認証なしの公開 API のため対象外
213
+ - **結果**: ✅
214
+
215
+ #### API3: Mass Assignment
216
+ - 実測: `POST /secrets/compare` に `{"a":"x","b":"y","is_admin":true}` → 200、レスポンスに `is_admin` なし
217
+ - Pydantic が extra フィールドを無視(デフォルト動作)
218
+ - **結果**: ✅ PASS
219
+
220
+ #### API4: 無制限リソース消費
221
+ - 実測: `a="x"*513` → 422(`max_length=512` で拒否)
222
+ - 実測: `upper=1_000_001` → 422(`le=1_000_000` で拒否)
223
+ - `ThrottleMiddleware`: FT165 アプリは未設定(本番では必須)
224
+ - **結果**: ✅ バリデーション境界は機能。スロットリングは本番設定が必要
225
+
226
+ #### API5: 機能レベルの認可不備
227
+ - FT165 は認証不要の公開 API のため対象外
228
+ - **結果**: ✅ 対象外
229
+
230
+ #### API6: SSRF
231
+ - URL を受け取るフィールドなし
232
+ - **結果**: ✅ 対象外
233
+
234
+ #### API7: セキュリティの設定ミス
235
+ - 実測: `X-Request-Id`, `X-Content-Type-Options`, `X-Frame-Options` → ✅ 全レスポンスに付与
236
+ - 実測: 422 エラーレスポンスにスタックトレース含まれず → ✅
237
+ - CORS ワイルドカード: `grep allow_origins="*"` → ✅ PASS
238
+ - **結果**: ✅
239
+
240
+ #### API8〜10
241
+ - バージョン管理・デバッグエンドポイント・外部 API 消費: FT165 アプリでは対象なし
242
+ - **結果**: ✅ 対象外
243
+
244
+ ---
245
+
246
+ ### 2. インジェクション攻撃
247
+
248
+ #### SQL インジェクション
249
+ - `grep -rn 'f".*SELECT|INSERT|UPDATE|DELETE'` → **PASS(0 件)**
250
+ - FT165 アプリに DB 操作なし
251
+ - **結果**: ✅
252
+
253
+ #### コマンドインジェクション
254
+ - `grep -rn "shell=True\|os\.system"` → **PASS(0 件)**
255
+ - `random_module_usage_check()` 内の `subprocess.run()` は固定引数のみ(ユーザー入力なし)
256
+ - **結果**: ✅
257
+
258
+ #### パストラバーサル
259
+ - FT165 アプリにファイル操作なし
260
+ - **結果**: ✅ 対象外
261
+
262
+ #### SSTI
263
+ - テンプレートエンジン使用なし
264
+ - **結果**: ✅ 対象外
265
+
266
+ ---
267
+
268
+ ### 3. 認証・認可
269
+
270
+ - `random` モジュール使用: **PASS(0 件)**
271
+ - `secrets.compare_digest` / `hmac.compare_digest` の使用: **✅ 両方で正しく実装済み**
272
+ - `auth/local_verifier.py:36`: `secrets.compare_digest(token, allowed)`
273
+ - `security/webhook.py:29`: `hmac.compare_digest(expected, signature)`
274
+ - `SecretStr` 使用箇所: 2 件(設定クラス内)
275
+ - **結果**: ✅ 全 PASS
276
+
277
+ ---
278
+
279
+ ### 4. 入力バリデーション
280
+
281
+ - `CompareBody.a/b`: `max_length=512` → 実測で 413 文字超で 422 ✅
282
+ - `nbytes`: `ge=1, le=64` で境界チェック ✅
283
+ - `upper`: `ge=2, le=1_000_000` で境界チェック ✅
284
+ - タイプエラー: `nbytes="bad"` → 422(スタックトレースなし)✅
285
+ - **結果**: ✅
286
+
287
+ ---
288
+
289
+ ### 5. 情報漏洩
290
+
291
+ - 422 エラーに `traceback` / `File "` 含まれず ✅
292
+ - `SecretStr` でパスワード系フィールドを保護(2 箇所)✅
293
+ - セキュリティヘッダー全レスポンスに付与 ✅
294
+ - **結果**: ✅
295
+
296
+ ---
297
+
298
+ ### 6. Python / FastAPI 固有の攻撃ベクター
299
+
300
+ #### ReDoS
301
+ - `middleware/request_id.py` の UUID 正規表現: `^[0-9a-f]{8}-...-[0-9a-f]{12}$`
302
+ - 実測(悪意ある入力 `"a"*40+"!"`): **0.00ms** — バックトラッキング爆発なし ✅
303
+ - **結果**: ✅
304
+
305
+ #### pickle / yaml / eval
306
+ - `grep eval\|exec\|pickle.loads\|yaml.load` → **PASS(0 件)** ✅
307
+ - **結果**: ✅
308
+
309
+ #### 非同期レースコンディション・型強制攻撃
310
+ - FT165 はステートレスな計算 API のため共有状態なし ✅
311
+ - Pydantic: `int` フィールドに文字列 `"bad"` → 422(型強制失敗でエラー返却)✅
312
+ - **結果**: ✅
313
+
314
+ ---
315
+
316
+ ### 7. 依存関係の脆弱性スキャン
317
+
318
+ ```
319
+ uv run pip-audit
320
+ ```
321
+
322
+ **結果**:
323
+
324
+ | Package | Version | ID | Fix Versions |
325
+ |---|---|---|---|
326
+ | pyjwt | 2.12.1 | PYSEC-2025-183 | (未記載) |
327
+
328
+ **CRITICAL: 1 件**
329
+
330
+ **詳細分析**:
331
+ - PyJWT 2.12.1 は `PYSEC-2025-183` の影響を受ける
332
+ - `pip-audit` の Fix Versions 欄が空 → 修正版未リリースの可能性
333
+ - **実際の影響**: nene2-python のソースコード全体を検索した結果、`import jwt` / `import pyjwt` が実コードに存在しない(コメントのみ)
334
+ - PyJWT は `pyproject.toml` に直接依存として宣言されていたが **デッド依存** → 本 FT で削除済み
335
+ - **推移的依存として残存**: `mcp>=1.0` パッケージが `pyjwt[crypto]` を推移的依存として使用しているため、pip-audit では引き続き検出される
336
+
337
+ **対応方針**:
338
+ 1. `pyproject.toml` からの直接依存宣言: **本 FT で削除済み** (`uv remove pyjwt`)
339
+ 2. 推移的依存(mcp 経由): mcp 側の修正を待つ。Fix Versions が空のため修正版未リリース。Issue で追跡する。
340
+ 3. nene2-python のコードが PyJWT を直接使わないため、実際の攻撃面はゼロ。
341
+
342
+ ---
343
+
344
+ ### 診断サマリー
345
+
346
+ | カテゴリ | 結果 | 最重要発見 |
347
+ |---|---|---|
348
+ | OWASP API Security Top 10 | ✅ | スロットリング未設定(本番要対応) |
349
+ | SQL インジェクション | ✅ | — |
350
+ | コマンドインジェクション | ✅ | — |
351
+ | パストラバーサル | ✅ | — |
352
+ | SSTI | ✅ | — |
353
+ | 認証・認可 | ✅ | compare_digest 正しく実装 |
354
+ | 入力バリデーション | ✅ | — |
355
+ | 情報漏洩 | ✅ | — |
356
+ | ReDoS | ✅ | 0ms — 安全 |
357
+ | pickle / yaml / eval | ✅ | — |
358
+ | 非同期レースコンディション | ✅ | — |
359
+ | 型強制攻撃 | ✅ | — |
360
+ | 依存関係 CVE | ❌ | **PyJWT 2.12.1 PYSEC-2025-183(デッド依存)** |
361
+
362
+ **総合評価**: **条件付き合格**(PyJWT CVE を次 PR で対処すること)
363
+
364
+ **発見した脆弱性**: 1 件(CRITICAL: 1 — PyJWT デッド依存 CVE)
365
+
366
+ **新規セキュリティ Issue**: PyJWT 依存削除または更新
367
+
368
+ ---
369
+
370
+ ## Follow-up Issues
371
+
372
+ | 優先度 | タイトル | 種別 |
373
+ |---|---|---|
374
+ | 高 | `fix: pyproject.toml から未使用の pyjwt 直接依存を削除(PYSEC-2025-183)` ← **本 FT で対処済み** | security |
375
+ | 高 | `track: mcp の推移的依存 pyjwt PYSEC-2025-183 修正版リリース待ち` | security |
376
+ | 中 | `feat: nene2.security に generate_token() ヘルパーを追加(最低 128 bit 保証)` | feat |
377
+ | 低 | `docs: secrets vs random の使い分けを how-to に追加` | docs |
378
+
379
+ ---
380
+
381
+ ## まとめ
382
+
383
+ `secrets` モジュールは暗号学的安全なトークン生成・タイミング安全比較の標準手段。
384
+ 32 テスト全通過、摩擦ゼロで実装完了。
385
+
386
+ セキュリティ診断では nene2-python のソースコード全体を横断的に確認した。
387
+ `compare_digest` の正しい使用・`random` モジュール不使用・型バリデーション・セキュリティヘッダーはすべて合格。
388
+ **唯一の発見: PyJWT 2.12.1 のデッド依存 CVE (PYSEC-2025-183)**。
389
+ 実際に使われていない依存が CVE を持つことはそれ自体がリスクのため、即時削除 PR を作成する。
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nene2-python"
3
- version = "1.8.35"
3
+ version = "1.8.36"
4
4
  description = "NENE2 Python — minimal API framework following NENE2's design philosophy"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -32,7 +32,6 @@ dependencies = [
32
32
  "mcp>=1.0",
33
33
  "pyyaml>=6.0",
34
34
  "httpx>=0.27",
35
- "pyjwt>=2.12.0",
36
35
  ]
37
36
 
38
37
  [project.urls]
@@ -925,7 +925,7 @@ wheels = [
925
925
 
926
926
  [[package]]
927
927
  name = "nene2-python"
928
- version = "1.8.34"
928
+ version = "1.8.35"
929
929
  source = { editable = "." }
930
930
  dependencies = [
931
931
  { name = "alembic" },
@@ -934,7 +934,6 @@ dependencies = [
934
934
  { name = "mcp" },
935
935
  { name = "pydantic" },
936
936
  { name = "pydantic-settings" },
937
- { name = "pyjwt" },
938
937
  { name = "python-multipart" },
939
938
  { name = "pyyaml" },
940
939
  { name = "sqlalchemy" },
@@ -975,7 +974,6 @@ requires-dist = [
975
974
  { name = "pip-audit", marker = "extra == 'dev'", specifier = ">=2.7" },
976
975
  { name = "pydantic", specifier = ">=2.9" },
977
976
  { name = "pydantic-settings", specifier = ">=2.6" },
978
- { name = "pyjwt", specifier = ">=2.12.0" },
979
977
  { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3" },
980
978
  { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24" },
981
979
  { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=6.0" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes