@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
@@ -0,0 +1,745 @@
1
+ // ProjectName SDK — secrets feature: secret access via a vendored
2
+ // @voxgig/sekreto provider chain, and the access-token exchange some APIs
3
+ // require on top of it. The cpp port of tm/ts/src/feature/secrets/
4
+ // SecretsFeature.ts, following the GO port's structure
5
+ // (tm/go/feature/secrets_feature.go) - same contract, C++ idiom.
6
+ //
7
+ // The SDK's `apikey` option keeps exactly its old meaning: an explicit
8
+ // credential given in code. This feature makes it ONE SOURCE among several
9
+ // rather than the only one: when active, the apikey is resolved through a
10
+ // sekreto chain in which the explicit option (when set) is the FIRST
11
+ // provider - a `memory` store named `options` - so an explicit value always
12
+ // wins, by sekreto's own first-hit rule rather than by special-case logic.
13
+ //
14
+ // MISS vs ERROR (sekreto's invariant): a provider MISS falls through - the
15
+ // op proceeds, unauthenticated if nothing else supplies a credential. A
16
+ // provider ERROR (unreachable vault, bad creds) must FAIL the op: a broken
17
+ // vault never degrades into an unauthenticated request.
18
+ //
19
+ // WHERE RESOLUTION HAPPENS, and why it is not the PreSpec hook. cpp's hooks
20
+ // are `void` virtuals (core/types.hpp Feature): a hook cannot fail an
21
+ // operation the way ts's awaited rejection can - and SdkClient::direct /
22
+ // SdkClient::graphql run no feature hooks at all (rawRequest calls
23
+ // `u->fetcher` directly). So, exactly as in go, rust and c, resolution
24
+ // happens at the TRANSPORT SEAM: `Utility::fetcher`, the one place every
25
+ // wire path crosses (entity ops through makeRequest, the raw paths through
26
+ // rawRequest, both reading the client's one Utility - features see the
27
+ // client's own Utility in init, and entities take a snapshot copy of the
28
+ // already-wrapped function). The wrapper refuses to send while the last
29
+ // resolution stands failed, which is fail-closed for the entity pipeline
30
+ // and the raw paths alike, with one implementation.
31
+ //
32
+ // WHERE THE CREDENTIAL LIVES, and why it is not the options map. The ts
33
+ // reference writes `options.apikey`; this port does NOT. prepareAuth builds
34
+ // the header from the client's options on every request, and this feature
35
+ // rewrites that header at the seam from a value it holds itself - same
36
+ // construction, same suppression rules - so `optionsMap()` stays exactly
37
+ // what the caller passed, and the raw paths and the entity path cannot
38
+ // disagree about it. go's structural rule, and in cpp it is load-bearing:
39
+ // the timeout feature runs the inner transport on a DETACHED thread
40
+ // (feature/timeout.hpp), so this wrapper genuinely is entered concurrently.
41
+ //
42
+ // THREAD SAFETY, stated because the parity table cannot check it. The
43
+ // vendored sekreto port disclaims it (Provider.hpp: the slot table is
44
+ // shared, and "this port ... does not claim to support" two threads building
45
+ // chains at once), and its read cache is a mutated std::vector. So EVERY
46
+ // touch of the chain, the credential and the exchange state happens under
47
+ // ONE mutex (`mu_`): a resolution or a token purchase runs to completion
48
+ // before another thread reads the credential, which is go's "share the one
49
+ // in-flight resolution/purchase" with the mutex doing the sharing - a thread
50
+ // that arrives during a purchase blocks, then finds a fresh token and does
51
+ // not buy again (the `current != used` check below). The inner transport is
52
+ // ALWAYS called outside the lock, so a slow request never serialises the
53
+ // others. The mutex is recursive because a token purchase may run a
54
+ // caller-supplied options.system.fetch, and a callback that re-enters this
55
+ // client (a nested direct(), say) must not deadlock on its own thread.
56
+ //
57
+ // EXCHANGE: some APIs will not take a long-lived credential at all. What
58
+ // the chain resolves is then a REFRESH token, which buys a short-lived
59
+ // ACCESS token from a token endpoint (`exchange.path`, relative to
60
+ // options.base); the access token is what every request carries, and when
61
+ // a response status in `exchange.statuses` (401) says it is spent the
62
+ // wrapper buys another and retries the same request once. Test mode buys
63
+ // nothing and answers with a deterministic fake token.
64
+ //
65
+ // THE cpp-SPECIFIC SEAMS:
66
+ //
67
+ // * A live provider object cannot travel in the options: sdk::Value is a
68
+ // closed union (undef|null|bool|number|string|list|map|Injector|Modify),
69
+ // so go's `providers: [&customProvider{}]` has no cpp spelling. The
70
+ // answer is the `custom` pseudo-kind, as in c: a providers entry that is
71
+ // an Injector, or a map `{ kind: "custom", lookup: <Injector>, name?:
72
+ // "..." }`, bridged by a sekreto Provider whose lookup calls the
73
+ // Injector the way the SDK calls options.system.fetch - args is a LIST
74
+ // holding the secret NAME - and reads a STRING (a hit), undef/null (a
75
+ // MISS - the chain continues) or a map `{ "__err__": "..." }` (an ERROR
76
+ // - the operation fails). Conflating the last two is the failure this
77
+ // library exists to prevent. The Injector itself never enters sekreto:
78
+ // the spec carries a slot ticket in `values` and a per-feature `custom`
79
+ // Definition (sekreto::providerplugin) builds the bridge from it, which
80
+ // is how upstream's own provider slots work.
81
+ //
82
+ // * The cpp core ships no HTTP client (utility/pipeline.hpp fetcher). The
83
+ // exchange therefore uses options.system.fetch when supplied - the seam
84
+ // every live cpp request already lives under - and otherwise
85
+ // `secrets_rawfetch`, which lives in the GENERATED feature/secrets/
86
+ // kinds.cpp: the vendored sekreto HTTPS client when a plugin group is
87
+ // active, a transport error that says so when none is (see Config_cpp).
88
+ //
89
+ // * The SDK is header-only and the vendored port is not. This header
90
+ // declares what the port defines; the generated feature/secrets/kinds.mk
91
+ // (read by tm/cpp/Makefile, which names no feature) has the port
92
+ // compiled into libsdkfeature.a, and core/config.hpp includes this
93
+ // header only when the model activates the feature - so a tree without
94
+ // the feature never sees a sekreto declaration.
95
+ //
96
+ // * cpp has no `extend` option (core/types.hpp), so the shipped suite
97
+ // adopts the feature two ways: the generated makeFeature("secrets") on a
98
+ // LIVE client, and direct construction through test/harness.hpp fhMake.
99
+
100
+ #ifndef SDK_FEATURE_SECRETS_HPP
101
+ #define SDK_FEATURE_SECRETS_HPP
102
+
103
+ #include <algorithm>
104
+ #include <cstdlib>
105
+ #include <functional>
106
+ #include <memory>
107
+ #include <mutex>
108
+ #include <optional>
109
+ #include <string>
110
+ #include <utility>
111
+ #include <vector>
112
+
113
+ #include "../core/types.hpp"
114
+ #include "base.hpp"
115
+ #include "options.hpp"
116
+
117
+ // The vendored @voxgig/sekreto port (multi-TU; compiled by the Makefile into
118
+ // libsdkfeature.a through the generated kinds.mk). Its headers reach voxgig/plugin by explicit ../plugin/
119
+ // paths, so no include path is involved and no SDK header is shadowed.
120
+ #include "secrets/sekreto/Providers.hpp"
121
+ #include "secrets/sekreto/Sekreto.hpp"
122
+
123
+ namespace sdk {
124
+
125
+ // ---- the generated wiring (feature/secrets/kinds.cpp, Config_cpp) ---------
126
+ //
127
+ // Ordinary extern declarations, defined by the generated translation unit:
128
+ // the plugin definitions the model selected (type-erased, so core/config.hpp
129
+ // can declare the same function without naming a plugin type), and the
130
+ // token-exchange transport of last resort.
131
+
132
+ std::vector<std::shared_ptr<void>> secrets_plugins();
133
+
134
+ struct SecretsRawResponse {
135
+ bool ok = false;
136
+ int status = 0;
137
+ std::string body;
138
+ std::string err;
139
+ };
140
+
141
+ SecretsRawResponse secrets_rawfetch(
142
+ const std::string& method, const std::string& url,
143
+ const std::vector<std::pair<std::string, std::string>>& headers,
144
+ const std::string& body);
145
+
146
+
147
+ // ---- the custom-provider bridge --------------------------------------------
148
+
149
+ class SecretsCustomProvider : public sekreto::Provider {
150
+ public:
151
+ explicit SecretsCustomProvider(Value fn) : fn_(std::move(fn)) {}
152
+
153
+ std::optional<std::string> lookup(const std::string& name) override {
154
+ vs::Injection inj(Value::undef(), Value::undef());
155
+ Value got = fn_.as_injector()(inj, vlist({Value(name)}), std::string(""), Value::undef());
156
+ if (got.is_string()) return got.as_string();
157
+ if (got.is_map()) {
158
+ Value emsg = getp(got, "__err__");
159
+ if (emsg.is_string()) throw sekreto::SekretoError(emsg.as_string());
160
+ }
161
+ // undef, null, or anything that is not a value: a MISS.
162
+ return std::nullopt;
163
+ }
164
+
165
+ std::string describe() const override { return "custom"; }
166
+
167
+ private:
168
+ Value fn_;
169
+ };
170
+
171
+
172
+ class SecretsFeature : public BaseFeature {
173
+ public:
174
+ using Fetcher = std::function<Value(CtxPtr, const std::string&, const Value&)>;
175
+
176
+ struct Exchange {
177
+ std::string path = "auth/token";
178
+ std::string method = "POST";
179
+ std::string request = "refresh_token";
180
+ std::string response = "access_token";
181
+ std::vector<int> statuses;
182
+ int retries = 1;
183
+ };
184
+
185
+ struct Track {
186
+ long long resolves = 0; // chain resolutions that completed (hit or miss)
187
+ long long buys = 0; // token purchases (test mode included)
188
+ long long refused = 0; // requests the fail-closed gate refused to send
189
+ };
190
+
191
+ SdkClient* client = nullptr;
192
+ Value options = Value::undef();
193
+
194
+ SecretsFeature() : BaseFeature("secrets", "0.1.0", true) {}
195
+
196
+ // Init is sync by feature contract: build the chain, never look anything
197
+ // up here.
198
+ void init(CtxPtr ctx, const Value& options_) override {
199
+ options = options_;
200
+ active = fopt::foptBool(options, "active", false);
201
+ if (!active) return;
202
+
203
+ client = ctx->client;
204
+ // The LIVE options map (the root context's, which IS client->options):
205
+ // read for `apikey` and `auth`, never written.
206
+ liveopts_ = (nullptr != client && client->options.is_map()) ? client->options : ctx->options;
207
+
208
+ // WRAP FIRST. The fail-closed gate needs the seam whatever happens
209
+ // below - a chain that fails to build must still refuse to send - so
210
+ // the wrapper is installed before anything here can fail, and the
211
+ // exchange (when on) additionally needs to SEE responses: expiry is
212
+ // only ever discovered from one, and this is the one place a response
213
+ // can be seen and the request tried again.
214
+ Fetcher inner = ctx->utility->fetcher;
215
+ SecretsFeature* self = this;
216
+ ctx->utility->fetcher = [self, inner](CtxPtr ctx2, const std::string& url,
217
+ const Value& fetchdef) -> Value {
218
+ return self->transport(ctx2, url, fetchdef, inner);
219
+ };
220
+
221
+ secretname_ = fopt::foptStr(options, "name", "apikey");
222
+ cache_ = fopt::foptBool(options, "cache", true);
223
+
224
+ // Exchange config, normalised once. Null when off, so every later
225
+ // decision is a null check.
226
+ Value xopts = fopt::foptMap(options, "exchange");
227
+ if (fopt::foptBool(xopts, "active", false)) {
228
+ auto x = std::make_shared<Exchange>();
229
+ x->path = fopt::foptStr(xopts, "path", "auth/token");
230
+ x->method = fopt::foptStr(xopts, "method", "POST");
231
+ x->request = fopt::foptStr(xopts, "request", "refresh_token");
232
+ x->response = fopt::foptStr(xopts, "response", "access_token");
233
+ x->retries = fopt::foptInt(xopts, "retries", 1);
234
+ Value sl = fopt::foptList(xopts, "statuses");
235
+ if (sl.is_list()) {
236
+ for (const auto& s : *sl.as_list()) {
237
+ if (s.is_number()) x->statuses.push_back(static_cast<int>(s.as_int()));
238
+ }
239
+ }
240
+ if (x->statuses.empty()) x->statuses.push_back(401);
241
+ exchange_ = x;
242
+ }
243
+
244
+ // The explicit credential, when set, is the first store in the chain.
245
+ //
246
+ // WHICH option that is depends on the exchange. Without one, the secret
247
+ // being resolved IS the credential the transport sends, so `apikey` is
248
+ // it. With one, the secret is a REFRESH token and `apikey` means the
249
+ // opposite thing - an access token the caller already holds - so the
250
+ // explicit seat belongs to `exchange.refresh`, and apikey is left alone
251
+ // to serve as the starting access token (see resolve).
252
+ //
253
+ // Read with mapget, not foptStr: an explicitly EMPTY apikey means
254
+ // "defer to the chain", and foptStr would answer its default for it.
255
+ std::string explicitcred;
256
+ if (!exchange_) {
257
+ Value a = mapget(liveopts_, "apikey");
258
+ if (a.is_string()) explicitcred = a.as_string();
259
+ } else {
260
+ Value r = getp(xopts, "refresh");
261
+ if (r.is_string()) explicitcred = r.as_string();
262
+ }
263
+
264
+ std::vector<sekreto::ProviderSpec> specs;
265
+
266
+ if (!explicitcred.empty()) {
267
+ try {
268
+ std::string key = sekreto::envkey(secretname_, "");
269
+ sekreto::ProviderSpec seat;
270
+ seat.kind = "memory";
271
+ seat.name = "options";
272
+ seat.values.set(key, explicitcred);
273
+ specs.push_back(seat);
274
+ } catch (const std::exception& e) {
275
+ fail(e.what());
276
+ }
277
+ }
278
+
279
+ Value providers = fopt::foptList(options, "providers");
280
+ if (providers.is_list()) {
281
+ for (const Value& p : *providers.as_list()) {
282
+ if (p.is_injector()) {
283
+ // A provider already built (as built as cpp can spell it): joins
284
+ // the chain as it is.
285
+ specs.push_back(customSpec(p, ""));
286
+ } else if (p.is_map()) {
287
+ Value kind = getp(p, "kind");
288
+ if (kind.is_string() && "custom" == kind.as_string()) {
289
+ Value lookup = getp(p, "lookup");
290
+ if (!lookup.is_injector()) {
291
+ fail("sekreto: not a provider or a provider spec: " + vs::jsonify(p, 0) +
292
+ " (a `custom` provider needs a `lookup` callable)");
293
+ continue;
294
+ }
295
+ specs.push_back(customSpec(lookup, as_str(getp(p, "name"))));
296
+ } else {
297
+ try {
298
+ specs.push_back(sekreto::specof(toPluginValue(p)));
299
+ } catch (const std::exception& e) {
300
+ fail(e.what());
301
+ }
302
+ }
303
+ } else {
304
+ // FAIL CLOSED, never drop. An entry that is neither a callable
305
+ // nor a spec map (a bare "hashicorp" where a spec was meant, a
306
+ // number, a null) must not leave the chain quietly shorter than
307
+ // the options say: sekreto's own wording lands in the
308
+ // init-failure gate, which refuses to send. A loop that skipped
309
+ // the entry would SHORTEN the chain instead of failing it - a
310
+ // misconfigured providers list then sends ordinary
311
+ // unauthenticated requests, the exact fail-open the gate exists
312
+ // to prevent. (ts/kotlin push every entry through to Sekreto's
313
+ // constructor, whose dynamic list refuses it with this message;
314
+ // cpp's SekretoOptions::providers is a typed vector, so the
315
+ // feature raises the library's wording itself, as go and rust do.)
316
+ fail("sekreto: not a provider or a provider spec: " + vs::jsonify(p, 0));
317
+ }
318
+ }
319
+ }
320
+
321
+ if (!initerr_.empty()) return;
322
+
323
+ // The plugin DEFINITIONS the model selected for this feature, from the
324
+ // generated feature/secrets/kinds.cpp. Upstream sekreto's contract
325
+ // since the registry was retired: a kind not passed here is unknown to
326
+ // this Sekreto, so the model's choice of plugin groups IS the SDK's
327
+ // provider vocabulary. The `custom` bridge is added only when a custom
328
+ // entry needs it.
329
+ std::vector<sekreto::Definition> defs;
330
+ for (const auto& erased : secrets_plugins()) {
331
+ defs.push_back(std::static_pointer_cast<plugin::Definition>(erased));
332
+ }
333
+ if (!customs_.empty()) defs.push_back(customDefinition());
334
+
335
+ sekreto::SekretoOptions sopts;
336
+ sopts.providers = specs;
337
+ sopts.plugins = defs;
338
+ sopts.cache = cache_;
339
+
340
+ try {
341
+ sek_ = std::make_shared<sekreto::Sekreto>(sopts);
342
+ } catch (const std::exception& e) {
343
+ // Init cannot fail the construction the way ts's throwing init does;
344
+ // the transport gate refuses to send instead, which keeps a
345
+ // misconfigured chain fail-closed rather than silently
346
+ // unauthenticated.
347
+ fail(e.what());
348
+ }
349
+ }
350
+
351
+ // ---- accessors ------------------------------------------------------------
352
+
353
+ // The LIVE chain (the cpp spelling of ts's `sekreto()` accessor - named
354
+ // `chain` because a member called `sekreto` would shadow the namespace),
355
+ // or null when the feature is inactive or its construction failed. Never
356
+ // a clone: sekreto holds provider state that has to stay live to be worth
357
+ // anything. Not thread-safe to USE concurrently with requests; see the
358
+ // header note.
359
+ std::shared_ptr<sekreto::Sekreto> chain() const {
360
+ std::lock_guard<std::recursive_mutex> lk(mu_);
361
+ return sek_;
362
+ }
363
+
364
+ // The resolved credential ("" when none) - the value the transport
365
+ // wrapper injects into each request. Read here rather than from the
366
+ // options map, which this feature never mutates.
367
+ std::string credential() const {
368
+ std::lock_guard<std::recursive_mutex> lk(mu_);
369
+ return cred_;
370
+ }
371
+
372
+ // sekreto's own message when the chain could not be built (an unknown
373
+ // kind, a malformed providers entry, a provider refusing its
374
+ // configuration), or "". While it is set the transport wrapper refuses
375
+ // to send.
376
+ std::string initError() const {
377
+ std::lock_guard<std::recursive_mutex> lk(mu_);
378
+ return initerr_;
379
+ }
380
+
381
+ Track track() const {
382
+ std::lock_guard<std::recursive_mutex> lk(mu_);
383
+ return track_;
384
+ }
385
+
386
+ // The client's secrets feature, or null when the client has none. Walks
387
+ // the client's feature list by name, so it finds the instance the
388
+ // generated config installed from the options - never a second one.
389
+ static std::shared_ptr<SecretsFeature> of(SdkClient* client) {
390
+ if (nullptr == client) return nullptr;
391
+ for (const auto& f : client->features) {
392
+ if (f && "secrets" == f->getName()) {
393
+ return std::dynamic_pointer_cast<SecretsFeature>(f);
394
+ }
395
+ }
396
+ return nullptr;
397
+ }
398
+
399
+ private:
400
+ Value liveopts_ = Value::undef();
401
+ std::string secretname_ = "apikey";
402
+ bool cache_ = true;
403
+
404
+ std::shared_ptr<sekreto::Sekreto> sek_;
405
+ std::string initerr_;
406
+
407
+ // The RESOLVED credential (the access token when exchanging), held here
408
+ // and injected at the seam - never written into the options map. A
409
+ // cached resolution stands (cache: true only); a FAILURE never sets it,
410
+ // so the next request asks the chain again.
411
+ std::string cred_;
412
+ bool resolved_ = false;
413
+
414
+ std::shared_ptr<Exchange> exchange_;
415
+ std::string refresh_;
416
+
417
+ // The custom-provider slot table: the Injector each `custom` spec's slot
418
+ // ticket names.
419
+ std::vector<Value> customs_;
420
+
421
+ Track track_;
422
+ mutable std::recursive_mutex mu_;
423
+
424
+ // The FIRST failure stands: it names the entry the caller has to fix.
425
+ void fail(const std::string& msg) {
426
+ if (initerr_.empty()) initerr_ = msg;
427
+ }
428
+
429
+ // ---- options map -> sekreto ------------------------------------------------
430
+
431
+ // An SDK Value as the plugin value model sekreto::specof reads. `values`
432
+ // (memory's literal map) is coerced to strings, as every port does.
433
+ static plugin::V toPluginValue(const Value& v, bool stringleaves = false) {
434
+ if (v.is_string()) return plugin::vstr(v.as_string());
435
+ if (v.is_list()) {
436
+ plugin::V l = plugin::vlist();
437
+ for (const auto& e : *v.as_list()) plugin::push(l, toPluginValue(e, stringleaves));
438
+ return l;
439
+ }
440
+ if (v.is_map()) {
441
+ plugin::V m = plugin::vmap();
442
+ for (const auto& kv : *v.as_map()) {
443
+ // Containers recurse; only the LEAVES under `values` are coerced.
444
+ plugin::set(m, kv.first,
445
+ toPluginValue(kv.second, stringleaves || "values" == kv.first));
446
+ }
447
+ return m;
448
+ }
449
+ if (stringleaves) return plugin::vstr(Struct::stringify(v));
450
+ if (v.is_bool()) return plugin::vbool(v.as_bool());
451
+ if (v.is_number()) return plugin::vnum(v.as_double());
452
+ return plugin::vnull();
453
+ }
454
+
455
+ sekreto::ProviderSpec customSpec(const Value& fn, const std::string& name) {
456
+ sekreto::ProviderSpec spec;
457
+ spec.kind = "custom";
458
+ spec.name = name;
459
+ spec.values.set("slot", std::to_string(customs_.size()));
460
+ customs_.push_back(fn);
461
+ return spec;
462
+ }
463
+
464
+ sekreto::Definition customDefinition() {
465
+ SecretsFeature* self = this;
466
+ return sekreto::providerplugin("custom",
467
+ [self](const sekreto::ProviderSpec& spec) -> std::shared_ptr<sekreto::Provider> {
468
+ std::optional<std::string> slot = spec.values.get("slot");
469
+ size_t index = slot.has_value() ? static_cast<size_t>(std::atol(slot->c_str()))
470
+ : self->customs_.size();
471
+ if (index >= self->customs_.size()) {
472
+ throw sekreto::SekretoError("sekreto: custom provider has no lookup callable");
473
+ }
474
+ return std::make_shared<SecretsCustomProvider>(self->customs_[index]);
475
+ });
476
+ }
477
+
478
+ // ---- credential injection ---------------------------------------------------
479
+
480
+ // Write the resolved credential into THIS request's authorization header,
481
+ // the way prepareAuth builds it (same options auth.prefix, same
482
+ // suppression rule), so the two cannot drift.
483
+ //
484
+ // `auth: null` is the documented way to send NO credential, and
485
+ // prepareAuth honours it by removing the header; so does this, and it
486
+ // never writes one - which is what keeps the exchange from transmitting
487
+ // exactly the credential the caller suppressed on a retry. Read with
488
+ // mapget + is_nullish exactly as prepareAuth reads it. An EMPTY
489
+ // credential leaves prepareAuth's header alone: nothing was resolved, so
490
+ // there is nothing to inject and nothing to retract that prepareAuth did
491
+ // not already decide.
492
+ void reauth(const Value& fetchdef) {
493
+ Value headers = getp(fetchdef, "headers");
494
+ if (!headers.is_map()) return;
495
+
496
+ if (authSuppressed()) {
497
+ map_remove(headers, "authorization");
498
+ return;
499
+ }
500
+
501
+ if (cred_.empty()) return;
502
+
503
+ std::string prefix = as_str(Struct::getpath(liveopts_, {"auth", "prefix"}));
504
+ map_put(headers, "authorization",
505
+ Value(prefix.empty() ? cred_ : prefix + " " + cred_));
506
+ }
507
+
508
+ bool authSuppressed() const {
509
+ return is_nullish(mapget(liveopts_, "auth"));
510
+ }
511
+
512
+ // ---- the exchange -----------------------------------------------------------
513
+
514
+ // Buy an access token with the refresh token. Answers sekreto-style: a
515
+ // message, or "" for success with the token in cred_. Called under mu_.
516
+ std::string buy() {
517
+ const Exchange& x = *exchange_;
518
+
519
+ // TEST MODE BUYS NOTHING. The test feature replaces the transport so no
520
+ // request leaves the process; an exchange here would be the one HTTP
521
+ // call it could not stop, and it would need a live token endpoint for a
522
+ // suite whose whole point is not needing one. A deterministic,
523
+ // obviously-fake token instead - the same answer makeOptions gives a
524
+ // required server variable, for the same reason.
525
+ if (nullptr == client || "live" != client->mode) {
526
+ cred_ = "test-" + x.response;
527
+ track_.buys++;
528
+ return "";
529
+ }
530
+
531
+ if (refresh_.empty()) {
532
+ return "secrets: no refresh token: the provider chain has no '" + secretname_ +
533
+ "', and feature.secrets.exchange.refresh is unset";
534
+ }
535
+
536
+ Value opts = client->optionsMap();
537
+
538
+ // The token endpoint is RELATIVE to the base, which already carries
539
+ // whatever account or tenant segment the server URL declares.
540
+ std::string base = as_str(getp(opts, "base"));
541
+ while (!base.empty() && '/' == base.back()) base.pop_back();
542
+ std::string path = x.path;
543
+ while (!path.empty() && '/' == path.front()) path.erase(0, 1);
544
+ std::string url = base + "/" + path;
545
+
546
+ // The body is MARSHALLED, never concatenated: a refresh token (or a
547
+ // configured request-field name) carrying a quote, backslash or newline
548
+ // must arrive as that literal value, not as malformed JSON.
549
+ std::string bodytext = vs::jsonify(vmap({{x.request, Value(refresh_)}}), 0);
550
+
551
+ Value fetchdef = vmap({
552
+ {"method", Value(x.method)},
553
+ {"headers", vmap({{"content-type", Value(std::string("application/json"))}})},
554
+ {"body", Value(bodytext)}});
555
+
556
+ // Deliberately NOT the SDK transport. The transport is what this feature
557
+ // wraps, and sending the token request back through it would recurse on
558
+ // the first expiry - and would route the exchange through the test mock,
559
+ // which knows nothing about it. options.system.fetch when the caller
560
+ // supplied one (the seam every live cpp request already uses), else the
561
+ // bundled transport of last resort from the generated kinds.cpp.
562
+ Value sysFetch = Struct::getpath(opts, {"system", "fetch"});
563
+ Value res;
564
+ if (sysFetch.is_injector()) {
565
+ try {
566
+ vs::Injection inj(Value::undef(), Value::undef());
567
+ res = sysFetch.as_injector()(inj, vlist({Value(url), fetchdef}), std::string(""),
568
+ Value::undef());
569
+ } catch (const SdkErrorPtr& e) {
570
+ return e->msg;
571
+ } catch (const std::exception& e) {
572
+ return std::string("secrets: token exchange transport failed: ") + e.what();
573
+ }
574
+ } else {
575
+ std::vector<std::pair<std::string, std::string>> hdrs{{"content-type", "application/json"}};
576
+ SecretsRawResponse raw = secrets_rawfetch(x.method, url, hdrs, bodytext);
577
+ if (!raw.ok) return raw.err;
578
+ res = vmap({{"status", Value(raw.status)}, {"body", Value(raw.body)}});
579
+ }
580
+
581
+ int status = fopt::fresStatus(res);
582
+ if (200 > status || 300 <= status) {
583
+ return "secrets: token exchange failed: " + std::to_string(status) + " from " + url;
584
+ }
585
+
586
+ Value parsed = Value::undef();
587
+ Value jf = mapget(res, "json");
588
+ if (jf.is_injector()) {
589
+ vs::Injection inj(Value::undef(), Value::undef());
590
+ parsed = jf.as_injector()(inj, Value::undef(), std::string(""), Value::undef());
591
+ } else {
592
+ Value raw = getp(res, "body");
593
+ parsed = raw.is_string() ? vs::parse_json(raw.as_string()) : raw;
594
+ }
595
+
596
+ Value token = getp(parsed, x.response);
597
+ if (!token.is_string() || token.as_string().empty()) {
598
+ return "secrets: token exchange returned no '" + x.response + "' field from " + url;
599
+ }
600
+
601
+ cred_ = token.as_string();
602
+ track_.buys++;
603
+ return "";
604
+ }
605
+
606
+ bool spent(const Value& res) const {
607
+ int status = fopt::fresStatus(res);
608
+ if (0 > status) return false;
609
+ return std::find(exchange_->statuses.begin(), exchange_->statuses.end(), status) !=
610
+ exchange_->statuses.end();
611
+ }
612
+
613
+ // ---- resolution -------------------------------------------------------------
614
+
615
+ // One resolution. A settled SUCCESS is kept only when caching is on
616
+ // (`cache: false` means every request asks the chain again, and sekreto's
617
+ // own cache is off with it); a FAILURE is never kept, so a transient
618
+ // vault outage never poisons the client - the next request asks again.
619
+ // Answers the provider's own message, or "". Called under mu_.
620
+ std::string resolve() {
621
+ if (cache_ && resolved_) return "";
622
+ if (!sek_) return "";
623
+
624
+ std::optional<std::string> found;
625
+ try {
626
+ found = sek_->tryget(secretname_);
627
+ } catch (const std::exception& e) {
628
+ // A provider ERROR fails the op (via the transport gate); only a MISS
629
+ // falls through.
630
+ return e.what();
631
+ }
632
+ track_.resolves++;
633
+
634
+ if (!exchange_) {
635
+ // A hit is the credential. An UNCACHED miss after an earlier hit is a
636
+ // revocation: the chain now says no provider has the secret, so the
637
+ // resolved value must not keep going out on the wire. (An explicit
638
+ // apikey OPTION is never lost here - it seats FIRST in the chain as a
639
+ // memory provider, so the chain HITS while one is set.)
640
+ cred_ = found.has_value() ? found.value() : "";
641
+ resolved_ = true;
642
+ return "";
643
+ }
644
+
645
+ // Exchanging: what the chain resolved is the REFRESH token, kept for
646
+ // every later purchase. A miss is not fatal here - an explicit `apikey`
647
+ // may already hold a usable access token, and the API is what gets to
648
+ // say whether it does.
649
+ refresh_ = found.has_value() ? found.value() : "";
650
+
651
+ if (cred_.empty()) {
652
+ // A starting access token supplied as the OPTION: read from the frozen
653
+ // options map (no feature ever writes it).
654
+ Value held = mapget(liveopts_, "apikey");
655
+ if (held.is_string()) cred_ = held.as_string();
656
+ }
657
+ if (!cred_.empty()) {
658
+ // Spend it: if it is stale the API answers with an expiry status and
659
+ // the wrapper buys another, which is the same path expiry takes anyway.
660
+ resolved_ = true;
661
+ return "";
662
+ }
663
+
664
+ std::string berr = buy();
665
+ if (!berr.empty()) return berr;
666
+ resolved_ = true;
667
+ return "";
668
+ }
669
+
670
+ // ---- the transport wrapper --------------------------------------------------
671
+
672
+ Value transport(CtxPtr ctx, const std::string& url, const Value& fetchdef,
673
+ const Fetcher& inner) {
674
+ bool suppressed = false;
675
+ {
676
+ std::lock_guard<std::recursive_mutex> lk(mu_);
677
+
678
+ // FAIL-CLOSED, at the ONE seam every wire path crosses. Entity ops,
679
+ // direct, graphql and the exchange retries all come through here, so
680
+ // resolving HERE is what gives the raw paths - which run no feature
681
+ // hooks at all - the same credential the entity pipeline gets. A
682
+ // construction failure (initerr_) or a provider ERROR refuses the
683
+ // request with sekreto's own message - never an unauthenticated send.
684
+ if (!initerr_.empty()) {
685
+ track_.refused++;
686
+ throw ctx->makeError("secrets_init", initerr_);
687
+ }
688
+ std::string rerr = resolve();
689
+ if (!rerr.empty()) {
690
+ track_.refused++;
691
+ throw ctx->makeError("secrets_resolve", rerr);
692
+ }
693
+
694
+ // Inject the resolved credential into THIS request's header (the
695
+ // header was built by prepareAuth from the options apikey; the
696
+ // chain-resolved value lives in feature state instead).
697
+ reauth(fetchdef);
698
+ suppressed = authSuppressed();
699
+ }
700
+
701
+ if (!exchange_) return inner(ctx, url, fetchdef);
702
+
703
+ // `auth: null` is a deliberately unauthenticated request: a refusal of
704
+ // it is not an expired token and cannot be fixed by buying one -
705
+ // retrying would transmit exactly the credential the caller suppressed.
706
+ if (suppressed) return inner(ctx, url, fetchdef);
707
+
708
+ int max = exchange_->retries;
709
+ int attempt = 0;
710
+ for (;;) {
711
+ // The credential THIS attempt goes out with, captured before it
712
+ // leaves: it is what tells a stale refusal apart from a fresh one.
713
+ std::string used = credential();
714
+
715
+ Value out = inner(ctx, url, fetchdef);
716
+
717
+ if (attempt >= max || !spent(out)) return out;
718
+
719
+ {
720
+ std::lock_guard<std::recursive_mutex> lk(mu_);
721
+ // Another request may have bought a token while this one was in
722
+ // flight (the mutex made it wait for that purchase to finish).
723
+ // Spend what is current before buying: a second exchange for a
724
+ // token that is already fresh is wasted, and on a provider that
725
+ // invalidates the previous credential on issuance it breaks this
726
+ // request's own retry.
727
+ if (cred_.empty() || cred_ == used) {
728
+ std::string berr = buy();
729
+ if (!berr.empty()) {
730
+ // The purchase failed: answer with the API's own refusal rather
731
+ // than this one. The caller asked for data, and the refusal is
732
+ // the more useful of the two - the exchange error is a symptom.
733
+ return out;
734
+ }
735
+ }
736
+ reauth(fetchdef);
737
+ }
738
+ attempt++;
739
+ }
740
+ }
741
+ };
742
+
743
+ } // namespace sdk
744
+
745
+ #endif // SDK_FEATURE_SECRETS_HPP