@voxgig/sdkgen 4.10.0 → 4.11.0

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 (304) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/package.json +1 -1
  3. package/project/.sdk/model/feature/secrets.aon +330 -0
  4. package/project/.sdk/model/target/c.aon +15 -2
  5. package/project/.sdk/model/target/cpp.aon +18 -0
  6. package/project/.sdk/model/target/lua.aon +17 -0
  7. package/project/.sdk/model/target/ocaml.aon +48 -5
  8. package/project/.sdk/model/target/zig.aon +30 -4
  9. package/project/.sdk/src/cmp/c/Config_c.ts +338 -1
  10. package/project/.sdk/src/cmp/c/Main_c.ts +57 -2
  11. package/project/.sdk/src/cmp/c/Package_c.ts +12 -2
  12. package/project/.sdk/src/cmp/cpp/Config_cpp.ts +324 -2
  13. package/project/.sdk/src/cmp/cpp/Gitignore_cpp.ts +2 -0
  14. package/project/.sdk/src/cmp/cpp/Main_cpp.ts +62 -4
  15. package/project/.sdk/src/cmp/cpp/Package_cpp.ts +10 -0
  16. package/project/.sdk/src/cmp/lua/Config_lua.ts +88 -0
  17. package/project/.sdk/src/cmp/lua/Gitignore_lua.ts +4 -0
  18. package/project/.sdk/src/cmp/lua/Main_lua.ts +93 -3
  19. package/project/.sdk/src/cmp/lua/Package_lua.ts +60 -0
  20. package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +10 -0
  21. package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +183 -5
  22. package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +160 -3
  23. package/project/.sdk/src/cmp/ocaml/Package_ocaml.ts +25 -1
  24. package/project/.sdk/src/cmp/zig/Config_zig.ts +130 -9
  25. package/project/.sdk/src/cmp/zig/Main_zig.ts +208 -4
  26. package/project/.sdk/{tm/zig/build.zig → src/cmp/zig/fragment/Build.fragment.zig} +7 -0
  27. package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +25 -0
  28. package/project/.sdk/{tm/zig/root.zig → src/cmp/zig/fragment/Root.fragment.zig} +9 -0
  29. package/project/.sdk/tm/c/Makefile +58 -9
  30. package/project/.sdk/tm/c/core/sdk.h +10 -0
  31. package/project/.sdk/tm/c/feature/secrets/plugin/capability.c +133 -0
  32. package/project/.sdk/tm/c/feature/secrets/plugin/capability.h +39 -0
  33. package/project/.sdk/tm/c/feature/secrets/plugin/catalog.c +83 -0
  34. package/project/.sdk/tm/c/feature/secrets/plugin/catalog.h +48 -0
  35. package/project/.sdk/tm/c/feature/secrets/plugin/config.c +413 -0
  36. package/project/.sdk/tm/c/feature/secrets/plugin/config.h +24 -0
  37. package/project/.sdk/tm/c/feature/secrets/plugin/depend.c +264 -0
  38. package/project/.sdk/tm/c/feature/secrets/plugin/depend.h +29 -0
  39. package/project/.sdk/tm/c/feature/secrets/plugin/env.c +220 -0
  40. package/project/.sdk/tm/c/feature/secrets/plugin/env.h +19 -0
  41. package/project/.sdk/tm/c/feature/secrets/plugin/export.c +97 -0
  42. package/project/.sdk/tm/c/feature/secrets/plugin/export.h +19 -0
  43. package/project/.sdk/tm/c/feature/secrets/plugin/graph.c +237 -0
  44. package/project/.sdk/tm/c/feature/secrets/plugin/graph.h +17 -0
  45. package/project/.sdk/tm/c/feature/secrets/plugin/host.c +1494 -0
  46. package/project/.sdk/tm/c/feature/secrets/plugin/host.h +135 -0
  47. package/project/.sdk/tm/c/feature/secrets/plugin/order.c +288 -0
  48. package/project/.sdk/tm/c/feature/secrets/plugin/order.h +18 -0
  49. package/project/.sdk/tm/c/feature/secrets/plugin/point.c +141 -0
  50. package/project/.sdk/tm/c/feature/secrets/plugin/point.h +80 -0
  51. package/project/.sdk/tm/c/feature/secrets/plugin/ref.c +170 -0
  52. package/project/.sdk/tm/c/feature/secrets/plugin/ref.h +52 -0
  53. package/project/.sdk/tm/c/feature/secrets/plugin/resolve.c +98 -0
  54. package/project/.sdk/tm/c/feature/secrets/plugin/resolve.h +18 -0
  55. package/project/.sdk/tm/c/feature/secrets/plugin/types.c +150 -0
  56. package/project/.sdk/tm/c/feature/secrets/plugin/types.h +104 -0
  57. package/project/.sdk/tm/c/feature/secrets/plugin/value.c +649 -0
  58. package/project/.sdk/tm/c/feature/secrets/plugin/value.h +135 -0
  59. package/project/.sdk/tm/c/feature/secrets/plugin/version.c +151 -0
  60. package/project/.sdk/tm/c/feature/secrets/plugin/version.h +34 -0
  61. package/project/.sdk/tm/c/feature/secrets/plugins/aws.c +281 -0
  62. package/project/.sdk/tm/c/feature/secrets/plugins/azuresecrets.c +207 -0
  63. package/project/.sdk/tm/c/feature/secrets/plugins/boru.c +164 -0
  64. package/project/.sdk/tm/c/feature/secrets/plugins/clock.c +75 -0
  65. package/project/.sdk/tm/c/feature/secrets/plugins/doppler.c +141 -0
  66. package/project/.sdk/tm/c/feature/secrets/plugins/encode.c +144 -0
  67. package/project/.sdk/tm/c/feature/secrets/plugins/gcpsecrets.c +177 -0
  68. package/project/.sdk/tm/c/feature/secrets/plugins/hashicorp.c +239 -0
  69. package/project/.sdk/tm/c/feature/secrets/plugins/httpjson.c +599 -0
  70. package/project/.sdk/tm/c/feature/secrets/plugins/infisical.c +165 -0
  71. package/project/.sdk/tm/c/feature/secrets/plugins/onepassword.c +194 -0
  72. package/project/.sdk/tm/c/feature/secrets/plugins/proc.c +233 -0
  73. package/project/.sdk/tm/c/feature/secrets/plugins/secretspec.c +125 -0
  74. package/project/.sdk/tm/c/feature/secrets/plugins/sekretoplugins.h +125 -0
  75. package/project/.sdk/tm/c/feature/secrets/plugins/sha256.c +270 -0
  76. package/project/.sdk/tm/c/feature/secrets/plugins/sigv4.c +382 -0
  77. package/project/.sdk/tm/c/feature/secrets/plugins/support.h +153 -0
  78. package/project/.sdk/tm/c/feature/secrets/plugins/tls.c +420 -0
  79. package/project/.sdk/tm/c/feature/secrets/sekreto/internal.h +86 -0
  80. package/project/.sdk/tm/c/feature/secrets/sekreto/json.c +721 -0
  81. package/project/.sdk/tm/c/feature/secrets/sekreto/providers.c +813 -0
  82. package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.c +944 -0
  83. package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.h +490 -0
  84. package/project/.sdk/tm/c/feature/secrets/sekreto/util.c +442 -0
  85. package/project/.sdk/tm/c/feature/secrets.c +797 -0
  86. package/project/.sdk/tm/c/feature/secrets.h +46 -0
  87. package/project/.sdk/tm/c/src/feature/secrets/.gitkeep +0 -0
  88. package/project/.sdk/tm/c/tests/feature/secrets/secrets_test.c +802 -0
  89. package/project/.sdk/tm/clojure/feature/secrets/sdk/feature/secrets.clj +38 -11
  90. package/project/.sdk/tm/clojure/test/sdk/test/feature/secrets.clj +40 -6
  91. package/project/.sdk/tm/cpp/Makefile +48 -4
  92. package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.cpp +121 -0
  93. package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.hpp +41 -0
  94. package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.cpp +60 -0
  95. package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.hpp +66 -0
  96. package/project/.sdk/tm/cpp/feature/secrets/plugin/config.cpp +376 -0
  97. package/project/.sdk/tm/cpp/feature/secrets/plugin/config.hpp +29 -0
  98. package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.cpp +230 -0
  99. package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.hpp +31 -0
  100. package/project/.sdk/tm/cpp/feature/secrets/plugin/env.cpp +205 -0
  101. package/project/.sdk/tm/cpp/feature/secrets/plugin/env.hpp +25 -0
  102. package/project/.sdk/tm/cpp/feature/secrets/plugin/export.cpp +84 -0
  103. package/project/.sdk/tm/cpp/feature/secrets/plugin/export.hpp +24 -0
  104. package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.cpp +222 -0
  105. package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.hpp +21 -0
  106. package/project/.sdk/tm/cpp/feature/secrets/plugin/host.cpp +1200 -0
  107. package/project/.sdk/tm/cpp/feature/secrets/plugin/host.hpp +250 -0
  108. package/project/.sdk/tm/cpp/feature/secrets/plugin/order.cpp +250 -0
  109. package/project/.sdk/tm/cpp/feature/secrets/plugin/order.hpp +22 -0
  110. package/project/.sdk/tm/cpp/feature/secrets/plugin/point.cpp +110 -0
  111. package/project/.sdk/tm/cpp/feature/secrets/plugin/point.hpp +79 -0
  112. package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.cpp +136 -0
  113. package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.hpp +60 -0
  114. package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.cpp +82 -0
  115. package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.hpp +25 -0
  116. package/project/.sdk/tm/cpp/feature/secrets/plugin/types.cpp +90 -0
  117. package/project/.sdk/tm/cpp/feature/secrets/plugin/types.hpp +65 -0
  118. package/project/.sdk/tm/cpp/feature/secrets/plugin/value.cpp +491 -0
  119. package/project/.sdk/tm/cpp/feature/secrets/plugin/value.hpp +120 -0
  120. package/project/.sdk/tm/cpp/feature/secrets/plugin/version.cpp +143 -0
  121. package/project/.sdk/tm/cpp/feature/secrets/plugin/version.hpp +36 -0
  122. package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.cpp +248 -0
  123. package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.hpp +30 -0
  124. package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.cpp +149 -0
  125. package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.hpp +23 -0
  126. package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.cpp +117 -0
  127. package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.hpp +27 -0
  128. package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.cpp +180 -0
  129. package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.hpp +52 -0
  130. package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.cpp +87 -0
  131. package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.hpp +23 -0
  132. package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.cpp +121 -0
  133. package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.hpp +23 -0
  134. package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.cpp +153 -0
  135. package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.hpp +23 -0
  136. package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.cpp +666 -0
  137. package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.hpp +160 -0
  138. package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.cpp +123 -0
  139. package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.hpp +23 -0
  140. package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.cpp +125 -0
  141. package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.hpp +23 -0
  142. package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.cpp +223 -0
  143. package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.hpp +47 -0
  144. package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.cpp +104 -0
  145. package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.hpp +27 -0
  146. package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.cpp +242 -0
  147. package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.hpp +58 -0
  148. package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.cpp +260 -0
  149. package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.hpp +47 -0
  150. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.cpp +476 -0
  151. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.hpp +89 -0
  152. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.cpp +312 -0
  153. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.hpp +263 -0
  154. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.cpp +354 -0
  155. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.hpp +111 -0
  156. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.cpp +613 -0
  157. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.hpp +227 -0
  158. package/project/.sdk/tm/cpp/feature/secrets.hpp +745 -0
  159. package/project/.sdk/tm/cpp/src/feature/secrets/.gitkeep +0 -0
  160. package/project/.sdk/tm/cpp/test/feature/secrets/secrets_test.cpp +808 -0
  161. package/project/.sdk/tm/csharp/feature/SecretsFeature.cs +34 -8
  162. package/project/.sdk/tm/csharp/test/feature/secrets/SecretsFeatureTest.cs +85 -0
  163. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets.ex +17 -2
  164. package/project/.sdk/tm/elixir/test/feature/secrets/secrets_test.exs +41 -0
  165. package/project/.sdk/tm/go/feature/secrets_feature.go +32 -10
  166. package/project/.sdk/tm/go/test/feature/secrets/secrets_feature_test.go +116 -0
  167. package/project/.sdk/tm/java/feature/SecretsFeature.java +33 -8
  168. package/project/.sdk/tm/java/test/feature/secrets/SecretsFeatureTest.java +64 -0
  169. package/project/.sdk/tm/js/src/feature/secrets/SecretsFeature.js +34 -11
  170. package/project/.sdk/tm/js/test/feature/secrets/Secrets.test.js +73 -0
  171. package/project/.sdk/tm/kotlin/feature/SecretsFeature.kt +32 -7
  172. package/project/.sdk/tm/kotlin/test/feature/secrets/SecretsTest.kt +69 -0
  173. package/project/.sdk/tm/lua/Makefile +15 -4
  174. package/project/.sdk/tm/lua/feature/secrets/native/sekretonet.c +806 -0
  175. package/project/.sdk/tm/lua/feature/secrets/plugin/capability.lua +135 -0
  176. package/project/.sdk/tm/lua/feature/secrets/plugin/catalog.lua +63 -0
  177. package/project/.sdk/tm/lua/feature/secrets/plugin/config.lua +361 -0
  178. package/project/.sdk/tm/lua/feature/secrets/plugin/depend.lua +232 -0
  179. package/project/.sdk/tm/lua/feature/secrets/plugin/env.lua +168 -0
  180. package/project/.sdk/tm/lua/feature/secrets/plugin/export.lua +66 -0
  181. package/project/.sdk/tm/lua/feature/secrets/plugin/graph.lua +194 -0
  182. package/project/.sdk/tm/lua/feature/secrets/plugin/host.lua +1190 -0
  183. package/project/.sdk/tm/lua/feature/secrets/plugin/json.lua +169 -0
  184. package/project/.sdk/tm/lua/feature/secrets/plugin/order.lua +207 -0
  185. package/project/.sdk/tm/lua/feature/secrets/plugin/point.lua +106 -0
  186. package/project/.sdk/tm/lua/feature/secrets/plugin/ref.lua +115 -0
  187. package/project/.sdk/tm/lua/feature/secrets/plugin/resolve.lua +78 -0
  188. package/project/.sdk/tm/lua/feature/secrets/plugin/types.lua +275 -0
  189. package/project/.sdk/tm/lua/feature/secrets/plugin/version.lua +131 -0
  190. package/project/.sdk/tm/lua/feature/secrets/plugin.lua +76 -0
  191. package/project/.sdk/tm/lua/feature/secrets/sekreto/addr.lua +124 -0
  192. package/project/.sdk/tm/lua/feature/secrets/sekreto/err.lua +47 -0
  193. package/project/.sdk/tm/lua/feature/secrets/sekreto/name.lua +315 -0
  194. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/aws.lua +228 -0
  195. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/azuresecrets.lua +137 -0
  196. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/boru.lua +124 -0
  197. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/crypto.lua +212 -0
  198. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/doppler.lua +89 -0
  199. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/gcpsecrets.lua +129 -0
  200. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/hashicorp.lua +154 -0
  201. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/httpjson.lua +248 -0
  202. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/infisical.lua +113 -0
  203. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/json.lua +449 -0
  204. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/net.lua +198 -0
  205. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/onepassword.lua +126 -0
  206. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/secretspec.lua +93 -0
  207. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/sigv4.lua +269 -0
  208. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/support.lua +146 -0
  209. package/project/.sdk/tm/lua/feature/secrets/sekreto/providers.lua +346 -0
  210. package/project/.sdk/tm/lua/feature/secrets/sekreto.lua +479 -0
  211. package/project/.sdk/tm/lua/feature/secrets_feature.lua +690 -0
  212. package/project/.sdk/tm/lua/src/feature/secrets/.gitkeep +0 -0
  213. package/project/.sdk/tm/lua/test/extend_test.lua +105 -0
  214. package/project/.sdk/tm/lua/test/feature/secrets/secrets_feature_test.lua +862 -0
  215. package/project/.sdk/tm/ocaml/Makefile +52 -8
  216. package/project/.sdk/tm/ocaml/feature/secrets/plugin/capability.ml +106 -0
  217. package/project/.sdk/tm/ocaml/feature/secrets/plugin/catalog.ml +43 -0
  218. package/project/.sdk/tm/ocaml/feature/secrets/plugin/config.ml +349 -0
  219. package/project/.sdk/tm/ocaml/feature/secrets/plugin/defs.ml +143 -0
  220. package/project/.sdk/tm/ocaml/feature/secrets/plugin/depend.ml +217 -0
  221. package/project/.sdk/tm/ocaml/feature/secrets/plugin/env.ml +197 -0
  222. package/project/.sdk/tm/ocaml/feature/secrets/plugin/export.ml +89 -0
  223. package/project/.sdk/tm/ocaml/feature/secrets/plugin/graph.ml +227 -0
  224. package/project/.sdk/tm/ocaml/feature/secrets/plugin/host.ml +1112 -0
  225. package/project/.sdk/tm/ocaml/feature/secrets/plugin/order.ml +202 -0
  226. package/project/.sdk/tm/ocaml/feature/secrets/plugin/point.ml +138 -0
  227. package/project/.sdk/tm/ocaml/feature/secrets/plugin/ref.ml +126 -0
  228. package/project/.sdk/tm/ocaml/feature/secrets/plugin/resolve.ml +62 -0
  229. package/project/.sdk/tm/ocaml/feature/secrets/plugin/types.ml +97 -0
  230. package/project/.sdk/tm/ocaml/feature/secrets/plugin/value.ml +387 -0
  231. package/project/.sdk/tm/ocaml/feature/secrets/plugin/version.ml +139 -0
  232. package/project/.sdk/tm/ocaml/feature/secrets/plugins/aws.ml +171 -0
  233. package/project/.sdk/tm/ocaml/feature/secrets/plugins/azuresecrets.ml +116 -0
  234. package/project/.sdk/tm/ocaml/feature/secrets/plugins/boru.ml +95 -0
  235. package/project/.sdk/tm/ocaml/feature/secrets/plugins/crypto.ml +149 -0
  236. package/project/.sdk/tm/ocaml/feature/secrets/plugins/doppler.ml +61 -0
  237. package/project/.sdk/tm/ocaml/feature/secrets/plugins/gcpsecrets.ml +93 -0
  238. package/project/.sdk/tm/ocaml/feature/secrets/plugins/hashicorp.ml +120 -0
  239. package/project/.sdk/tm/ocaml/feature/secrets/plugins/http.ml +421 -0
  240. package/project/.sdk/tm/ocaml/feature/secrets/plugins/httpjson.ml +66 -0
  241. package/project/.sdk/tm/ocaml/feature/secrets/plugins/infisical.ml +85 -0
  242. package/project/.sdk/tm/ocaml/feature/secrets/plugins/onepassword.ml +112 -0
  243. package/project/.sdk/tm/ocaml/feature/secrets/plugins/runcmd.ml +97 -0
  244. package/project/.sdk/tm/ocaml/feature/secrets/plugins/secretspec.ml +68 -0
  245. package/project/.sdk/tm/ocaml/feature/secrets/plugins/sigv4.ml +259 -0
  246. package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls.ml +44 -0
  247. package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls_stubs.c +358 -0
  248. package/project/.sdk/tm/ocaml/feature/secrets/sekreto/json.ml +376 -0
  249. package/project/.sdk/tm/ocaml/feature/secrets/sekreto/provider.ml +647 -0
  250. package/project/.sdk/tm/ocaml/feature/secrets/sekreto/secret.ml +299 -0
  251. package/project/.sdk/tm/ocaml/feature/secrets/sekreto/sekreto.ml +347 -0
  252. package/project/.sdk/tm/ocaml/feature/secrets_feature.ml +453 -0
  253. package/project/.sdk/tm/ocaml/src/feature/secrets/.gitkeep +0 -0
  254. package/project/.sdk/tm/ocaml/test/feature/secrets/t_secrets.ml +670 -0
  255. package/project/.sdk/tm/perl/feature/secrets_feature.pm +33 -12
  256. package/project/.sdk/tm/perl/t/feature/secrets/secrets.t +47 -0
  257. package/project/.sdk/tm/php/feature/SecretsFeature.php +30 -6
  258. package/project/.sdk/tm/php/test/feature/secrets/SecretsTest.php +98 -0
  259. package/project/.sdk/tm/py/pkg/feature/secrets_feature.py +28 -6
  260. package/project/.sdk/tm/py/test/feature/secrets/test_secrets.py +114 -0
  261. package/project/.sdk/tm/rb/feature/secrets_feature.rb +28 -6
  262. package/project/.sdk/tm/rb/test/feature/secrets/secrets_feature_test.rb +93 -0
  263. package/project/.sdk/tm/rust/feature/secrets.rs +33 -10
  264. package/project/.sdk/tm/rust/tests/feature/secrets/main.rs +96 -0
  265. package/project/.sdk/tm/scala/feature/SecretsFeature.scala +29 -7
  266. package/project/.sdk/tm/scala/sdktest/feature/secrets/SecretsTestMain.scala +71 -0
  267. package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +35 -12
  268. package/project/.sdk/tm/ts/test/feature/secrets/Secrets.test.ts +73 -0
  269. package/project/.sdk/tm/zig/feature/secrets/plugin/capability.zig +137 -0
  270. package/project/.sdk/tm/zig/feature/secrets/plugin/catalog.zig +90 -0
  271. package/project/.sdk/tm/zig/feature/secrets/plugin/config.zig +346 -0
  272. package/project/.sdk/tm/zig/feature/secrets/plugin/depend.zig +244 -0
  273. package/project/.sdk/tm/zig/feature/secrets/plugin/env.zig +201 -0
  274. package/project/.sdk/tm/zig/feature/secrets/plugin/export.zig +83 -0
  275. package/project/.sdk/tm/zig/feature/secrets/plugin/graph.zig +212 -0
  276. package/project/.sdk/tm/zig/feature/secrets/plugin/host.zig +1224 -0
  277. package/project/.sdk/tm/zig/feature/secrets/plugin/inst.zig +126 -0
  278. package/project/.sdk/tm/zig/feature/secrets/plugin/order.zig +228 -0
  279. package/project/.sdk/tm/zig/feature/secrets/plugin/plugin.zig +34 -0
  280. package/project/.sdk/tm/zig/feature/secrets/plugin/point.zig +166 -0
  281. package/project/.sdk/tm/zig/feature/secrets/plugin/ref.zig +147 -0
  282. package/project/.sdk/tm/zig/feature/secrets/plugin/resolve.zig +74 -0
  283. package/project/.sdk/tm/zig/feature/secrets/plugin/types.zig +140 -0
  284. package/project/.sdk/tm/zig/feature/secrets/plugin/value.zig +662 -0
  285. package/project/.sdk/tm/zig/feature/secrets/plugin/version.zig +139 -0
  286. package/project/.sdk/tm/zig/feature/secrets/plugins/aws.zig +294 -0
  287. package/project/.sdk/tm/zig/feature/secrets/plugins/azuresecrets.zig +222 -0
  288. package/project/.sdk/tm/zig/feature/secrets/plugins/boru.zig +188 -0
  289. package/project/.sdk/tm/zig/feature/secrets/plugins/doppler.zig +135 -0
  290. package/project/.sdk/tm/zig/feature/secrets/plugins/gcpsecrets.zig +170 -0
  291. package/project/.sdk/tm/zig/feature/secrets/plugins/hashicorp.zig +242 -0
  292. package/project/.sdk/tm/zig/feature/secrets/plugins/httpjson.zig +535 -0
  293. package/project/.sdk/tm/zig/feature/secrets/plugins/infisical.zig +167 -0
  294. package/project/.sdk/tm/zig/feature/secrets/plugins/onepassword.zig +202 -0
  295. package/project/.sdk/tm/zig/feature/secrets/plugins/secretspec.zig +160 -0
  296. package/project/.sdk/tm/zig/feature/secrets/plugins/sigv4.zig +336 -0
  297. package/project/.sdk/tm/zig/feature/secrets/sekreto/addr.zig +148 -0
  298. package/project/.sdk/tm/zig/feature/secrets/sekreto/builtins.zig +259 -0
  299. package/project/.sdk/tm/zig/feature/secrets/sekreto/provider.zig +440 -0
  300. package/project/.sdk/tm/zig/feature/secrets/sekreto/sekreto.zig +944 -0
  301. package/project/.sdk/tm/zig/feature/secrets.zig +854 -0
  302. package/project/.sdk/tm/zig/src/feature/secrets/.gitkeep +0 -0
  303. package/project/.sdk/tm/zig/test/feature/secrets/secrets_test.zig +891 -0
  304. package/project/sdkgen-package.json +1 -1
@@ -325,10 +325,18 @@ sub transport {
325
325
  }
326
326
 
327
327
 
328
- # One resolution, shared by every request. A settled SUCCESS is kept only
329
- # when caching is on (`cache: false` means every resolve asks the chain
330
- # again); a FAILURE is never kept, so a transient vault outage cannot
331
- # poison the client permanently - the next request asks the chain again.
328
+ # One resolution, shared by every request. A settled HIT is kept only when
329
+ # caching is on (`cache: false` means every resolve asks the chain again);
330
+ # a FAILURE is never kept, so a transient vault outage cannot poison the
331
+ # client permanently - the next request asks the chain again.
332
+ #
333
+ # A MISS is not kept either, however caching is set. That rule is
334
+ # sekreto's, not this feature's: `A miss is never cached: the next read
335
+ # asks again`, in sekreto's own source. Keeping a settled miss here would
336
+ # override that from the layer above, and a secret provisioned after
337
+ # startup - a mounted file, a policy granted a minute late - would never be
338
+ # picked up for the life of the client. `cache` is about caching a HIT; it
339
+ # was never a promise to keep saying no.
332
340
  #
333
341
  # Returns an error MESSAGE (a string) or undef.
334
342
  sub resolve {
@@ -337,23 +345,26 @@ sub resolve {
337
345
  return $self->{initerr} if defined $self->{initerr};
338
346
  return undef if $self->{resolved} && $self->{cache};
339
347
 
340
- my $err = $self->_resolve_once();
341
- $self->{resolved} = defined($err) ? 0 : 1;
348
+ my ($err, $hit) = $self->_resolve_once();
349
+ $self->{resolved} = (defined($err) || !$hit) ? 0 : 1;
342
350
 
343
351
  return $err;
344
352
  }
345
353
 
346
354
 
355
+ # Resolve once, answering ($err, $hit): whether a credential came out of
356
+ # it. That flag is the whole of what resolve needs to tell a cacheable HIT
357
+ # from a miss it must not keep.
347
358
  sub _resolve_once {
348
359
  my ($self) = @_;
349
360
 
350
- return undef unless defined $self->{sek};
361
+ return (undef, 0) unless defined $self->{sek};
351
362
 
352
363
  # sekreto's miss-vs-error rule, inherited: `try` returns undef on a MISS
353
364
  # and dies on a provider ERROR.
354
365
  my $found;
355
366
  my $ok = eval { $found = $self->{sek}->try($self->{secretname}); 1 };
356
- return _trim(defined $@ && '' ne "$@" ? "$@" : 'secrets: provider failed')
367
+ return (_trim(defined $@ && '' ne "$@" ? "$@" : 'secrets: provider failed'), 0)
357
368
  if !$ok;
358
369
 
359
370
  if (!defined $self->{exchange}) {
@@ -363,7 +374,7 @@ sub _resolve_once {
363
374
  # way - it seats FIRST in the chain as a memory provider, so the chain
364
375
  # HITS while one is set and this branch is unreachable.)
365
376
  $self->{cred} = defined $found ? "$found" : '';
366
- return undef;
377
+ return (undef, defined($found) ? 1 : 0);
367
378
  }
368
379
 
369
380
  # Exchanging: what the chain resolved is the REFRESH token, kept for
@@ -381,13 +392,23 @@ sub _resolve_once {
381
392
  # A starting access token was supplied. Spend it: if it is stale the API
382
393
  # answers with an expiry status and the wrapper buys another, which is
383
394
  # the same path expiry takes anyway.
384
- return undef if '' ne $self->{cred};
395
+ return (undef, 1) if '' ne $self->{cred};
396
+
397
+ # `auth => undef` is the documented way to send NO credential, and a
398
+ # purchase is a credential-bearing call: the refresh token goes to the
399
+ # token endpoint in the request body. _with_refresh honours suppression
400
+ # for the RETRY, but it runs after this - by then the refresh token has
401
+ # already left the process, and no later check can call it back. The
402
+ # suppression has to be honoured here, before the first purchase, or it
403
+ # only ever half-held.
404
+ return (undef, 0)
405
+ if !defined ProjectNameHelpers::gp($self->{liveopts}, 'auth');
385
406
 
386
407
  my ($token, $err) = $self->_buy();
387
- return $err if defined $err;
408
+ return ($err, 0) if defined $err;
388
409
 
389
410
  $self->{cred} = $token;
390
- return undef;
411
+ return (undef, 1);
391
412
  }
392
413
 
393
414
 
@@ -510,6 +510,45 @@ SKIP: {
510
510
 
511
511
  # --- cache -----------------------------------------------------------------
512
512
 
513
+ # A MISS IS NOT A CACHEABLE ANSWER - sekreto's own rule, which this feature
514
+ # used to override from the layer above.
515
+ #
516
+ # DEFAULT caching here, which is the whole point: `cache: true` is about
517
+ # holding a HIT (the block after next pins that half), and keeping the
518
+ # settled resolution after a miss meant the chain was never asked again for
519
+ # the life of the client. A secret provisioned after startup (a mounted
520
+ # file, a vault policy granted a minute late) was invisible forever.
521
+ {
522
+ clear_env();
523
+
524
+ my $calls = 0;
525
+ my $present = 0;
526
+ my $late = {
527
+ 'lookup' => sub { $calls++; return $present ? 'LATEKEY01' : undef },
528
+ 'describe' => sub { 'late:test' },
529
+ };
530
+
531
+ my $stub = counting_fetch();
532
+ my $sdk = secrets_sdk($stub, { 'providers' => [$late] });
533
+
534
+ $sdk->direct({ 'path' => '/one' });
535
+ no_credential($stub->{calls}[0]{auth},
536
+ 'the chain has nothing yet, so no credential should go out');
537
+
538
+ my $asked = $calls;
539
+ ok(0 < $asked, 'the chain was asked at least once');
540
+
541
+ # The secret is provisioned while the client is live.
542
+ $present = 1;
543
+
544
+ $sdk->direct({ 'path' => '/two' });
545
+
546
+ ok($asked < $calls,
547
+ 'the MISS was cached: a secret that appears later can never be picked up');
548
+ credential_is($stub->{calls}[1]{auth}, 'LATEKEY01', 'the late secret goes out');
549
+ }
550
+
551
+
513
552
  {
514
553
  clear_env();
515
554
 
@@ -907,6 +946,14 @@ sub exchange_sdk {
907
946
  'exchange: a suppressed request must not be retried');
908
947
  no_credential(api_calls($stub)->[0]{auth},
909
948
  'exchange: no credential may be sent when auth is suppressed');
949
+
950
+ # AND NO PURCHASE. resolve runs before _with_refresh's suppression check,
951
+ # so the refresh token used to go to the token endpoint in a request body
952
+ # even here. Stopping the retry does not unsend it, and only the token
953
+ # endpoint can see this.
954
+ is(scalar @{ token_calls($stub) }, 0,
955
+ 'exchange: auth undef suppressed the credential but the refresh token '
956
+ . 'was still POSTed to the exchange endpoint');
910
957
  }
911
958
 
912
959
  {
@@ -309,6 +309,14 @@ class ProjectNameSecretsFeature extends ProjectNameBaseFeature
309
309
  // is never cached, so a transient vault outage does not poison the
310
310
  // client after the vault recovers.
311
311
  //
312
+ // A MISS is not cached either, however caching is set. That rule is
313
+ // sekreto's, not this feature's: `A miss is never cached: the next read
314
+ // asks again`, in sekreto's own source. Caching a settled miss here
315
+ // would override that from the layer above, and a secret provisioned
316
+ // after startup - a mounted file, a policy granted a minute late -
317
+ // would never be picked up for the life of the client. `cache` is about
318
+ // caching a HIT; it was never a promise to keep saying no.
319
+ //
312
320
  // Answers the error rather than throwing it: the caller is the
313
321
  // transport gate, whose contract is a [value, err] pair.
314
322
  public function resolve(): ?\Throwable
@@ -321,22 +329,25 @@ class ProjectNameSecretsFeature extends ProjectNameBaseFeature
321
329
  }
322
330
 
323
331
  try {
324
- $this->resolve_once();
332
+ $hit = $this->resolve_once();
325
333
  } catch (\Throwable $e) {
326
334
  return $e;
327
335
  }
328
336
 
329
- if ($this->cache) {
337
+ if ($this->cache && $hit) {
330
338
  $this->resolved = true;
331
339
  }
332
340
 
333
341
  return null;
334
342
  }
335
343
 
336
- private function resolve_once(): void
344
+ // Resolve once, answering whether a credential came out of it. That
345
+ // bool is the whole of what resolve() needs to tell a cacheable HIT
346
+ // from a miss it must not cache.
347
+ private function resolve_once(): bool
337
348
  {
338
349
  if (null === $this->sekreto) {
339
- return;
350
+ return false;
340
351
  }
341
352
 
342
353
  // Miss-vs-error: `try` answers null for "no store has it" and
@@ -355,7 +366,7 @@ class ProjectNameSecretsFeature extends ProjectNameBaseFeature
355
366
  // chain as a memory provider, so the chain HITS while one is
356
367
  // set and the miss branch is unreachable.)
357
368
  $this->setcred($found);
358
- return;
369
+ return null !== $found;
359
370
  }
360
371
 
361
372
  // Exchanging: what the chain resolved is the REFRESH token, kept
@@ -377,10 +388,23 @@ class ProjectNameSecretsFeature extends ProjectNameBaseFeature
377
388
  // A starting access token was supplied. Spend it: if it is
378
389
  // stale the API answers with an expiry status and the wrapper
379
390
  // buys another, which is the same path expiry takes anyway.
380
- return;
391
+ return true;
392
+ }
393
+
394
+ // `auth: null` is the documented way to send NO credential, and a
395
+ // purchase is a credential-bearing call: the refresh token goes to
396
+ // the token endpoint in the request body. with_refresh honours
397
+ // suppression for the RETRY, but it runs after this - by then the
398
+ // refresh token has already left the process, and no later check
399
+ // can call it back. The suppression has to be honoured here, before
400
+ // the first purchase, or it only ever half-held.
401
+ if (null === ($this->liveopts['auth'] ?? null)) {
402
+ return false;
381
403
  }
382
404
 
383
405
  $this->buy();
406
+
407
+ return true;
384
408
  }
385
409
 
386
410
  // Buy a token and try the request again when the API says the current
@@ -595,6 +595,74 @@ class ProjectNameSecretsTest extends TestCase
595
595
  'the wire saw ' . var_export($last['auth'], true));
596
596
  }
597
597
 
598
+ // A MISS IS NOT A CACHEABLE ANSWER - sekreto's own rule, which this
599
+ // feature used to override from the layer above.
600
+ //
601
+ // DEFAULT caching here, which is the whole point: `cache: true` is
602
+ // about holding a HIT, and caching the settled resolution after a miss
603
+ // meant the chain was never asked again for the life of the client. A
604
+ // secret provisioned after startup (a mounted file, a vault policy
605
+ // granted a minute late) was invisible forever, and the only workaround
606
+ // was giving up hit caching entirely.
607
+ public function testACachedMissIsReasked(): void
608
+ {
609
+ $present = false;
610
+ $calls = 0;
611
+ $wire = new ProjectNameSecretsWire();
612
+ $client = $this->secretsClient($wire, [
613
+ 'feature' => ['secrets' => [
614
+ 'active' => true,
615
+ 'providers' => [$this->customProvider(
616
+ function (string $_name) use (&$present, &$calls) {
617
+ $calls++;
618
+ return $present ? 'LATEKEY01' : null;
619
+ })],
620
+ ]],
621
+ ]);
622
+
623
+ $this->driveOp($client, $wire);
624
+ $first = $wire->api()[0];
625
+ $this->assertFalse($first['has'] && '' !== (string)$first['auth'],
626
+ 'the chain has nothing yet, so no credential should go out');
627
+
628
+ $asked = $calls;
629
+ $this->assertGreaterThan(0, $asked);
630
+
631
+ // The secret is provisioned while the client is live.
632
+ $present = true;
633
+
634
+ $this->driveOp($client, $wire);
635
+ $api = $wire->api();
636
+ $this->assertCredential($api[count($api) - 1]['auth'], 'LATEKEY01');
637
+
638
+ $this->assertGreaterThan($asked, $calls,
639
+ 'the MISS was cached: a secret that appears later can never be picked up');
640
+ }
641
+
642
+ // The other half of the same rule: a HIT is still cached by default, so
643
+ // the fix above must not turn every request into a chain walk.
644
+ public function testACachedHitIsKept(): void
645
+ {
646
+ $calls = 0;
647
+ $wire = new ProjectNameSecretsWire();
648
+ $client = $this->secretsClient($wire, [
649
+ 'feature' => ['secrets' => [
650
+ 'active' => true,
651
+ 'providers' => [$this->customProvider(
652
+ function (string $_name) use (&$calls) {
653
+ $calls++;
654
+ return 'STABLEKEY01';
655
+ })],
656
+ ]],
657
+ ]);
658
+
659
+ $this->driveOp($client, $wire);
660
+ $this->driveOp($client, $wire);
661
+
662
+ $this->assertSame(1, $calls,
663
+ 'a hit must be cached under the default cache: true');
664
+ }
665
+
598
666
  public function testAuthNullSuppressesTheCredentialChainOrNoChain(): void
599
667
  {
600
668
  $this->setenv('APIKEY', 'ENVKEY03');
@@ -708,6 +776,36 @@ class ProjectNameSecretsTest extends TestCase
708
776
  $this->assertCredential($api[1]['auth'], 'ACCESS02');
709
777
  }
710
778
 
779
+ // `auth: null` SUPPRESSES THE PURCHASE, not just the retry.
780
+ //
781
+ // resolve() runs before with_refresh's suppression check, so the
782
+ // refresh token used to go to the token endpoint in a request body even
783
+ // here. Stopping the retry does not unsend it - this asserts on the
784
+ // token endpoint, which is the half the API-header assertions cannot
785
+ // see.
786
+ public function testAuthNullBuysNoTokenAtAll(): void
787
+ {
788
+ $this->setenv('REFRESH_TOKEN', 'REFRESH01');
789
+ $wire = new ProjectNameSecretsWire();
790
+ $wire->apistatus = [401];
791
+
792
+ $client = $this->secretsClient($wire, [
793
+ 'auth' => null,
794
+ 'feature' => $this->secretsOpts([
795
+ 'name' => 'refresh_token',
796
+ 'exchange' => ['active' => true],
797
+ ]),
798
+ ]);
799
+
800
+ $this->driveOp($client, $wire);
801
+
802
+ $this->assertCount(0, $wire->token(),
803
+ 'auth null suppressed the credential but the refresh token was ' .
804
+ 'still POSTed to the exchange endpoint');
805
+ $this->assertFalse($wire->api()[0]['has'],
806
+ 'no credential may be sent when auth is suppressed');
807
+ }
808
+
711
809
  // A second refusal on a token bought moments ago is a real failure, not
712
810
  // a spin: exactly one rebuy, and the refusal is returned as it stands.
713
811
  public function testASecondRefusalIsReturnedAsIs(): void
@@ -201,21 +201,31 @@ class ProjectNameSecretsFeature(ProjectNameBaseFeature):
201
201
  def PreSpec(self, _ctx):
202
202
  return self.resolve()
203
203
 
204
- # Resolve the apikey before the first request. A successful resolve is
204
+ # Resolve the apikey before the first request. A successful HIT is
205
205
  # reused while caching is on; with `cache: False` the chain is asked
206
206
  # again on every call - holding the first answer forever would make
207
207
  # the documented option a lie. A provider ERROR raises (fails the op)
208
208
  # and leaves nothing cached, so a transient vault outage does not
209
209
  # poison the client after the vault recovers.
210
+ #
211
+ # A MISS is not marked resolved either, however caching is set. That
212
+ # rule is sekreto's, not this feature's: `A miss is never cached: the
213
+ # next read asks again`, in sekreto's own source. Marking a settled
214
+ # miss here would override that from the layer above, and a secret
215
+ # provisioned after startup - a mounted file, a policy granted a minute
216
+ # late - would never be picked up for the life of the client. `cache`
217
+ # is about caching a HIT; it was never a promise to keep saying no.
210
218
  def resolve(self):
211
219
  if self._resolved and self._cache:
212
220
  return
213
- self._resolve_once()
214
- self._resolved = True
221
+ self._resolved = self._resolve_once()
215
222
 
223
+ # Resolve once, reporting whether a credential came out of it. That
224
+ # bool is the whole of what resolve() needs to tell a cacheable HIT
225
+ # from a miss it must not retain.
216
226
  def _resolve_once(self):
217
227
  if self._sekreto is None:
218
- return
228
+ return False
219
229
 
220
230
  # Miss-vs-error: `try_` answers None for "no store has it" and
221
231
  # RAISES for "a store could not answer" - only the miss falls
@@ -242,7 +252,7 @@ class ProjectNameSecretsFeature(ProjectNameBaseFeature):
242
252
  if self._client.options.get("apikey") == self._lastset:
243
253
  self._client.options["apikey"] = ""
244
254
  self._lastset = None
245
- return
255
+ return found is not None
246
256
 
247
257
  # Exchanging: what the chain resolved is the REFRESH token, kept
248
258
  # for every later purchase. A miss is not fatal here - an explicit
@@ -255,10 +265,22 @@ class ProjectNameSecretsFeature(ProjectNameBaseFeature):
255
265
  # A starting access token was supplied. Spend it: if it is
256
266
  # stale the API answers 401 and the transport wrapper buys
257
267
  # another, which is the same path expiry takes anyway.
258
- return
268
+ return True
269
+
270
+ # `auth: None` is the documented way to send NO credential, and a
271
+ # purchase is a credential-bearing call: the refresh token goes to
272
+ # the token endpoint in the request body. _with_refresh honours
273
+ # suppression for the RETRY, but it runs after this - by then the
274
+ # refresh token has already left the process, and no later check
275
+ # can call it back. The suppression has to be honoured here, before
276
+ # the first purchase, or it only ever half-held.
277
+ if self._client.options_map().get("auth") is None:
278
+ return False
259
279
 
260
280
  self._client.options["apikey"] = self._buy()
261
281
 
282
+ return True
283
+
262
284
  # Buy an access token with the refresh token. (ts shares one in-flight
263
285
  # purchase between concurrent async callers; python resolves
264
286
  # synchronously, so the sharing that remains observable is the
@@ -107,6 +107,18 @@ class TestSecrets(unittest.TestCase):
107
107
  self.assertEqual(1, len(transport.calls))
108
108
  return transport.calls[0]["fetchdef"].get("headers") or {}
109
109
 
110
+ def _lastwire(self, sdk, transport):
111
+ """Drive one operation and read the headers off the LAST call, so a
112
+ test can operate more than once on the same client."""
113
+ utility = sdk._utility
114
+ ctx = utility.make_context({"opname": "load"}, sdk.get_root_ctx())
115
+ utility.feature_hook(ctx, "PreSpec")
116
+
117
+ fetchdef = sdk.prepare({"path": "/"})
118
+ res, err = utility.fetcher(ctx, fetchdef["url"], fetchdef)
119
+ self.assertIsNone(err)
120
+ return transport.calls[-1]["fetchdef"].get("headers") or {}
121
+
110
122
  def assert_credential(self, headers, token):
111
123
  # The Authorization header carries the SPEC's credential prefix,
112
124
  # which a TEMPLATE cannot know - assert on the CREDENTIAL and let
@@ -196,6 +208,71 @@ class TestSecrets(unittest.TestCase):
196
208
  with self.assertRaises(RuntimeError):
197
209
  utility.feature_hook(ctx, "PreSpec")
198
210
 
211
+ # --- a miss is not a cacheable answer ---
212
+
213
+ # sekreto's own rule, which this feature used to override from the
214
+ # layer above: `A miss is never cached: the next read asks again`.
215
+ #
216
+ # DEFAULT caching in both tests below, which is the whole point.
217
+ # `cache: True` is about holding a HIT; holding the settled miss meant
218
+ # the chain was never asked again for the life of the client, so a
219
+ # secret provisioned after startup (a mounted file, a vault policy
220
+ # granted a minute late) was invisible forever.
221
+ def test_a_cached_miss_is_reasked(self):
222
+ state = {"present": False, "calls": 0}
223
+
224
+ def lookup(name):
225
+ state["calls"] += 1
226
+ return "LATEKEY01" if state["present"] else None
227
+
228
+ sdk, transport = self._sdk({
229
+ "feature": {"secrets": {
230
+ "active": True,
231
+ "providers": [{"lookup": lookup, "describe": lambda: "late:test"}],
232
+ }},
233
+ })
234
+
235
+ headers = self._lastwire(sdk, transport)
236
+ self.assertNotIn("authorization", headers,
237
+ "the chain has nothing yet, so no credential "
238
+ "should go out")
239
+
240
+ asked = state["calls"]
241
+ self.assertLess(0, asked)
242
+
243
+ # The secret is provisioned while the client is live.
244
+ state["present"] = True
245
+
246
+ headers = self._lastwire(sdk, transport)
247
+ self.assert_credential(headers, "LATEKEY01")
248
+
249
+ self.assertLess(asked, state["calls"],
250
+ "the MISS was cached: a secret that appears later "
251
+ "can never be picked up")
252
+
253
+ def test_a_cached_hit_is_kept(self):
254
+ # The other half of the same rule: a HIT is still cached by
255
+ # default, so the fix above must not turn every request into a
256
+ # chain walk.
257
+ state = {"calls": 0}
258
+
259
+ def lookup(name):
260
+ state["calls"] += 1
261
+ return "STABLEKEY01"
262
+
263
+ sdk, transport = self._sdk({
264
+ "feature": {"secrets": {
265
+ "active": True,
266
+ "providers": [{"lookup": lookup, "describe": lambda: "counting:test"}],
267
+ }},
268
+ })
269
+
270
+ self._lastwire(sdk, transport)
271
+ self._lastwire(sdk, transport)
272
+
273
+ self.assertEqual(1, state["calls"],
274
+ "a hit must be cached under the default cache: True")
275
+
199
276
  # --- auth: None suppression ---
200
277
 
201
278
  def test_auth_none_still_suppresses_the_credential(self):
@@ -283,6 +360,43 @@ class TestSecretsReviewPins(unittest.TestCase):
283
360
  srv.shutdown()
284
361
  srv.server_close()
285
362
 
363
+ def test_auth_none_buys_no_token_at_all(self):
364
+ # `auth: None` SUPPRESSES THE PURCHASE, not just the retry.
365
+ #
366
+ # resolve() runs before _with_refresh's suppression check, so the
367
+ # refresh token used to go to the token endpoint in a request body
368
+ # even here. Stopping the retry does not unsend it - and only the
369
+ # token endpoint can see this, which is why the API-header
370
+ # assertion above is not enough.
371
+ tricky = "REFRESH01"
372
+ _TokenEndpoint.refresh_seen = None
373
+
374
+ srv = http.server.HTTPServer(("127.0.0.1", 0), _TokenEndpoint)
375
+ thread = threading.Thread(target=srv.serve_forever, daemon=True)
376
+ thread.start()
377
+ try:
378
+ sdk = secrets_sdk({
379
+ "base": "http://127.0.0.1:%d/api" % srv.server_port,
380
+ "auth": None,
381
+ "feature": {"secrets": {
382
+ "active": True,
383
+ "name": "refresh_token",
384
+ "exchange": {"active": True, "refresh": tricky},
385
+ }},
386
+ })
387
+
388
+ utility = sdk._utility
389
+ ctx = utility.make_context({"opname": "load"}, sdk.get_root_ctx())
390
+ utility.feature_hook(ctx, "PreSpec")
391
+
392
+ self.assertIsNone(_TokenEndpoint.refresh_seen,
393
+ "auth: None suppressed the credential but the "
394
+ "refresh token was still POSTed to the "
395
+ "exchange endpoint")
396
+ finally:
397
+ srv.shutdown()
398
+ srv.server_close()
399
+
286
400
  def test_uncached_miss_retracts_the_credential(self):
287
401
  # `cache: False` re-asks the chain per operation; when a provider
288
402
  # that answered once reports a MISS (a revoked secret), the value
@@ -222,11 +222,19 @@ class ProjectNameSecretsFeature < ProjectNameBaseFeature
222
222
  _with_refresh(ctx, url, fetchdef, inner)
223
223
  end
224
224
 
225
- # One resolution, shared by every concurrent caller. A settled SUCCESS is
225
+ # One resolution, shared by every concurrent caller. A settled HIT is
226
226
  # kept only when caching is on (`cache: false` means every request asks
227
227
  # the chain again); a FAILURE is never cached, so a transient vault
228
228
  # outage does not poison the client after the vault recovers.
229
229
  #
230
+ # A MISS is not kept either, however caching is set. That rule is
231
+ # sekreto's, not this feature's: `A miss is never cached: the next read
232
+ # asks again`, in sekreto's own source. Keeping a settled miss here would
233
+ # override that from the layer above, and a secret provisioned after
234
+ # startup - a mounted file, a policy granted a minute late - would never
235
+ # be picked up for the life of the client. `cache` is about caching a
236
+ # HIT; it was never a promise to keep saying no.
237
+ #
230
238
  # Answers the error rather than raising it: the caller is the transport
231
239
  # gate, whose contract is a [value, err] pair.
232
240
  def resolve
@@ -236,20 +244,23 @@ class ProjectNameSecretsFeature < ProjectNameBaseFeature
236
244
  return nil if @resolved
237
245
 
238
246
  begin
239
- _resolve_once
247
+ hit = _resolve_once
240
248
  rescue StandardError => e
241
249
  return e
242
250
  end
243
251
 
244
- @resolved = true if @cache
252
+ @resolved = true if @cache && hit
245
253
  nil
246
254
  end
247
255
  end
248
256
 
249
257
  private
250
258
 
259
+ # Resolve once, answering whether a credential came out of it. That
260
+ # boolean is the whole of what resolve needs to tell a cacheable HIT from
261
+ # a miss it must not keep.
251
262
  def _resolve_once
252
- return if @sekreto.nil?
263
+ return false if @sekreto.nil?
253
264
 
254
265
  # Miss-vs-error: `try` answers nil for "no store has it" and RAISES for
255
266
  # "a store could not answer" - only the miss falls through.
@@ -264,7 +275,7 @@ class ProjectNameSecretsFeature < ProjectNameBaseFeature
264
275
  # the chain HITS while one is set and the miss branch is
265
276
  # unreachable.)
266
277
  _setcred(found)
267
- return
278
+ return !found.nil?
268
279
  end
269
280
 
270
281
  # Exchanging: what the chain resolved is the REFRESH token, kept for
@@ -283,9 +294,20 @@ class ProjectNameSecretsFeature < ProjectNameBaseFeature
283
294
  # A starting access token was supplied. Spend it: if it is stale the
284
295
  # API answers with an expiry status and the wrapper buys another, which
285
296
  # is the same path expiry takes anyway.
286
- return unless apikey.nil? || apikey.empty?
297
+ return true unless apikey.nil? || apikey.empty?
298
+
299
+ # `auth: nil` is the documented way to send NO credential, and a
300
+ # purchase is a credential-bearing call: the refresh token goes to the
301
+ # token endpoint in the request body. _with_refresh honours suppression
302
+ # for the RETRY, but it runs after this - by then the refresh token has
303
+ # already left the process, and no later check can call it back. The
304
+ # suppression has to be honoured here, before the first purchase, or it
305
+ # only ever half-held.
306
+ return false if @liveopts["auth"].nil?
287
307
 
288
308
  _buy
309
+
310
+ true
289
311
  end
290
312
 
291
313
  # Buy a token and try the request again when the API says the current one