graph-agents-cli 0.3.1__py3-none-any.whl

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 (291) hide show
  1. graph_agents_cli/__init__.py +26 -0
  2. graph_agents_cli/_api_policy.py +2145 -0
  3. graph_agents_cli/_approvals.py +400 -0
  4. graph_agents_cli/_build.py +186 -0
  5. graph_agents_cli/_build_info.json +7 -0
  6. graph_agents_cli/_chat_client.py +462 -0
  7. graph_agents_cli/_click.py +157 -0
  8. graph_agents_cli/_defaults.py +139 -0
  9. graph_agents_cli/_experiments.py +64 -0
  10. graph_agents_cli/_http.py +192 -0
  11. graph_agents_cli/_output.py +83 -0
  12. graph_agents_cli/_project.py +462 -0
  13. graph_agents_cli/_remote.py +220 -0
  14. graph_agents_cli/_response_schema.py +264 -0
  15. graph_agents_cli/_runner.py +319 -0
  16. graph_agents_cli/_skills_check.py +274 -0
  17. graph_agents_cli/_tools.py +189 -0
  18. graph_agents_cli/_trust.py +66 -0
  19. graph_agents_cli/api/__init__.py +15 -0
  20. graph_agents_cli/api/_changes.py +506 -0
  21. graph_agents_cli/api/_files.py +658 -0
  22. graph_agents_cli/api/cmd_api.py +2480 -0
  23. graph_agents_cli/deploy/__init__.py +15 -0
  24. graph_agents_cli/deploy/_config.py +171 -0
  25. graph_agents_cli/deploy/_image.py +128 -0
  26. graph_agents_cli/deploy/_kube.py +286 -0
  27. graph_agents_cli/deploy/_modes.py +234 -0
  28. graph_agents_cli/deploy/_preflight.py +370 -0
  29. graph_agents_cli/deploy/_values.py +168 -0
  30. graph_agents_cli/deploy/cmd_deploy.py +1866 -0
  31. graph_agents_cli/deploy/gitops.py +562 -0
  32. graph_agents_cli/deploy/local_load.py +273 -0
  33. graph_agents_cli/dev/__init__.py +13 -0
  34. graph_agents_cli/dev/cmd_build.py +131 -0
  35. graph_agents_cli/dev/cmd_install.py +78 -0
  36. graph_agents_cli/dev/cmd_lint.py +119 -0
  37. graph_agents_cli/dev/cmd_playground.py +297 -0
  38. graph_agents_cli/dev/policy_check.py +1287 -0
  39. graph_agents_cli/eval/__init__.py +22 -0
  40. graph_agents_cli/eval/_client.py +670 -0
  41. graph_agents_cli/eval/_common.py +177 -0
  42. graph_agents_cli/eval/_judge.py +168 -0
  43. graph_agents_cli/eval/_judge_runner.py +238 -0
  44. graph_agents_cli/eval/_paths.py +212 -0
  45. graph_agents_cli/eval/checks.py +581 -0
  46. graph_agents_cli/eval/cmd_analyze.py +278 -0
  47. graph_agents_cli/eval/cmd_compare.py +284 -0
  48. graph_agents_cli/eval/cmd_eval_group.py +80 -0
  49. graph_agents_cli/eval/cmd_generate.py +558 -0
  50. graph_agents_cli/eval/cmd_grade.py +466 -0
  51. graph_agents_cli/eval/cmd_metric.py +156 -0
  52. graph_agents_cli/eval/cmd_run.py +370 -0
  53. graph_agents_cli/eval/cmd_submit.py +400 -0
  54. graph_agents_cli/eval/config.py +435 -0
  55. graph_agents_cli/eval/dataset.py +350 -0
  56. graph_agents_cli/eval/gate.py +420 -0
  57. graph_agents_cli/eval/transcript.py +192 -0
  58. graph_agents_cli/extension/__init__.py +13 -0
  59. graph_agents_cli/extension/_compat.py +86 -0
  60. graph_agents_cli/extension/_loader.py +293 -0
  61. graph_agents_cli/extension/_manifest.py +135 -0
  62. graph_agents_cli/extension/_overrides.py +195 -0
  63. graph_agents_cli/extension/_paths.py +91 -0
  64. graph_agents_cli/extension/_refs.py +193 -0
  65. graph_agents_cli/extension/_resolver.py +453 -0
  66. graph_agents_cli/extension/_schema.py +106 -0
  67. graph_agents_cli/extension/_spec.py +253 -0
  68. graph_agents_cli/extension/_sync.py +102 -0
  69. graph_agents_cli/extension/_trust.py +58 -0
  70. graph_agents_cli/extension/cmd_extension_add.py +259 -0
  71. graph_agents_cli/extension/cmd_extension_group.py +57 -0
  72. graph_agents_cli/extension/cmd_extension_list.py +56 -0
  73. graph_agents_cli/extension/cmd_extension_remove.py +61 -0
  74. graph_agents_cli/extension/cmd_extension_update.py +195 -0
  75. graph_agents_cli/info/__init__.py +13 -0
  76. graph_agents_cli/info/cmd_info.py +222 -0
  77. graph_agents_cli/infra/__init__.py +15 -0
  78. graph_agents_cli/infra/checks.py +1169 -0
  79. graph_agents_cli/infra/cmd_infra.py +103 -0
  80. graph_agents_cli/main.py +591 -0
  81. graph_agents_cli/peer/__init__.py +15 -0
  82. graph_agents_cli/peer/_generate.py +254 -0
  83. graph_agents_cli/peer/cmd_peer.py +1151 -0
  84. graph_agents_cli/run/__init__.py +13 -0
  85. graph_agents_cli/run/_local_server.py +1157 -0
  86. graph_agents_cli/run/_signals.py +141 -0
  87. graph_agents_cli/run/cmd_approvals.py +530 -0
  88. graph_agents_cli/run/cmd_run.py +1421 -0
  89. graph_agents_cli/scaffold/__init__.py +19 -0
  90. graph_agents_cli/scaffold/agents/README.md +24 -0
  91. graph_agents_cli/scaffold/agents/empty_py/.template/templateconfig.yaml +22 -0
  92. graph_agents_cli/scaffold/agents/langgraph/.env.example +292 -0
  93. graph_agents_cli/scaffold/agents/langgraph/.template/templateconfig.yaml +28 -0
  94. graph_agents_cli/scaffold/agents/langgraph/Dockerfile +59 -0
  95. graph_agents_cli/scaffold/agents/langgraph/Dockerfile.langgraph-server +59 -0
  96. graph_agents_cli/scaffold/agents/langgraph/README.md +571 -0
  97. graph_agents_cli/scaffold/agents/langgraph/api-policy.yaml +60 -0
  98. graph_agents_cli/scaffold/agents/langgraph/app/__init__.py +20 -0
  99. graph_agents_cli/scaffold/agents/langgraph/app/agent.py +174 -0
  100. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/__init__.py +15 -0
  101. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/a2a.py +2162 -0
  102. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/a2a_client.py +1167 -0
  103. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/api_client.py +4220 -0
  104. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/approvals.py +1349 -0
  105. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/auth.py +1986 -0
  106. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/chat.py +2962 -0
  107. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/checkpointer.py +432 -0
  108. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/content.py +569 -0
  109. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/db.py +580 -0
  110. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/limits.py +203 -0
  111. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/metrics.py +231 -0
  112. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/middleware.py +361 -0
  113. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/model.py +611 -0
  114. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/playground.py +230 -0
  115. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/run_locks.py +459 -0
  116. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/structured.py +755 -0
  117. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/telemetry.py +681 -0
  118. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/threads.py +493 -0
  119. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/token_exchange.py +959 -0
  120. graph_agents_cli/scaffold/agents/langgraph/app/fast_api_app.py +770 -0
  121. graph_agents_cli/scaffold/agents/langgraph/app/policies/__init__.py +55 -0
  122. graph_agents_cli/scaffold/agents/langgraph/app/policies/custom.py +97 -0
  123. graph_agents_cli/scaffold/agents/langgraph/app/tools/__init__.py +46 -0
  124. graph_agents_cli/scaffold/agents/langgraph/app/tools/example_api.py +92 -0
  125. graph_agents_cli/scaffold/agents/langgraph/app/tools/weather.py +33 -0
  126. graph_agents_cli/scaffold/agents/langgraph/langgraph.json +14 -0
  127. graph_agents_cli/scaffold/agents/langgraph/pyproject.toml +78 -0
  128. graph_agents_cli/scaffold/agents/langgraph/tests/conftest.py +376 -0
  129. graph_agents_cli/scaffold/agents/langgraph/tests/eval/datasets/basic-dataset.json +53 -0
  130. graph_agents_cli/scaffold/agents/langgraph/tests/eval/eval_config.yaml +32 -0
  131. graph_agents_cli/scaffold/agents/langgraph/tests/integration/approval_graph.py +137 -0
  132. graph_agents_cli/scaffold/agents/langgraph/tests/integration/fake_issuer.py +216 -0
  133. graph_agents_cli/scaffold/agents/langgraph/tests/integration/fake_openai.py +357 -0
  134. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_a2a_outcomes.py +569 -0
  135. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_a2a_relay.py +479 -0
  136. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_api_surface.py +812 -0
  137. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_approvals.py +1367 -0
  138. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_approvals_server.py +794 -0
  139. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_cross_actor.py +497 -0
  140. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_cross_actor_server.py +247 -0
  141. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_history_repair.py +278 -0
  142. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_model_apis.py +242 -0
  143. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_postgres.py +637 -0
  144. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_resilience_postgres.py +770 -0
  145. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_runtime_guardrails.py +854 -0
  146. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_server_e2e.py +340 -0
  147. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_server_runtime.py +989 -0
  148. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_structured_answers.py +584 -0
  149. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_structured_server.py +222 -0
  150. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_token_exchange_issuer.py +650 -0
  151. graph_agents_cli/scaffold/agents/langgraph/tests/load_test/.results/.placeholder +0 -0
  152. graph_agents_cli/scaffold/agents/langgraph/tests/load_test/README.md +22 -0
  153. graph_agents_cli/scaffold/agents/langgraph/tests/load_test/conftest.py +21 -0
  154. graph_agents_cli/scaffold/agents/langgraph/tests/load_test/load_test.py +81 -0
  155. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_a2a_client.py +824 -0
  156. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_a2a_scoping.py +724 -0
  157. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_api_client.py +1214 -0
  158. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_api_client_hardening.py +716 -0
  159. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_api_policy_rpc.py +767 -0
  160. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_approval_ledger.py +1536 -0
  161. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_fake_model.py +115 -0
  162. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_jwt_policy.py +991 -0
  163. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_limits.py +310 -0
  164. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_logging.py +148 -0
  165. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_logging_hardening.py +271 -0
  166. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_policy.py +378 -0
  167. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_resilience.py +610 -0
  168. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_server_auth.py +702 -0
  169. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_structured.py +673 -0
  170. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_telemetry.py +404 -0
  171. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_thread_listing.py +255 -0
  172. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_threads.py +268 -0
  173. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_token_exchange.py +1320 -0
  174. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_untrusted_content.py +393 -0
  175. graph_agents_cli/scaffold/agents/langgraph/uv-fastapi.lock +2084 -0
  176. graph_agents_cli/scaffold/agents/langgraph/uv-langgraph-server.lock +2106 -0
  177. graph_agents_cli/scaffold/agents/langgraph/{{cookiecutter.agent_guidance_filename}} +129 -0
  178. graph_agents_cli/scaffold/base_templates/_shared/graph-agents-cli-manifest.yaml +36 -0
  179. graph_agents_cli/scaffold/base_templates/python/.dockerignore +32 -0
  180. graph_agents_cli/scaffold/base_templates/python/.github/CODEOWNERS +30 -0
  181. graph_agents_cli/scaffold/base_templates/python/.github/agent.env +7 -0
  182. graph_agents_cli/scaffold/base_templates/python/.github/workflows/pr_checks.yaml +214 -0
  183. graph_agents_cli/scaffold/base_templates/python/.gitignore +209 -0
  184. graph_agents_cli/scaffold/base_templates/python/tests/unit/test_dummy.py +23 -0
  185. graph_agents_cli/scaffold/base_templates/python/{{cookiecutter.agent_guidance_filename}} +35 -0
  186. graph_agents_cli/scaffold/cmd_scaffold_group.py +49 -0
  187. graph_agents_cli/scaffold/commands/__init__.py +13 -0
  188. graph_agents_cli/scaffold/commands/create.py +1424 -0
  189. graph_agents_cli/scaffold/commands/enhance.py +1652 -0
  190. graph_agents_cli/scaffold/commands/upgrade.py +570 -0
  191. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/.github/agent.env +12 -0
  192. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/.github/workflows/promote-to-prod.yaml +371 -0
  193. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/.github/workflows/staging.yaml +450 -0
  194. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/argocd/application-dev.yaml +43 -0
  195. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/argocd/application-prod.yaml +41 -0
  196. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/argocd/application-staging.yaml +43 -0
  197. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/.helmignore +14 -0
  198. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/Chart.yaml +21 -0
  199. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/examples/networkpolicy.yaml +103 -0
  200. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/NOTES.txt +48 -0
  201. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/_helpers.tpl +189 -0
  202. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/certificate.yaml +15 -0
  203. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/configmap.yaml +10 -0
  204. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/deployment.yaml +199 -0
  205. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/hpa.yaml +22 -0
  206. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/httproute.yaml +30 -0
  207. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/ingress.yaml +39 -0
  208. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/networkpolicy.yaml +48 -0
  209. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/pdb.yaml +13 -0
  210. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/postgresql-secret.yaml +37 -0
  211. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/service.yaml +15 -0
  212. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/serviceaccount.yaml +13 -0
  213. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/servicemonitor.yaml +42 -0
  214. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values-dev.yaml +22 -0
  215. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values-prod.yaml +45 -0
  216. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values-staging.yaml +29 -0
  217. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values.yaml +396 -0
  218. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/tests/integration/test_chart.py +269 -0
  219. graph_agents_cli/scaffold/deployment_targets/none/README.md +5 -0
  220. graph_agents_cli/scaffold/deployment_targets/none/python/README.md +6 -0
  221. graph_agents_cli/scaffold/utils/__init__.py +13 -0
  222. graph_agents_cli/scaffold/utils/backup.py +212 -0
  223. graph_agents_cli/scaffold/utils/build_record.py +257 -0
  224. graph_agents_cli/scaffold/utils/cli_options.py +184 -0
  225. graph_agents_cli/scaffold/utils/fs.py +83 -0
  226. graph_agents_cli/scaffold/utils/generate_locks.py +214 -0
  227. graph_agents_cli/scaffold/utils/generation_metadata.py +88 -0
  228. graph_agents_cli/scaffold/utils/keyedit.py +768 -0
  229. graph_agents_cli/scaffold/utils/keymerge.py +537 -0
  230. graph_agents_cli/scaffold/utils/language.py +138 -0
  231. graph_agents_cli/scaffold/utils/lock_utils.py +94 -0
  232. graph_agents_cli/scaffold/utils/logging.py +77 -0
  233. graph_agents_cli/scaffold/utils/manifest.py +292 -0
  234. graph_agents_cli/scaffold/utils/merge.py +970 -0
  235. graph_agents_cli/scaffold/utils/merge3.py +216 -0
  236. graph_agents_cli/scaffold/utils/openapi_seed.py +199 -0
  237. graph_agents_cli/scaffold/utils/remote_template.py +376 -0
  238. graph_agents_cli/scaffold/utils/template.py +1352 -0
  239. graph_agents_cli/scaffold/utils/upgrade.py +894 -0
  240. graph_agents_cli/scaffold/utils/version.py +438 -0
  241. graph_agents_cli/secrets/__init__.py +15 -0
  242. graph_agents_cli/secrets/_apply.py +954 -0
  243. graph_agents_cli/secrets/_required.py +188 -0
  244. graph_agents_cli/secrets/cmd_secrets.py +211 -0
  245. graph_agents_cli/setup/__init__.py +13 -0
  246. graph_agents_cli/setup/_antigravity.py +221 -0
  247. graph_agents_cli/setup/cmd_auth.py +1030 -0
  248. graph_agents_cli/setup/cmd_dev_token.py +513 -0
  249. graph_agents_cli/setup/cmd_setup.py +428 -0
  250. graph_agents_cli/setup/cmd_update.py +140 -0
  251. graph_agents_cli/skills/__init__.py +13 -0
  252. graph_agents_cli/skills/_bundle.py +65 -0
  253. graph_agents_cli/skills/data/README.md +19 -0
  254. graph_agents_cli/skills/data/graph-agents-cli-deploy/SKILL.md +357 -0
  255. graph_agents_cli/skills/data/graph-agents-cli-deploy/references/github-settings.md +113 -0
  256. graph_agents_cli/skills/data/graph-agents-cli-deploy/references/gitops.md +137 -0
  257. graph_agents_cli/skills/data/graph-agents-cli-deploy/references/kubernetes.md +315 -0
  258. graph_agents_cli/skills/data/graph-agents-cli-deploy/references/secrets.md +160 -0
  259. graph_agents_cli/skills/data/graph-agents-cli-eval/SKILL.md +303 -0
  260. graph_agents_cli/skills/data/graph-agents-cli-eval/references/dataset_schema.md +282 -0
  261. graph_agents_cli/skills/data/graph-agents-cli-eval/references/metrics-guide.md +143 -0
  262. graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/SKILL.md +659 -0
  263. graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/references/langchain-models.md +124 -0
  264. graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/references/langgraph.md +235 -0
  265. graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/references/template-contract.md +477 -0
  266. graph_agents_cli/skills/data/graph-agents-cli-observability/SKILL.md +231 -0
  267. graph_agents_cli/skills/data/graph-agents-cli-observability/references/langsmith.md +46 -0
  268. graph_agents_cli/skills/data/graph-agents-cli-observability/references/otel.md +59 -0
  269. graph_agents_cli/skills/data/graph-agents-cli-scaffold/SKILL.md +414 -0
  270. graph_agents_cli/skills/data/graph-agents-cli-scaffold/references/flags.md +134 -0
  271. graph_agents_cli/skills/data/graph-agents-cli-workflow/SKILL.md +478 -0
  272. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/brainstorming.md +118 -0
  273. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/commands.md +419 -0
  274. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/extension.md +156 -0
  275. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/internals.md +67 -0
  276. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/spec-template.md +56 -0
  277. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/terminology.md +119 -0
  278. graph_agents_cli/system/__init__.py +15 -0
  279. graph_agents_cli/system/_apply.py +519 -0
  280. graph_agents_cli/system/_checks.py +1023 -0
  281. graph_agents_cli/system/_deploy.py +215 -0
  282. graph_agents_cli/system/_model.py +363 -0
  283. graph_agents_cli/system/_system.py +664 -0
  284. graph_agents_cli/system/_views.py +208 -0
  285. graph_agents_cli/system/cmd_system.py +423 -0
  286. graph_agents_cli-0.3.1.dist-info/METADATA +162 -0
  287. graph_agents_cli-0.3.1.dist-info/RECORD +291 -0
  288. graph_agents_cli-0.3.1.dist-info/WHEEL +4 -0
  289. graph_agents_cli-0.3.1.dist-info/entry_points.txt +2 -0
  290. graph_agents_cli-0.3.1.dist-info/licenses/LICENSE +201 -0
  291. graph_agents_cli-0.3.1.dist-info/licenses/NOTICE +19 -0
@@ -0,0 +1,315 @@
1
+ # Kubernetes and the Helm chart
2
+
3
+ `deployment/helm/<name>/`: `Chart.yaml`, `values.yaml`, `values-dev.yaml`, `values-staging.yaml`,
4
+ `values-prod.yaml`, `templates/` (`deployment.yaml`, `service.yaml`, `configmap.yaml`,
5
+ `httproute.yaml`, `ingress.yaml`, `certificate.yaml`, `hpa.yaml`, `pdb.yaml`,
6
+ `networkpolicy.yaml`, `servicemonitor.yaml`, `postgresql-secret.yaml`, `serviceaccount.yaml`,
7
+ `NOTES.txt`, `_helpers.tpl`), `examples/networkpolicy.yaml` (a worked staging/prod NetworkPolicy,
8
+ not packaged), `charts/` (the Postgres and Redis subcharts once fetched). There is
9
+ **no template for the app Secret**. Release name = project name; namespace =
10
+ `environments.<env>.namespace`.
11
+
12
+ `Chart.yaml` declares the Bitnami `postgresql` (`18.11.6`) and `redis` (`28.2.3`) charts from
13
+ `oci://registry-1.docker.io/bitnamicharts` as conditional dependencies (`postgresql.enabled`,
14
+ `redis.enabled`), pinned to exact versions; `values.yaml` pins their images by digest.
15
+ `helm dependency build deployment/helm/<name>` must run before `helm upgrade --install` or
16
+ `helm template`; `deploy` runs it when a subchart is missing from `charts/` (also under
17
+ `--dry-run`, since the render needs it; exit 2 when the registry is unreachable). Docker Hub
18
+ rate-limits anonymous pulls: in CI or offline, vendor the `.tgz` files under `charts/` (or point
19
+ `repository` at a mirror). To use another Postgres (a managed database, CloudNativePG, another
20
+ chart), keep the subcharts off and put the connection string in the Secret.
21
+
22
+ ## Values
23
+
24
+ ```yaml
25
+ image: { repository: <registry>/<name>, tag: "", pullPolicy: IfNotPresent } # tag: a quoted string, set per deploy
26
+ imagePullSecrets: []
27
+ replicaCount: 1
28
+ runtime: fastapi # selects the subchart-derived variable names
29
+ env: { APP_ENV: prod, MODEL_PROVIDER: ..., MODEL_NAME: ..., CHECKPOINTER: postgres, AUTH_POLICY: ...,
30
+ AUTH_READ_ACROSS_ROLES: "", AUTH_ADMIN_ROLES: "", A2A_NAME: <agent dir>, PORT: "8000" }
31
+ existingSecret: "" # default "<release>-app"
32
+ secretOptional: false # true in values-dev.yaml only
33
+ service: { port: 80, targetPort: 8000 }
34
+ appUrl: "" # A2A card URL; empty derives it from the gateway/ingress hostname
35
+ gateway: { enabled: true, className: "", hostname: "", parentRef: { name: "", namespace: "" } }
36
+ ingress: { enabled: false, className: "", hostname: "", annotations: {} }
37
+ route:
38
+ publicPaths: [{path: /chat, type: Exact}, {path: /threads, type: PathPrefix}, {path: /a2a/<agent dir>, type: PathPrefix}]
39
+ devPaths: [{path: /playground, type: Exact}, {path: /docs, type: Exact}, {path: /openapi.json, type: Exact}]
40
+ metrics: { scrapeAnnotations: false, serviceMonitor: { enabled: false, interval: 30s, scrapeTimeout: 10s, labels: {}, bearerToken: { enabled: false, secretName: "", key: METRICS_TOKEN } } } # secretName "": <release>-metrics
41
+ tls: { existingSecret: "", certManager: { enabled: false, issuerRef: { name: "", kind: ClusterIssuer } } }
42
+ postgresql: { enabled: false, image: {..., digest: sha256:...}, auth: { database: agent, username: agent, existingSecret: <name>-postgresql-auth },
43
+ primary: { persistence: { size: 8Gi }, lifecycleHooks: { preStop: pg_ctl -m fast stop } } }
44
+ postgresqlSecret: { create: true } # the dev database password, created once and kept
45
+ redis: { enabled: false, architecture: standalone, image: {..., digest: sha256:...}, auth: { enabled: false } }
46
+ probes: { liveness: {path: /health, ...}, readiness: {path: /ready, ...}, startup: {path: /health, ...} }
47
+ resources: { requests: { cpu: 100m, memory: 256Mi }, limits: { memory: 1Gi } }
48
+ hpa: { enabled: false, minReplicas: 2, maxReplicas: 5, targetCPU: 70 }
49
+ pdb: { enabled: false, minAvailable: 1 }
50
+ topologySpread: { enabled: false, topologyKey: kubernetes.io/hostname }
51
+ shutdown: { preStopSleepSeconds: 5, drainSeconds: 20 }
52
+ terminationGracePeriodSeconds: 30 # must exceed preStopSleepSeconds + drainSeconds
53
+ serviceAccount: { create: true, name: "", annotations: {} }
54
+ tracing: { enabled: false, capture: metadata, otlpEndpoint: "", langsmith: { project: "" } }
55
+ podAnnotations: {}
56
+ podSecurityContext: { runAsNonRoot: true, runAsUser: 1000, runAsGroup: 1000, fsGroup: 1000, seccompProfile: RuntimeDefault }
57
+ securityContext: { allowPrivilegeEscalation: false, privileged: false, capabilities: { drop: [ALL] }, readOnlyRootFilesystem: true }
58
+ tmpVolume: { sizeLimit: 256Mi, medium: "" } # /tmp, the only writable path
59
+ extraVolumes: []
60
+ extraVolumeMounts: []
61
+ networkPolicy: { enabled: false, ingressFrom: [], restrictEgress: false, egressTo: [] }
62
+ nodeSelector: {}
63
+ tolerations: []
64
+ affinity: {}
65
+ ```
66
+
67
+ Render-time refusals, each with a message naming the fix: an empty `image.tag` (the chart never
68
+ defaults to `latest`), an unquoted numeric tag (`tag: 0123456` would lose digits), a
69
+ `secretOptional` that is not a bool, an HPA without `resources.requests.cpu` or with
70
+ `minReplicas > maxReplicas`, an empty or malformed `route.publicPaths` while a Gateway or Ingress
71
+ is on (a Gateway API rule without matches publishes every path; at most 64 entries; absolute
72
+ paths only; `PathPrefix` or `Exact`), scraping while `env.METRICS_ENABLED` is off, and a
73
+ `terminationGracePeriodSeconds` not longer than `shutdown.preStopSleepSeconds +
74
+ shutdown.drainSeconds` (or a negative pause or drain).
75
+
76
+ `gateway.parentRef.name` is `required` by the HTTPRoute whenever `gateway.enabled` (the
77
+ staging/prod default): set it in `values-<env>.yaml`; `deploy` checks the merged values before
78
+ any tool runs and exits 3 naming the file when it is blank, and `infra check --env <env>` lists it
79
+ as the required check `gateway parentRef`. `appUrl` (or `env.APP_URL`) is the public base URL the
80
+ A2A agent card advertises; empty derives `https://<gateway.hostname>` (or the ingress hostname,
81
+ `http` without TLS) and with no hostname the pod falls back to its bind address and warns
82
+ (`NOTES.txt` warns too). Every `values-<env>.yaml` writes `image:` as a nested mapping (`image:`
83
+ newline ` tag: ...`), never an inline `{}` map, because `deploy` rewrites `image.tag` textually
84
+ and comments survive that way.
85
+
86
+ Precedence: the Deployment uses `envFrom: [secretRef: existingSecret]` plus `env:` from values.
87
+ Kubernetes gives `env` precedence over `envFrom`, so chart-set variables (`CHECKPOINTER`,
88
+ `TRACING_*`, and `POSTGRES_DSN` / `DATABASE_URI` / `REDIS_URI` **only when the corresponding
89
+ subchart is enabled**) win; when a subchart is disabled the Secret supplies the connection
90
+ string. Outside dev (`secretOptional: false`) the pods do not start until the Secret exists
91
+ (`CreateContainerConfigError`); `deploy` checks its required keys before it changes anything.
92
+
93
+ `values-dev.yaml`: `env.APP_ENV=dev`, `secretOptional: true`, `postgresql.enabled=true` (and
94
+ `redis.enabled=true` under `langgraph-server`), `gateway.enabled=false`, `image.tag: ""`.
95
+ `values-staging.yaml`: `secretOptional: false`, `postgresql.enabled=false`, gateway on,
96
+ hostnames blank. `values-prod.yaml`: the same plus `replicaCount: 2`, a PDB, `topologySpread`
97
+ on and larger requests (250m / 512Mi). Environment values files are config: `upgrade` never
98
+ overwrites them and they never contain secrets. `values.yaml` and `templates/**` are scaffolding
99
+ (3-way merged).
100
+
101
+ ## Pod security
102
+
103
+ The pod runs as uid/gid 1000 with `runAsNonRoot`, seccomp `RuntimeDefault`, every capability
104
+ dropped, no privilege escalation, a read-only root filesystem (a `/tmp` emptyDir is the only
105
+ writable path; `HOME=/tmp`, `PYTHONDONTWRITEBYTECODE=1`) and no service-account token. The
106
+ rendered pod passes the `restricted` Pod Security Standard. Both images (`Dockerfile`,
107
+ `Dockerfile.langgraph-server`) run as 1000:1000 and are built for this.
108
+
109
+ ## Probes
110
+
111
+ Liveness and startup ask `/health` (the process answers); readiness asks `/ready` (the database
112
+ and run store are set up and answer within 2 s), so a pod that loses its database leaves the
113
+ Service endpoints instead of being restarted, and a pod that starts before its database (a first
114
+ install, a node drain during an outage) waits unready instead of crash-looping. Tune `probes.<kind>.{path,periodSeconds,timeoutSeconds,failureThreshold}`.
115
+
116
+ ## Graceful shutdown
117
+
118
+ On a rollout, `deploy --restart`, a scale-down or a node drain, a stopping pod first keeps
119
+ serving for `shutdown.preStopSleepSeconds` (default 5; a `preStop` hook running `sleep`) while
120
+ the Service endpoints, kube-proxy and the Gateway stop sending it new connections; without that
121
+ pause a rolling restart refuses connections. Then it gets SIGTERM and has
122
+ `shutdown.drainSeconds` (default 20) to finish in-flight requests and streams: the chart sets
123
+ `UVICORN_TIMEOUT_GRACEFUL_SHUTDOWN` (fastapi) or `BG_JOB_SHUTDOWN_GRACE_PERIOD_SECS`
124
+ (langgraph-server, whose image fixes uvicorn's own timeout) unless `env` sets it. A `/chat`
125
+ stream still running after that is cut off. The kubelet kills the pod at
126
+ `terminationGracePeriodSeconds` (default 30), counted from the start of the pause, so the chart
127
+ refuses a grace period that is not longer than pause + drain. For long runs (`RUN_TIMEOUT_S`)
128
+ raise `drainSeconds` and `terminationGracePeriodSeconds` together; `0` turns the pause (or the
129
+ drain limit) off.
130
+
131
+ The bundled dev Postgres gets a `preStop` hook too (`pg_ctl -m fast stop`): Postgres answers
132
+ Kubernetes' SIGTERM with a smart shutdown that waits for every client, and the agent's pooled
133
+ sessions never leave, so without it a restart of the database pod ends in a SIGKILL after 30 s
134
+ and crash recovery. With it the pod stops in about a second and restarts cleanly.
135
+
136
+ ## Traffic entry and TLS
137
+
138
+ - Default: Gateway API `HTTPRoute` (`gateway.networking.k8s.io/v1`; Kubernetes 1.28+). Set
139
+ `gateway.parentRef` to the operator's Gateway and `gateway.hostname`.
140
+ - Alternative: `ingress.enabled=true` with `ingress.className`.
141
+ - Only `route.publicPaths` are published (plus `route.devPaths` under `APP_ENV=dev`): `/chat`,
142
+ `/threads` and `/a2a/<agent dir>` by default. `/health`, `/ready` and `/metrics` stay inside
143
+ the cluster (the kubelet probes the pod directly). Keep the `/a2a` entry in step with
144
+ `env.A2A_NAME`. Under `langgraph-server`, `/threads` also publishes the server's native thread
145
+ routes (limited to the caller's own threads by the auth handler; a run started there skips
146
+ `/chat`'s guardrails); `/assistants`, `/runs`, `/store`, `/crons` are commented examples.
147
+ - No controller is assumed or installed. Choose a maintained implementation the platform supports
148
+ (Envoy Gateway, Cilium, Istio, Traefik, NGINX Gateway Fabric, Kong, or the platform's own
149
+ router). `infra check` lists the `GatewayClass` and `IngressClass` objects present.
150
+ - TLS: `tls.existingSecret` (operator-provided certificate) is the default expectation;
151
+ `tls.certManager.enabled=true` adds a `Certificate` and makes cert-manager a prerequisite.
152
+ - Authentication is enforced by the app (the auth policy), not by route annotations, so it is
153
+ identical on every controller and under local-load. Rate limiting is not built in: configure it
154
+ on the Gateway or ingress controller.
155
+
156
+ ## Metrics and network policy
157
+
158
+ `/metrics` (Prometheus text) is served on the http port. `metrics.scrapeAnnotations: true` adds
159
+ `prometheus.io/scrape|path|port` pod annotations; `metrics.serviceMonitor.enabled: true` renders a
160
+ `ServiceMonitor` (Prometheus Operator CRDs needed; `labels` for its selector). With
161
+ `METRICS_TOKEN` in the Secret (add it to `secrets.keys`) the scraper must send
162
+ `Authorization: Bearer <token>`: `metrics.serviceMonitor.bearerToken.enabled: true` makes the
163
+ ServiceMonitor do so (its endpoint's `authorization`). The token is read from
164
+ `<release>-metrics`, a Secret holding only `METRICS_TOKEN` that `secrets apply` and a direct
165
+ `deploy` write alongside the app Secret, so Prometheus needs read access to that one Secret and
166
+ never to the app Secret (provider key, API tokens, DSN); `bearerToken.secretName` / `.key` name
167
+ a Secret you manage instead. `infra check` reports a missing token Secret. Pod annotations cannot
168
+ carry a token: an annotation-discovering Prometheus needs the token in its own scrape job
169
+ (`authorization.credentials_file`), or leave `METRICS_TOKEN` unset and rely on the
170
+ NetworkPolicy.
171
+
172
+ `networkPolicy.enabled: true` admits only the http port, from `networkPolicy.ingressFrom` when
173
+ listed (list the Gateway's namespace and, if you scrape, the Prometheus namespace);
174
+ `restrictEgress: true` also limits egress to DNS (the kube-dns pods) and
175
+ `networkPolicy.egressTo`. It is off by default (it needs a CNI that enforces NetworkPolicy and
176
+ addresses only you know). `examples/networkpolicy.yaml` is a worked staging/prod example: in
177
+ from the Gateway's and Prometheus's namespaces; out to DNS, the database (Redis too under
178
+ `langgraph-server`), an on-network model server (`openai-compatible`) and HTTPS on public
179
+ addresses only (every private range and `169.254.169.254` excluded), with a commented entry for
180
+ an on-network API. Copy its `networkPolicy` block into `values-<env>.yaml` and replace the
181
+ addresses marked `CHANGE` (NetworkPolicy matches addresses, not host names; add a JWKS URL or
182
+ an OTLP collector the agent must reach). The chart tests render and validate it; once deployed,
183
+ `/ready` answers 200 (DNS and the database reachable) and a pod in another namespace cannot
184
+ connect.
185
+
186
+ ## Persistence toggles
187
+
188
+ | Runtime | Subchart on (`values-dev.yaml`) | Subchart off (staging/prod) |
189
+ |---|---|---|
190
+ | fastapi | chart sets `POSTGRES_DSN` from the chart-managed `<name>-postgresql-auth` Secret; `CHECKPOINTER=postgres` | `POSTGRES_DSN` from the Secret |
191
+ | langgraph-server | chart sets `DATABASE_URI` from the subchart and, with `redis.enabled` (on in `values-dev.yaml` for this runtime), `REDIS_URI`; the Deployment always sets `LANGGRAPH_SERVER=1` so `fast_api_app.py` detects the mounted runtime | both from the Secret |
192
+
193
+ The dev database password lives in `<name>-postgresql-auth`, created once by the chart and kept
194
+ across upgrades (`helm lookup`), uninstalls and Argo CD syncs (the Applications ignore its data).
195
+ The agent's database is agent-owned: its own credentials, migrations, backups, quotas. Never point
196
+ it at another application's operational database. Postgres `max_connections` must cover
197
+ replicas x (`DB_POOL_MAX_SIZE` + 1): the extra connection per replica renews the cross-replica
198
+ run leases (rows with a 30 s expiry, so a replica lost with its node frees its threads 30 s
199
+ later, and a database restart drops no lease). Connections get `connect_timeout=5` and TCP
200
+ keepalives unless the DSN sets them.
201
+
202
+ ### External database: a least-privileged role over TLS
203
+
204
+ The agent needs no superuser. It creates its tables on first start (under an advisory lock) and
205
+ then reads and writes only them, so a role that owns its own database is enough:
206
+
207
+ ```sql
208
+ CREATE ROLE agent LOGIN PASSWORD '...' NOSUPERUSER NOCREATEDB NOCREATEROLE;
209
+ CREATE DATABASE agent OWNER agent;
210
+ REVOKE ALL ON DATABASE agent FROM PUBLIC;
211
+ -- A shared database instead: CREATE SCHEMA agent AUTHORIZATION agent;
212
+ -- ALTER ROLE agent SET search_path = agent;
213
+ ```
214
+
215
+ The connection string goes to psycopg (libpq) unchanged, so every libpq parameter works. Require
216
+ TLS and check the server's certificate:
217
+ `postgresql://agent:<password>@db.example.com:5432/agent?sslmode=verify-full&sslrootcert=/etc/db-ca/ca.crt`,
218
+ with the CA mounted from a Secret or ConfigMap:
219
+
220
+ ```yaml
221
+ extraVolumes:
222
+ - name: db-ca
223
+ secret: { secretName: db-ca } # kubectl create secret generic db-ca --from-file=ca.crt
224
+ extraVolumeMounts:
225
+ - { name: db-ca, mountPath: /etc/db-ca, readOnly: true }
226
+ ```
227
+
228
+ A certificate from a public CA needs no file: `sslrootcert=system`. `PGSSLMODE` /
229
+ `PGSSLROOTCERT` in the chart env work too. `deploy` and `secrets apply` warn outside dev when the
230
+ DSN does not require TLS (`sslmode` `require`, `verify-ca` or `verify-full`), and `infra check`
231
+ reports it (`database tls`); the value is never printed. On the server, `hostssl` lines in
232
+ `pg_hba.conf` (and a `hostnossl ... reject` for the agent's role) refuse clear-text connections.
233
+
234
+ ## Scaling and availability
235
+
236
+ `hpa.enabled` (needs metrics-server; `infra check` requires it only then; the chart refuses it
237
+ without `resources.requests.cpu`) and `pdb.enabled` (on in prod) ship disabled in `values.yaml`.
238
+ `replicaCount > 1` requires `postgres` (memory + kubernetes is refused at scaffold time). Requests
239
+ (100m / 256Mi) and a 1Gi memory limit are set by default, with no CPU limit; override them per
240
+ environment. `topologySpread.enabled` (prod) spreads replicas across nodes softly.
241
+
242
+ ## Local-load dev clusters
243
+
244
+ `deploy` identifies a local cluster from the cluster itself (its nodes' `providerID`, names and
245
+ labels), confirmed with the tool's own listing; the context name decides only when the nodes
246
+ cannot be read (and a `kind-*` name is still confirmed with `kind get clusters`):
247
+
248
+ | Cluster | Load command |
249
+ |---|---|
250
+ | kind (confirmed with `kind get clusters`) | `kind load docker-image <image> --name <cluster>` |
251
+ | k3d (`k3d cluster list`) | `k3d image import <image> -c <cluster>` |
252
+ | k3s (one of its nodes is this machine) | `docker save -o .graph-agents-cli/image.tar` then `k3s ctr images import` (no `sudo`; needs root on most hosts, so run `deploy` as a user allowed to invoke `k3s ctr`) |
253
+ | minikube (`minikube profile list`) | `minikube image load <image> [-p <profile>]` |
254
+ | Docker Desktop, Rancher Desktop, OrbStack | none (shared daemon) |
255
+
256
+ Anything else gets the image through the registry. `deploy --env dev` then applies the Secret,
257
+ runs `helm dependency build` if needed, and runs `helm upgrade --install <name>
258
+ deployment/helm/<name> -n <name>-dev --create-namespace -f values.yaml -f values-dev.yaml --set
259
+ image.repository=...,image.tag=<tag>,existingSecret=<name>-app --wait --timeout 5m
260
+ --kube-context <context>` (`<tag>` is the short sha, `<sha>-dirty-<time>` for a tree with
261
+ uncommitted changes, a UTC timestamp outside git, or `--tag`). Access: `kubectl -n <name>-dev
262
+ port-forward svc/<name> 8000:80`, then `GRAPH_AGENTS_CLI_API_KEY=<API_KEY> graph-agents-cli run
263
+ --url http://localhost:8000 "hello"` (under `jwt`, a user's token; the variable keeps the
264
+ credential out of argv).
265
+
266
+ ## Multi-node self-hosted clusters
267
+
268
+ kubeadm, RKE2, OpenShift and similar: images go through the registry (`--registry`, default
269
+ `ghcr.io/<org>`; Harbor or `registry:2` in-cluster work the same; the placeholder
270
+ `ghcr.io/CHANGE-ME` is refused with exit 3). Private images need one image pull secret referenced
271
+ from `imagePullSecrets`; it is an operator prerequisite that `infra check` reports. On OpenShift
272
+ use the platform router through `ingress` or its Gateway implementation.
273
+
274
+ ## Disconnected profile (cluster side)
275
+
276
+ Mirror the base images into the registry (`python:3.12.14-slim-bookworm` and
277
+ `ghcr.io/astral-sh/uv` for fastapi, through the `PYTHON_IMAGE` / `UV_IMAGE` build args;
278
+ `langchain/langgraph-api` only if that runtime is ever cleared for the profile), vendor the
279
+ subcharts and mirror their images, point `UV_INDEX_URL` at a mirror when building, set
280
+ `OPENAI_BASE_URL` / `JUDGE_BASE_URL` at on-network model servers, and either
281
+ `tracing.enabled=false` or `tracing.otlpEndpoint` at an in-cluster collector. `infra check
282
+ --profile disconnected` fails on any hosted dependency.
283
+
284
+ ## Inspecting what will be applied
285
+
286
+ ```bash
287
+ graph-agents-cli deploy --env staging --dry-run # commands + rendered manifests
288
+ helm template <name> deployment/helm/<name> -f deployment/helm/<name>/values.yaml \
289
+ -f deployment/helm/<name>/values-staging.yaml --set-string image.tag=<tag> \
290
+ --set gateway.parentRef.name=<gateway>
291
+ helm lint deployment/helm/<name>
292
+ ```
293
+
294
+ ## Rollback (direct and helm-push)
295
+
296
+ `deploy` rolls a failed rollout back itself (`--atomic`, the default): it prints the pods'
297
+ states, this release's warning events since the deploy started and the logs, then rolls back to
298
+ the newest good revision, or uninstalls a first install that never succeeded. It only acts on the
299
+ revision this run created; if another helm operation holds the release it refuses and prints the
300
+ command that clears a stale lock. Once the release is back where it was (rolled back, never
301
+ changed, or uninstalled), the app Secret this deploy applied is put back too (a Secret it created
302
+ is deleted), unless someone changed it in the meantime; when the release stays on the failed
303
+ revision (`--no-atomic`, another deploy, an unreadable history) the Secret keeps the new values
304
+ and the error says how to put the old ones back. `deploy --status` reports the rollout within
305
+ `--timeout` (default 60 s: replicas, image, helm revision, each pod's readiness and restarts;
306
+ diagnostics and exit 1 when not ready), and `deploy --restart` waits for the new pods (default
307
+ 5 m; diagnostics and exit 2 when they do not become ready, while the old pods keep serving).
308
+ A manual rollback of a release that deployed fine:
309
+
310
+ ```bash
311
+ helm -n <name>-<env> history <name>
312
+ helm -n <name>-<env> rollback <name> <revision>
313
+ ```
314
+
315
+ argocd mode: revert on `main` through a PR (see `gitops.md`).
@@ -0,0 +1,160 @@
1
+ # Secrets
2
+
3
+ Plain Kubernetes Secrets, provisioned per environment, restricted to allow-listed keys. No
4
+ secrets controller is required.
5
+
6
+ ## Contract
7
+
8
+ - The chart references the app Secret by name (`existingSecret`, default `<release>-app`) and
9
+ mounts it with `envFrom`. The chart never templates the app Secret.
10
+ - Type `Opaque`, one key per allow-listed variable, in namespace `<name>-<env>`.
11
+ - Outside dev the Secret is required (`secretOptional: false`): pods do not start without it
12
+ (`CreateContainerConfigError`). `values-dev.yaml` sets `secretOptional: true`.
13
+ - Non-secret configuration is the chart's ConfigMap plus Deployment `env` (which overrides
14
+ `envFrom`).
15
+
16
+ ## Allow-listed keys (`secrets.keys` in the manifest)
17
+
18
+ Scaffold default:
19
+
20
+ | Key | When |
21
+ |---|---|
22
+ | `OPENAI_API_KEY` \| `ANTHROPIC_API_KEY` \| `GOOGLE_API_KEY` \| `MODEL_API_KEY` | the provider key for `model_provider` |
23
+ | `JUDGE_API_KEY` | always (defaults to the provider key at runtime) |
24
+ | `POSTGRES_DSN` | runtime `fastapi` |
25
+ | `DATABASE_URI`, `REDIS_URI` | runtime `langgraph-server` |
26
+ | `API_KEY` | `shared-bearer` only (the one policy that reads it; generated when missing). Projects created before this change keep it listed; it is harmless there |
27
+ | `LANGSMITH_API_KEY` | always (used only when tracing is enabled) |
28
+ | each `auth: bearer` API's `token_env` | when `api-policy.yaml` declares that API |
29
+ | `AUTH_JWT_SECRET` | added automatically (not listed) under `jwt` when the chart values or the env file opt into HS* (`AUTH_JWT_ALLOW_HS=true` or an HS* algorithm) |
30
+
31
+ Only these are exported from the env file, never the whole file. Add any other secret the
32
+ project uses to the list: for example `METRICS_TOKEN`, `PRINCIPAL_HASH_SALT`, a LangGraph
33
+ licence key, or a tool credential. `scaffold enhance` recomputes the list when the runtime or
34
+ provider changes (keys you added are kept).
35
+
36
+ `METRICS_TOKEN`, once the app Secret holds it, is also written alone into `<name>-metrics`, the
37
+ Secret the chart's ServiceMonitor reads its bearer token from
38
+ (`metrics.serviceMonitor.bearerToken`): the scraper then needs read access to that Secret only,
39
+ never to the app Secret with the provider key, the API tokens and the DSN.
40
+
41
+ ## Required keys
42
+
43
+ `deploy` and `secrets status` treat these as required, following the environment's merged chart
44
+ values: the provider key (not for `openai-compatible` or `fake`), `API_KEY` under
45
+ `shared-bearer`, `AUTH_JWT_SECRET` under `jwt` when the chart values list an HS* algorithm, and
46
+ `POSTGRES_DSN` or `DATABASE_URI`/`REDIS_URI` unless the bundled subchart provides them or
47
+ `CHECKPOINTER=memory`. A key set as a plain chart `env` value is satisfied. Only keys in
48
+ `secrets.keys` can be required: removing one from the allow-list is how an environment opts out.
49
+
50
+ ## Commands
51
+
52
+ ```bash
53
+ graph-agents-cli secrets apply --env <env> [--env-file <file>] [--context <ctx>] [--yes] [--rotate-api-key] [--dry-run]
54
+ graph-agents-cli secrets status --env <env> [--context <ctx>] [--strict] [--dry-run]
55
+ ```
56
+
57
+ - **Env file:** `--env-file`, else `.env.<env>`. Only `dev` falls back to `.env` (a developer's
58
+ local keys never reach staging or prod); any other environment without one exits 3 with the
59
+ list of allow-listed keys.
60
+ - **Kube context:** `--context`, else `environments.<env>.context`, else the kubeconfig's
61
+ current context, which outside dev needs a confirmation (`Apply the Secret for <env> on
62
+ context '<ctx>'? [y/N]`) or `--yes`. The context and API server are printed first. An explicit
63
+ context missing from the kubeconfig is exit 3.
64
+ - **`apply`** creates the namespace when it is missing, then applies `<name>-app` with
65
+ `kubectl create secret generic <name>-app --from-env-file=<0600 temporary file> --dry-run=client -o yaml | kubectl apply --server-side --field-manager=graph-agents-cli --force-conflicts -f -`.
66
+ The temporary file holds only the allow-listed keys and is deleted afterwards, so no value
67
+ appears on a command line; server-side apply writes no `last-applied-configuration` annotation
68
+ (one left by an older client-side apply is removed and reported). `--force-conflicts` makes
69
+ the CLI the owner of the allow-listed keys: do not let another controller manage them.
70
+ - **Merge:** a key the env file sets replaces the live value; an allow-listed key the env file
71
+ leaves out is kept from the live Secret (so a partial env file never deletes keys). Remove a key
72
+ by dropping it from `secrets.keys` (the next apply removes it) or with `kubectl`.
73
+ - **`API_KEY`:** the live key wins. When the env file sets a different one, the live key is kept
74
+ with a warning unless `--rotate-api-key` is passed (clients with the old key then get 401;
75
+ restart the pods with `deploy --restart`). Under `shared-bearer` (the policy in the chart's
76
+ `env.AUTH_POLICY`, else the manifest's), when it is in neither the env file nor the live
77
+ Secret, a key (32 random bytes, hex) is generated, applied, and written to the env file (mode
78
+ 0600) after the apply succeeds; it is never printed. If writing the file fails, the kubectl
79
+ command to read it back is printed. `jwt` and `custom` never get a generated key (one the env
80
+ file or the live Secret already holds is kept; drop `API_KEY` from `secrets.keys` to remove
81
+ it).
82
+ - Values must be single-line (the env-file format cannot carry a newline; exit 3 naming the
83
+ key). Exit 2 when kubectl fails (including a `get secret` failure that is not `NotFound`, which
84
+ is never treated as "generate a new key").
85
+ - **`apply --dry-run`** prints the pipeline and a redacted manifest; it reads nothing from the
86
+ cluster, prints no key, and notes that allow-listed keys absent from the env file would be kept
87
+ from the live Secret.
88
+ - Outside dev, `apply` warns when the external database's DSN does not require TLS (see
89
+ `kubernetes.md`, "External database"); the value is never printed.
90
+ - **`status`** runs `kubectl get secret <name>-app -o json` and lists `present`, `missing
91
+ required`, `missing optional` and `not in the allow-list` keys, never values. Exit codes
92
+ (usable as a gate): 0 every required key present, 1 the Secret or a required key is missing
93
+ (any allow-listed key with `--strict`), 2 kubectl failed (connection, credentials, RBAC), 3
94
+ configuration error (unknown environment or context, no manifest). `--dry-run` prints the
95
+ command only.
96
+ - `apply` is **not** refused under CI: keeping application secrets out of CI is the procedure
97
+ below (the scaffolded workflows never hold them), not a runtime guard.
98
+ - In `helm-push` and `argocd` modes `deploy` never applies Secrets (`--env-file` and
99
+ `--rotate-api-key` are refused there); helm-push `deploy` still checks the live Secret's
100
+ required keys before helm runs.
101
+
102
+ ## Direct-mode `deploy`
103
+
104
+ `deploy --env <env>` in `cd: skip` applies the Secret with the same rules as `secrets apply`,
105
+ but first checks, read-only, that the Secret it would produce holds every required key: when
106
+ one is missing it exits 1 before anything is built, pushed or changed, naming the keys and the
107
+ env file to add them to. `dev` without an env file leaves the Secret as it is (and still checks
108
+ it). So does an env file that sets none of the allow-listed keys (a keyless project: the `fake`
109
+ model, a keyless `openai-compatible` endpoint, no `shared-bearer` key), decided before anything
110
+ is built; outside dev, where the chart requires the Secret (`secretOptional: false`), a missing
111
+ Secret then exits 1 before the build. `deploy --dry-run` makes the same read of the live Secret
112
+ and refuses (exit 1) what the real run would; when the cluster cannot be read it says the check
113
+ could not be completed.
114
+
115
+ When the rollout fails and the release is put back where it was (rolled back, failed before a
116
+ new revision, or a first install uninstalled), `deploy` also puts the app Secret (and
117
+ `<name>-metrics`) back to the values it held before, byte for byte, with a `resourceVersion`
118
+ precondition; a Secret this deploy created is deleted. A Secret someone changed after this
119
+ deploy applied it is left alone. When the release stays on the failed revision (`--no-atomic`,
120
+ another deploy, an unreadable history), the Secret keeps the new values and the error names the
121
+ changed keys and how to re-apply the previous ones. When the pods are not replaced (same image
122
+ and chart values) but the Secret changed, `deploy` says to run `deploy --restart`.
123
+
124
+ ## Ownership in CD modes
125
+
126
+ The manifest records `secrets.owner` (free text: a team or role). That owner creates the Secret
127
+ once per environment with `graph-agents-cli secrets apply --env <env>` (from `.env.<env>`) from a
128
+ workstation with cluster access, or with `kubectl create secret generic`. CI never holds
129
+ application secrets. Argo never manages the app Secret. External Secrets Operator or Sealed
130
+ Secrets can replace this procedure; drop the keys they manage from `secrets.keys`.
131
+
132
+ ## Rotation
133
+
134
+ 1. Put the new value in `.env.<env>`.
135
+ 2. `graph-agents-cli secrets apply --env <env>` (add `--rotate-api-key` for `API_KEY`).
136
+ 3. `graph-agents-cli deploy --restart --env <env>` (`kubectl rollout restart`), because an
137
+ externally managed Secret does not change the pod template checksum. It waits until the new
138
+ pods are ready (`--timeout`, default 5m) and exits 2 with the pods' states, events and logs
139
+ when they are not (a bad value: the old pods keep serving). In argocd environments, prefer an
140
+ Argo resource action; self-heal may revert the restart annotation.
141
+ 4. For `API_KEY`, update the client application's configuration in the same window.
142
+
143
+ ## What is never a secret
144
+
145
+ `MODEL_PROVIDER`, `MODEL_NAME`, `OPENAI_BASE_URL`, `CHECKPOINTER`, `AUTH_POLICY`,
146
+ `AUTH_READ_ACROSS_ROLES`, `AUTH_ADMIN_ROLES`, the `AUTH_JWT_*` settings except
147
+ `AUTH_JWT_SECRET`, each API's `base_url_env`, the limits and logging settings, `TRACING_ENABLED`,
148
+ `TRACE_CAPTURE`, `LANGSMITH_PROJECT`, `LANGSMITH_ENDPOINT`, `OTEL_EXPORTER_OTLP_ENDPOINT`,
149
+ `APP_ENV`, `PORT`: these live in `values-<env>.yaml` `env:`. Settings that appear only in the
150
+ env file never reach the pods (the CLI warns about HS* JWT settings found there).
151
+
152
+ ## Checks
153
+
154
+ - `secrets status --env <env>` after `apply`: exit 0 means every required key is there.
155
+ - A pod in `CreateContainerConfigError` means the Secret is missing (outside dev).
156
+ - `infra check --env <env>` reports the app Secret's missing required keys (with a `secrets
157
+ apply` command that runs as printed), the `<name>-metrics` token Secret when the ServiceMonitor
158
+ sends a token, whether the external DSN requires TLS, and whether the image pull secret named
159
+ in `imagePullSecrets` exists; that pull secret is an operator prerequisite, not managed by
160
+ `secrets apply`.