snulbug 0.2.3__tar.gz → 0.2.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (406) hide show
  1. {snulbug-0.2.3 → snulbug-0.2.5}/CHANGELOG.md +32 -0
  2. {snulbug-0.2.3 → snulbug-0.2.5}/PKG-INFO +7 -2
  3. {snulbug-0.2.3 → snulbug-0.2.5}/README.md +6 -1
  4. {snulbug-0.2.3 → snulbug-0.2.5}/docs/README.md +1 -0
  5. snulbug-0.2.5/docs/always-on-runtime.md +385 -0
  6. snulbug-0.2.5/docs/credential-brokers.md +172 -0
  7. {snulbug-0.2.3 → snulbug-0.2.5}/docs/devcontainers.md +2 -2
  8. {snulbug-0.2.3 → snulbug-0.2.5}/docs/mcp-evidence.md +57 -0
  9. {snulbug-0.2.3 → snulbug-0.2.5}/docs/mcp-proxy.md +145 -5
  10. {snulbug-0.2.3 → snulbug-0.2.5}/docs/mcp-share.md +58 -14
  11. snulbug-0.2.5/docs/ngrok-end-to-end.md +375 -0
  12. {snulbug-0.2.3 → snulbug-0.2.5}/docs/release.md +12 -2
  13. {snulbug-0.2.3 → snulbug-0.2.5}/docs/share-doctor-checks.md +19 -0
  14. snulbug-0.2.5/docs/upgrading-to-0.2.4.md +112 -0
  15. snulbug-0.2.5/docs/upgrading-to-0.2.5.md +97 -0
  16. {snulbug-0.2.3 → snulbug-0.2.5}/examples/codespace_local_gateway/.devcontainer/devcontainer.json +1 -1
  17. {snulbug-0.2.3 → snulbug-0.2.5}/features/snulbug/README.md +1 -1
  18. {snulbug-0.2.3 → snulbug-0.2.5}/features/snulbug/devcontainer-feature.json +1 -1
  19. {snulbug-0.2.3 → snulbug-0.2.5}/pyproject.toml +1 -1
  20. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/__init__.py +1 -1
  21. snulbug-0.2.5/snulbug/_operations_smoke.py +336 -0
  22. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/cli/rich_reports.py +21 -0
  23. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/cli/rich_share.py +59 -0
  24. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/cli/share.py +46 -6
  25. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/config.py +6 -0
  26. snulbug-0.2.5/snulbug/credential_health.py +248 -0
  27. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/credentials.py +62 -14
  28. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/events.py +11 -0
  29. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/fabric.py +13 -9
  30. snulbug-0.2.5/snulbug/http_mounts.py +185 -0
  31. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/http_routes.py +127 -10
  32. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/inspection.py +52 -9
  33. snulbug-0.2.5/snulbug/ngrok_drift.py +438 -0
  34. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/proxy.py +154 -30
  35. snulbug-0.2.5/snulbug/public_origin.py +136 -0
  36. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/redaction.py +4 -0
  37. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/release_qa.py +45 -0
  38. snulbug-0.2.5/snulbug/request_explanations.py +348 -0
  39. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/share.py +131 -13
  40. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/share_console.py +287 -443
  41. snulbug-0.2.5/snulbug/share_runtime.py +642 -0
  42. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/share_session.py +1 -0
  43. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/simulator.py +1 -1
  44. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/tunnel.py +88 -21
  45. snulbug-0.2.5/tests/test_credential_health.py +283 -0
  46. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_fabric.py +2 -1
  47. snulbug-0.2.5/tests/test_gateway_snapshot.py +54 -0
  48. snulbug-0.2.5/tests/test_http_mounts.py +265 -0
  49. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_http_routes.py +222 -3
  50. snulbug-0.2.5/tests/test_ngrok_drift.py +369 -0
  51. snulbug-0.2.5/tests/test_operations_smoke.py +45 -0
  52. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_proxy.py +59 -0
  53. snulbug-0.2.5/tests/test_public_origin.py +209 -0
  54. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_release_qa.py +30 -6
  55. snulbug-0.2.5/tests/test_request_explanations.py +427 -0
  56. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_share_console.py +95 -12
  57. snulbug-0.2.5/tests/test_share_runtime.py +580 -0
  58. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_tunnel_init.py +68 -0
  59. snulbug-0.2.3/docs/credential-brokers.md +0 -67
  60. snulbug-0.2.3/docs/ngrok-end-to-end.md +0 -241
  61. {snulbug-0.2.3 → snulbug-0.2.5}/.gitignore +0 -0
  62. {snulbug-0.2.3 → snulbug-0.2.5}/CODE_OF_CONDUCT.md +0 -0
  63. {snulbug-0.2.3 → snulbug-0.2.5}/CONTRIBUTING.md +0 -0
  64. {snulbug-0.2.3 → snulbug-0.2.5}/LICENSE +0 -0
  65. {snulbug-0.2.3 → snulbug-0.2.5}/SECURITY.md +0 -0
  66. {snulbug-0.2.3 → snulbug-0.2.5}/docs/actions.md +0 -0
  67. {snulbug-0.2.3 → snulbug-0.2.5}/docs/auth-providers.md +0 -0
  68. {snulbug-0.2.3 → snulbug-0.2.5}/docs/aws-mcp.md +0 -0
  69. {snulbug-0.2.3 → snulbug-0.2.5}/docs/bundles.md +0 -0
  70. {snulbug-0.2.3 → snulbug-0.2.5}/docs/ci-policy-gates.md +0 -0
  71. {snulbug-0.2.3 → snulbug-0.2.5}/docs/comparison.md +0 -0
  72. {snulbug-0.2.3 → snulbug-0.2.5}/docs/discovery-providers.md +0 -0
  73. {snulbug-0.2.3 → snulbug-0.2.5}/docs/event-sink-providers.md +0 -0
  74. {snulbug-0.2.3 → snulbug-0.2.5}/docs/evidence-exporters.md +0 -0
  75. {snulbug-0.2.3 → snulbug-0.2.5}/docs/getting-started.md +0 -0
  76. {snulbug-0.2.3 → snulbug-0.2.5}/docs/lua-policy-dsl.md +0 -0
  77. {snulbug-0.2.3 → snulbug-0.2.5}/docs/lua-request-api.md +0 -0
  78. {snulbug-0.2.3 → snulbug-0.2.5}/docs/mcp-auth-recipes.md +0 -0
  79. {snulbug-0.2.3 → snulbug-0.2.5}/docs/mcp-client-recipes.md +0 -0
  80. {snulbug-0.2.3 → snulbug-0.2.5}/docs/mcp-fabric.md +0 -0
  81. {snulbug-0.2.3 → snulbug-0.2.5}/docs/mcp-gateway.md +0 -0
  82. {snulbug-0.2.3 → snulbug-0.2.5}/docs/mcp-guide.md +0 -0
  83. {snulbug-0.2.3 → snulbug-0.2.5}/docs/mcp-impact.md +0 -0
  84. {snulbug-0.2.3 → snulbug-0.2.5}/docs/mcp-learn.md +0 -0
  85. {snulbug-0.2.3 → snulbug-0.2.5}/docs/mcp-policy.md +0 -0
  86. {snulbug-0.2.3 → snulbug-0.2.5}/docs/mcp-presets.md +0 -0
  87. {snulbug-0.2.3 → snulbug-0.2.5}/docs/mcp-protocol.md +0 -0
  88. {snulbug-0.2.3 → snulbug-0.2.5}/docs/mcp-recorder.md +0 -0
  89. {snulbug-0.2.3 → snulbug-0.2.5}/docs/mcp-schemas.md +0 -0
  90. {snulbug-0.2.3 → snulbug-0.2.5}/docs/policy-deny-backoff.md +0 -0
  91. {snulbug-0.2.3 → snulbug-0.2.5}/docs/quickstart.md +0 -0
  92. {snulbug-0.2.3 → snulbug-0.2.5}/docs/roadmap.md +0 -0
  93. {snulbug-0.2.3 → snulbug-0.2.5}/docs/security-model.md +0 -0
  94. {snulbug-0.2.3 → snulbug-0.2.5}/docs/state.md +0 -0
  95. {snulbug-0.2.3 → snulbug-0.2.5}/docs/tool-catalog-projection.md +0 -0
  96. {snulbug-0.2.3 → snulbug-0.2.5}/docs/tool-risk-analyzers.md +0 -0
  97. {snulbug-0.2.3 → snulbug-0.2.5}/docs/tunnel-providers.md +0 -0
  98. {snulbug-0.2.3 → snulbug-0.2.5}/docs/upstream-transports.md +0 -0
  99. {snulbug-0.2.3 → snulbug-0.2.5}/examples/auth_claim_patterns/.gitignore +0 -0
  100. {snulbug-0.2.3 → snulbug-0.2.5}/examples/auth_claim_patterns/README.md +0 -0
  101. {snulbug-0.2.3 → snulbug-0.2.5}/examples/auth_claim_patterns/ci-workload-identity.toml +0 -0
  102. {snulbug-0.2.3 → snulbug-0.2.5}/examples/auth_claim_patterns/group-gated-tools.toml +0 -0
  103. {snulbug-0.2.3 → snulbug-0.2.5}/examples/auth_claim_patterns/policy.lua +0 -0
  104. {snulbug-0.2.3 → snulbug-0.2.5}/examples/auth_claim_patterns/tenant-isolation.toml +0 -0
  105. {snulbug-0.2.3 → snulbug-0.2.5}/examples/basic/README.md +0 -0
  106. {snulbug-0.2.3 → snulbug-0.2.5}/examples/basic/app.py +0 -0
  107. {snulbug-0.2.3 → snulbug-0.2.5}/examples/bundles/idempotency.snulbug/README.md +0 -0
  108. {snulbug-0.2.3 → snulbug-0.2.5}/examples/bundles/idempotency.snulbug/fixtures/duplicate-delivery.json +0 -0
  109. {snulbug-0.2.3 → snulbug-0.2.5}/examples/bundles/idempotency.snulbug/fixtures/first-delivery.json +0 -0
  110. {snulbug-0.2.3 → snulbug-0.2.5}/examples/bundles/idempotency.snulbug/manifest.json +0 -0
  111. {snulbug-0.2.3 → snulbug-0.2.5}/examples/bundles/idempotency.snulbug/policy.lua +0 -0
  112. {snulbug-0.2.3 → snulbug-0.2.5}/examples/bundles/idempotency.snulbug/snapshots/duplicate.json +0 -0
  113. {snulbug-0.2.3 → snulbug-0.2.5}/examples/bundles/idempotency.snulbug/snapshots/empty.json +0 -0
  114. {snulbug-0.2.3 → snulbug-0.2.5}/examples/bundles/mcp-gateway.snulbug/README.md +0 -0
  115. {snulbug-0.2.3 → snulbug-0.2.5}/examples/bundles/mcp-gateway.snulbug/fixtures/allowed-tool.json +0 -0
  116. {snulbug-0.2.3 → snulbug-0.2.5}/examples/bundles/mcp-gateway.snulbug/fixtures/blocked-tool.json +0 -0
  117. {snulbug-0.2.3 → snulbug-0.2.5}/examples/bundles/mcp-gateway.snulbug/fixtures/unauthenticated.json +0 -0
  118. {snulbug-0.2.3 → snulbug-0.2.5}/examples/bundles/mcp-gateway.snulbug/manifest.json +0 -0
  119. {snulbug-0.2.3 → snulbug-0.2.5}/examples/bundles/mcp-gateway.snulbug/policy.lua +0 -0
  120. {snulbug-0.2.3 → snulbug-0.2.5}/examples/bundles/mcp-gateway.snulbug/snapshots/empty.json +0 -0
  121. {snulbug-0.2.3 → snulbug-0.2.5}/examples/codespace_local_gateway/README.md +0 -0
  122. {snulbug-0.2.3 → snulbug-0.2.5}/examples/codespace_local_gateway/mcp-client.json +0 -0
  123. {snulbug-0.2.3 → snulbug-0.2.5}/examples/codespace_local_gateway/mock_mcp_server.py +0 -0
  124. {snulbug-0.2.3 → snulbug-0.2.5}/examples/codespace_local_gateway/policy.lua +0 -0
  125. {snulbug-0.2.3 → snulbug-0.2.5}/examples/codespace_local_gateway/snulbug.env-gateway.toml +0 -0
  126. {snulbug-0.2.3 → snulbug-0.2.5}/examples/codespace_local_gateway/snulbug.local-gateway.toml +0 -0
  127. {snulbug-0.2.3 → snulbug-0.2.5}/examples/customer_policy/README.md +0 -0
  128. {snulbug-0.2.3 → snulbug-0.2.5}/examples/customer_policy/app.py +0 -0
  129. {snulbug-0.2.3 → snulbug-0.2.5}/examples/customer_policy/policies/acme.lua +0 -0
  130. {snulbug-0.2.3 → snulbug-0.2.5}/examples/customer_policy/policies/default.lua +0 -0
  131. {snulbug-0.2.3 → snulbug-0.2.5}/examples/customer_policy/policies/globex.lua +0 -0
  132. {snulbug-0.2.3 → snulbug-0.2.5}/examples/customer_policy/requests/acme-invalid.json +0 -0
  133. {snulbug-0.2.3 → snulbug-0.2.5}/examples/customer_policy/requests/acme-valid.json +0 -0
  134. {snulbug-0.2.3 → snulbug-0.2.5}/examples/customer_policy/requests/globex-sandbox.json +0 -0
  135. {snulbug-0.2.3 → snulbug-0.2.5}/examples/customer_policy/requests/unknown-tenant.json +0 -0
  136. {snulbug-0.2.3 → snulbug-0.2.5}/examples/github-actions/snulbug-policy-gates.yml +0 -0
  137. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/.gitignore +0 -0
  138. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/Dockerfile.gateway +0 -0
  139. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/README.md +0 -0
  140. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/auth/keycloak/README.md +0 -0
  141. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/auth/keycloak/auth-init.json +0 -0
  142. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/auth/keycloak/client-token-request.json +0 -0
  143. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/auth/keycloak/commands.json +0 -0
  144. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/auth/keycloak/snulbug.auth.toml +0 -0
  145. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/docker-compose.yml +0 -0
  146. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/get-token.py +0 -0
  147. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/keycloak/snulbug-demo-realm.json +0 -0
  148. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/mock_mcp_server.py +0 -0
  149. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/policy.snulbug/policy.lua +0 -0
  150. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/requests/blocked-write-file.json +0 -0
  151. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/requests/read-file.json +0 -0
  152. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/requests/tools-list.json +0 -0
  153. {snulbug-0.2.3 → snulbug-0.2.5}/examples/keycloak_oauth_demo/snulbug.toml +0 -0
  154. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_container_facade/Dockerfile.gateway +0 -0
  155. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_container_facade/Dockerfile.remote-peer +0 -0
  156. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_container_facade/README.md +0 -0
  157. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_container_facade/docker-compose.yml +0 -0
  158. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_container_facade/hypertele-client.json +0 -0
  159. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_container_facade/hypertele-server.json +0 -0
  160. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_container_facade/mcp-client.json +0 -0
  161. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_container_facade/mock_mcp_server.js +0 -0
  162. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_container_facade/mock_mcp_server.py +0 -0
  163. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_container_facade/policy.lua +0 -0
  164. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_container_facade/snulbug.facade.toml +0 -0
  165. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_container_facade/snulbug.local.toml +0 -0
  166. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_gateway/README.md +0 -0
  167. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_gateway/app.py +0 -0
  168. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_proxy_demo/.gitignore +0 -0
  169. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_proxy_demo/README.md +0 -0
  170. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_proxy_demo/requests/blocked-tool.json +0 -0
  171. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_proxy_demo/requests/safe-tool.json +0 -0
  172. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_proxy_demo/requests/tools-list.json +0 -0
  173. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_proxy_demo/run_demo.py +0 -0
  174. {snulbug-0.2.3 → snulbug-0.2.5}/examples/mcp_proxy_demo/upstream.py +0 -0
  175. {snulbug-0.2.3 → snulbug-0.2.5}/examples/policy_promotion/README.md +0 -0
  176. {snulbug-0.2.3 → snulbug-0.2.5}/examples/policy_promotion/active.lua +0 -0
  177. {snulbug-0.2.3 → snulbug-0.2.5}/examples/policy_promotion/draft.lua +0 -0
  178. {snulbug-0.2.3 → snulbug-0.2.5}/examples/policy_promotion/fixtures/acme-invalid.json +0 -0
  179. {snulbug-0.2.3 → snulbug-0.2.5}/examples/policy_promotion/fixtures/acme-valid.json +0 -0
  180. {snulbug-0.2.3 → snulbug-0.2.5}/examples/provider_policy_templates/README.md +0 -0
  181. {snulbug-0.2.3 → snulbug-0.2.5}/examples/provider_policy_templates/cloudflare-access-group-gate.lua +0 -0
  182. {snulbug-0.2.3 → snulbug-0.2.5}/examples/provider_policy_templates/entra-app-role-gate.lua +0 -0
  183. {snulbug-0.2.3 → snulbug-0.2.5}/examples/provider_policy_templates/github-actions-workload-gate.lua +0 -0
  184. {snulbug-0.2.3 → snulbug-0.2.5}/examples/provider_policy_templates/keycloak-role-gate.lua +0 -0
  185. {snulbug-0.2.3 → snulbug-0.2.5}/examples/state_replay/README.md +0 -0
  186. {snulbug-0.2.3 → snulbug-0.2.5}/examples/state_replay/duplicate-state.json +0 -0
  187. {snulbug-0.2.3 → snulbug-0.2.5}/examples/state_replay/idempotency.lua +0 -0
  188. {snulbug-0.2.3 → snulbug-0.2.5}/examples/state_replay/request.json +0 -0
  189. {snulbug-0.2.3 → snulbug-0.2.5}/examples/webhook_normalization/README.md +0 -0
  190. {snulbug-0.2.3 → snulbug-0.2.5}/examples/webhook_normalization/app.py +0 -0
  191. {snulbug-0.2.3 → snulbug-0.2.5}/examples/webhook_normalization/policies/github.lua +0 -0
  192. {snulbug-0.2.3 → snulbug-0.2.5}/examples/webhook_normalization/policies/stripe.lua +0 -0
  193. {snulbug-0.2.3 → snulbug-0.2.5}/examples/webhook_normalization/policies/unknown.lua +0 -0
  194. {snulbug-0.2.3 → snulbug-0.2.5}/examples/webhook_normalization/requests/github-push.json +0 -0
  195. {snulbug-0.2.3 → snulbug-0.2.5}/examples/webhook_normalization/requests/stripe-invoice.json +0 -0
  196. {snulbug-0.2.3 → snulbug-0.2.5}/examples/webhook_normalization/requests/stripe-malformed.json +0 -0
  197. {snulbug-0.2.3 → snulbug-0.2.5}/examples/webhook_normalization/requests/unknown-vendor.json +0 -0
  198. {snulbug-0.2.3 → snulbug-0.2.5}/features/snulbug/install.sh +0 -0
  199. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/__main__.py +0 -0
  200. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/assets/prism.css +0 -0
  201. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/assets/prism.js +0 -0
  202. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/auth_providers.py +0 -0
  203. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/auth_recipes.py +0 -0
  204. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/auth-required.snulbug/README.md +0 -0
  205. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/auth-required.snulbug/fixtures/authenticated.json +0 -0
  206. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/auth-required.snulbug/fixtures/missing-token.json +0 -0
  207. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/auth-required.snulbug/manifest.json +0 -0
  208. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/auth-required.snulbug/policy.lua +0 -0
  209. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/aws-mcp.snulbug/README.md +0 -0
  210. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/aws-mcp.snulbug/contexts/inventory-lease.json +0 -0
  211. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/aws-mcp.snulbug/contexts/sandbox-change-lease.json +0 -0
  212. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/aws-mcp.snulbug/fixtures/data-read.json +0 -0
  213. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/aws-mcp.snulbug/fixtures/iam-delete-user.json +0 -0
  214. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/aws-mcp.snulbug/fixtures/inventory-read.json +0 -0
  215. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/aws-mcp.snulbug/fixtures/knowledge-search.json +0 -0
  216. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/aws-mcp.snulbug/fixtures/missing-token.json +0 -0
  217. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/aws-mcp.snulbug/fixtures/sandbox-mutation.json +0 -0
  218. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/aws-mcp.snulbug/manifest.json +0 -0
  219. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/aws-mcp.snulbug/policy.lua +0 -0
  220. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/aws-mcp.snulbug/snapshots/empty.json +0 -0
  221. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/local-dev-safe.snulbug/README.md +0 -0
  222. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/local-dev-safe.snulbug/fixtures/missing-token.json +0 -0
  223. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/local-dev-safe.snulbug/fixtures/safe-tool.json +0 -0
  224. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/local-dev-safe.snulbug/fixtures/unsafe-tool.json +0 -0
  225. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/local-dev-safe.snulbug/manifest.json +0 -0
  226. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/local-dev-safe.snulbug/policy.lua +0 -0
  227. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/local-dev-safe.snulbug/snapshots/empty.json +0 -0
  228. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/no-shell-tools.snulbug/README.md +0 -0
  229. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/no-shell-tools.snulbug/fixtures/missing-token.json +0 -0
  230. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/no-shell-tools.snulbug/fixtures/safe-tool.json +0 -0
  231. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/no-shell-tools.snulbug/fixtures/shell-tool.json +0 -0
  232. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/no-shell-tools.snulbug/manifest.json +0 -0
  233. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/no-shell-tools.snulbug/policy.lua +0 -0
  234. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/project-path-allowlist.snulbug/README.md +0 -0
  235. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/project-path-allowlist.snulbug/fixtures/absolute-path.json +0 -0
  236. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/project-path-allowlist.snulbug/fixtures/allowed-path.json +0 -0
  237. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/project-path-allowlist.snulbug/fixtures/traversal-path.json +0 -0
  238. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/project-path-allowlist.snulbug/fixtures/unsafe-tool.json +0 -0
  239. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/project-path-allowlist.snulbug/manifest.json +0 -0
  240. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/project-path-allowlist.snulbug/policy.lua +0 -0
  241. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/read-only-local-dev.snulbug/README.md +0 -0
  242. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/read-only-local-dev.snulbug/fixtures/missing-token.json +0 -0
  243. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/read-only-local-dev.snulbug/fixtures/safe-tool.json +0 -0
  244. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/read-only-local-dev.snulbug/fixtures/tools-list.json +0 -0
  245. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/read-only-local-dev.snulbug/fixtures/write-method.json +0 -0
  246. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/read-only-local-dev.snulbug/manifest.json +0 -0
  247. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/read-only-local-dev.snulbug/policy.lua +0 -0
  248. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/read-only-local-dev.snulbug/snapshots/empty.json +0 -0
  249. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/tool-allowlist.snulbug/README.md +0 -0
  250. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/tool-allowlist.snulbug/fixtures/safe-tool.json +0 -0
  251. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/tool-allowlist.snulbug/fixtures/tools-list.json +0 -0
  252. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/tool-allowlist.snulbug/fixtures/unsafe-tool.json +0 -0
  253. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/tool-allowlist.snulbug/manifest.json +0 -0
  254. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/tool-allowlist.snulbug/policy.lua +0 -0
  255. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/tunnel-safe.snulbug/README.md +0 -0
  256. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/tunnel-safe.snulbug/fixtures/batch-request.json +0 -0
  257. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/tunnel-safe.snulbug/fixtures/missing-token.json +0 -0
  258. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/tunnel-safe.snulbug/fixtures/safe-tool.json +0 -0
  259. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/tunnel-safe.snulbug/fixtures/unsafe-tool.json +0 -0
  260. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/tunnel-safe.snulbug/manifest.json +0 -0
  261. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/tunnel-safe.snulbug/policy.lua +0 -0
  262. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/tunnel-safe.snulbug/snapshots/empty.json +0 -0
  263. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/workspace-firewall.snulbug/README.md +0 -0
  264. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/workspace-firewall.snulbug/fixtures/allowed-read.json +0 -0
  265. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/workspace-firewall.snulbug/fixtures/missing-token.json +0 -0
  266. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/workspace-firewall.snulbug/fixtures/secret-path.json +0 -0
  267. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/workspace-firewall.snulbug/fixtures/traversal-path.json +0 -0
  268. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/workspace-firewall.snulbug/fixtures/unsafe-tool.json +0 -0
  269. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/workspace-firewall.snulbug/manifest.json +0 -0
  270. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/builtin_presets/mcp/workspace-firewall.snulbug/policy.lua +0 -0
  271. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/bundle.py +0 -0
  272. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/cli/__init__.py +0 -0
  273. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/cli/common.py +0 -0
  274. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/cli/evidence.py +0 -0
  275. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/cli/fabric.py +0 -0
  276. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/cli/policy.py +0 -0
  277. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/cli/schemas.py +0 -0
  278. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/cli_helpers.py +0 -0
  279. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/cloudflare_access.py +0 -0
  280. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/codespaces.py +0 -0
  281. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/confirm.py +0 -0
  282. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/control_events.py +0 -0
  283. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/controller.py +0 -0
  284. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/discovery.py +0 -0
  285. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/evidence_exporters.py +0 -0
  286. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/fabric_control.py +0 -0
  287. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/fabric_members.py +0 -0
  288. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/fabric_runtime.py +0 -0
  289. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/gateway_templates.py +0 -0
  290. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/guide.py +0 -0
  291. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/impact.py +0 -0
  292. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/json_schema.py +0 -0
  293. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/lab.py +0 -0
  294. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/learn.py +0 -0
  295. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/leases.py +0 -0
  296. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/manifests.py +0 -0
  297. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_auth.py +0 -0
  298. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_cache.py +0 -0
  299. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_capabilities.py +0 -0
  300. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_client_requests.py +0 -0
  301. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_completion.py +0 -0
  302. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_mrtr.py +0 -0
  303. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_parameter_headers.py +0 -0
  304. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_policy_bundles.py +0 -0
  305. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_progress.py +0 -0
  306. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_protocol.py +0 -0
  307. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_resources.py +0 -0
  308. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_schema_policy.py +0 -0
  309. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_schemas.py +0 -0
  310. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_spec_conformance.py +0 -0
  311. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_stdio.py +0 -0
  312. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_stream.py +0 -0
  313. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_subscriptions.py +0 -0
  314. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_tasks.py +0 -0
  315. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/mcp_tools.py +0 -0
  316. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/middleware.py +0 -0
  317. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/policy_activation.py +0 -0
  318. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/policy_backoff.py +0 -0
  319. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/policy_review.py +0 -0
  320. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/presets.py +0 -0
  321. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/promotion.py +0 -0
  322. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/py.typed +0 -0
  323. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/quickstart.py +0 -0
  324. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/recorder.py +0 -0
  325. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/response_policy.py +0 -0
  326. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/runtime.py +0 -0
  327. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/sarif.py +0 -0
  328. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/scaffolds.py +0 -0
  329. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/schema_policy.py +0 -0
  330. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/share_doctor.py +0 -0
  331. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/state.py +0 -0
  332. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/tool_catalog.py +0 -0
  333. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/tool_risk.py +0 -0
  334. {snulbug-0.2.3 → snulbug-0.2.5}/snulbug/upstream_transports.py +0 -0
  335. {snulbug-0.2.3 → snulbug-0.2.5}/tests/fixtures/json_schema_2020_12/LICENSE +0 -0
  336. {snulbug-0.2.3 → snulbug-0.2.5}/tests/fixtures/json_schema_2020_12/README.md +0 -0
  337. {snulbug-0.2.3 → snulbug-0.2.5}/tests/fixtures/json_schema_2020_12/dependentSchemas.json +0 -0
  338. {snulbug-0.2.3 → snulbug-0.2.5}/tests/fixtures/json_schema_2020_12/dynamicRef.json +0 -0
  339. {snulbug-0.2.3 → snulbug-0.2.5}/tests/fixtures/json_schema_2020_12/if-then-else.json +0 -0
  340. {snulbug-0.2.3 → snulbug-0.2.5}/tests/fixtures/json_schema_2020_12/prefixItems.json +0 -0
  341. {snulbug-0.2.3 → snulbug-0.2.5}/tests/fixtures/json_schema_2020_12/unevaluatedProperties.json +0 -0
  342. {snulbug-0.2.3 → snulbug-0.2.5}/tests/fixtures/pocket_id/README.md +0 -0
  343. {snulbug-0.2.3 → snulbug-0.2.5}/tests/fixtures/pocket_id/oauth.json +0 -0
  344. {snulbug-0.2.3 → snulbug-0.2.5}/tests/fixtures/stdio_subscriptions.py +0 -0
  345. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_auth_claim_patterns.py +0 -0
  346. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_auth_recipes.py +0 -0
  347. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_automation_invites.py +0 -0
  348. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_cli_helpers.py +0 -0
  349. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_cloudflare_access.py +0 -0
  350. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_codespaces.py +0 -0
  351. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_config.py +0 -0
  352. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_credentials.py +0 -0
  353. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_customer_policy_demo.py +0 -0
  354. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_devcontainer_feature.py +0 -0
  355. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_discovery.py +0 -0
  356. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_events.py +0 -0
  357. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_evidence_exporters.py +0 -0
  358. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_fabric_controller.py +0 -0
  359. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_json_schema.py +0 -0
  360. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_keycloak_oauth_demo.py +0 -0
  361. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_leases.py +0 -0
  362. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_manifests.py +0 -0
  363. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_cache.py +0 -0
  364. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_gateway_demo.py +0 -0
  365. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_guide.py +0 -0
  366. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_helpers.py +0 -0
  367. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_impact.py +0 -0
  368. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_inspection.py +0 -0
  369. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_lab.py +0 -0
  370. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_learn.py +0 -0
  371. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_mrtr.py +0 -0
  372. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_parameter_headers.py +0 -0
  373. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_policy_bundles.py +0 -0
  374. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_presets.py +0 -0
  375. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_progress.py +0 -0
  376. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_protocol.py +0 -0
  377. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_proxy_demo.py +0 -0
  378. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_quickstart.py +0 -0
  379. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_recorder.py +0 -0
  380. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_resources.py +0 -0
  381. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_schema_policy.py +0 -0
  382. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_schemas.py +0 -0
  383. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_share.py +0 -0
  384. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_spec_conformance.py +0 -0
  385. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_stdio.py +0 -0
  386. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_stream.py +0 -0
  387. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_subscriptions.py +0 -0
  388. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_mcp_tools.py +0 -0
  389. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_middleware.py +0 -0
  390. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_pocket_id_auth.py +0 -0
  391. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_policy_bundle.py +0 -0
  392. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_policy_promotion.py +0 -0
  393. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_policy_state.py +0 -0
  394. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_provider_policy_templates.py +0 -0
  395. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_redaction_audit.py +0 -0
  396. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_response_policy.py +0 -0
  397. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_sandbox_abuse.py +0 -0
  398. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_scaffolds.py +0 -0
  399. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_state_snapshots.py +0 -0
  400. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_tool_catalog.py +0 -0
  401. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_tool_risk.py +0 -0
  402. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_tunnel_audit.py +0 -0
  403. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_tunnel_doctor.py +0 -0
  404. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_upstream_transports.py +0 -0
  405. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_webhook_normalization_demo.py +0 -0
  406. {snulbug-0.2.3 → snulbug-0.2.5}/tests/test_webhooks.py +0 -0
@@ -6,6 +6,38 @@ The format is based on Keep a Changelog, and this project follows semantic versi
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.2.5] - 2026-10-23
10
+
11
+ Public-origin preservation for shared MCP and HTTP gateways.
12
+ Read the [0.2.5 upgrade notes](docs/upgrading-to-0.2.5.md) before deploying an
13
+ existing ngrok share or a share with required signed HTTP mount contracts.
14
+
15
+ - Forward canonical public host, scheme, and port from the configured share URL across internal hops. HTTP mounts can opt into `preserve_host = true`; backend Host remains the default and upstream TLS verification is unchanged.
16
+ - Generate ngrok policies that capture and validate the original public authority/scheme before forwarding and replace caller-supplied origin/audit headers. Reject invalid capture pairs at the gateway and unsafe public URLs at config load; keep observed ingress headers for audit provenance, not forwarding authority.
17
+ - Include effective origin and Host mode in mount health checks, doctor, status, reports, the web console, readiness reviews, and signed mount contracts. Existing ngrok policies need manual review and reattachment; no cloud changes or share regeneration are applied automatically.
18
+ - Add regression coverage for spoofed/duplicate headers, redirects, prefixes, ports, IPv6, MCP forwarding, health/cache invalidation, and contract drift, including installed-wheel packaging checks.
19
+
20
+ No change to the default MCP protocol or preview limitations. Application auth,
21
+ proxy trust, base paths, and CSRF defenses remain the mounted app's responsibility;
22
+ this release does not rewrite HTML, redirect locations, or cookie paths.
23
+
24
+ ## [0.2.4] - 2026-10-19
25
+
26
+ Operational hardening for always-on shares and shared HTTP/MCP gateways.
27
+ See the [0.2.4 upgrade notes](docs/upgrading-to-0.2.4.md) before restarting an
28
+ existing service, especially if a tunnel is already running separately.
29
+
30
+ - Moved ngrok/SSH tunnel management into the share runtime: `share run` supervises tunnels with or without the web console, bounded crash-restart backoff, explicit stop/start controls, and graceful gateway/tunnel shutdown. Added `--no-tunnel` and always-on deployment recipes.
31
+ - Added read-only ngrok policy drift checks to share doctor, readiness, reports, and the console: compare current config, generated policy, and deployed Cloud Endpoint policy without exposing deployed secrets or applying cloud changes. Includes OAuth-aware edge baselines and stale-result invalidation.
32
+ - Added evidence-based request failure explanations to existing audit logs, live decisions, and session reports: distinguish admission gates, upstream credentials/connectivity, HTTP/MCP errors, partial catalogs, and response checks while preserving Lua decisions and unknown execution outcomes. Removed raw forwarding exceptions from client errors and facade health evidence; no tool-call retries.
33
+ - Hardened upstream credential operations: bounded regular-file/header reads, secret-free broker failures, and live share doctor availability/hygiene checks surfaced in readiness, status, reports, and the Auth drawer. Documented atomic file rotation and environment restart semantics; passive refreshes reuse saved diagnostics without resolving secrets.
34
+ - Added explicit HTTP mount contracts for authentication ownership, methods, credential-header forwarding, inactivity timeouts, and optional backend health checks. Reused share doctor/readiness, reports, the provider drawer, and signed share-contract bindings; public mounts strip Authorization/cookies by default, and MCP auth/leases remain separate from application access.
35
+ - Added isolated installed-wheel operations QA for share creation, local MCP/HTTP traffic, credential rotation, persisted leases, and supervised shutdown. Fixed installed-package share creation so container recipe snapshots no longer require a source checkout.
36
+
37
+ No change to the default MCP protocol version or experimental profile limitations.
38
+ This release does not add HTTP application OIDC, overload protection, automatic
39
+ tool-call retries, or a published container image.
40
+
9
41
  ## [0.2.3] - 2026-10-18
10
42
 
11
43
  Add Pocket ID setup through the existing OAuth provider and lease machinery.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: snulbug
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: A local-dev MCP policy proxy with Lua policies, replay logs, and learned least-privilege bundles
5
5
  Project-URL: Homepage, https://github.com/nahuaque/snulbug
6
6
  Project-URL: Repository, https://github.com/nahuaque/snulbug
@@ -174,7 +174,12 @@ snulbug mcp share run .snulbug/shares/share-...
174
174
  Inside a generated share directory, `snulbug mcp share run` is enough;
175
175
  it reads `.snulbug/share/session.json`, reconciles the active config,
176
176
  policy, lease, and log paths, and starts the local share console before
177
- starting the gateway. Pass `--no-console` when you only want the proxy.
177
+ starting the gateway. Pass `--no-console` to disable the local web UI.
178
+
179
+ For ngrok and SSH shares, `share run` also starts and supervises the generated
180
+ tunnel independently of the console. Use `--no-tunnel` for an externally managed
181
+ agent. See [always-on deployment](docs/always-on-runtime.md) for crash recovery,
182
+ graceful shutdown, and systemd, launchd, and container recipes.
178
183
 
179
184
  3. Check what is happening:
180
185
 
@@ -131,7 +131,12 @@ snulbug mcp share run .snulbug/shares/share-...
131
131
  Inside a generated share directory, `snulbug mcp share run` is enough;
132
132
  it reads `.snulbug/share/session.json`, reconciles the active config,
133
133
  policy, lease, and log paths, and starts the local share console before
134
- starting the gateway. Pass `--no-console` when you only want the proxy.
134
+ starting the gateway. Pass `--no-console` to disable the local web UI.
135
+
136
+ For ngrok and SSH shares, `share run` also starts and supervises the generated
137
+ tunnel independently of the console. Use `--no-tunnel` for an externally managed
138
+ agent. See [always-on deployment](docs/always-on-runtime.md) for crash recovery,
139
+ graceful shutdown, and systemd, launchd, and container recipes.
135
140
 
136
141
  3. Check what is happening:
137
142
 
@@ -29,6 +29,7 @@ share create -> share run -> share status -> share requests approve -> share pol
29
29
  ## Sharing, Tunnels, And Remote Dev
30
30
 
31
31
  - [End-to-end ngrok MCP gateway](ngrok-end-to-end.md)
32
+ - [Always-on share runtime and deployment](always-on-runtime.md)
32
33
  - [Tunnel provider plugins](tunnel-providers.md)
33
34
  - [Credential broker plugins](credential-brokers.md)
34
35
  - [Discovery provider plugins](discovery-providers.md)
@@ -0,0 +1,385 @@
1
+ # Always-on share runtime
2
+
3
+ Use the existing `snulbug mcp share run DIRECTORY` command for a long-lived
4
+ gateway. It runs in the foreground; systemd, launchd, or a container restart
5
+ policy owns whole-gateway restarts. There is no separate daemon or new CLI.
6
+ Create and review the share first using [MCP share sessions](mcp-share.md).
7
+
8
+ ## Tunnel ownership
9
+
10
+ For ngrok and SSH shares, `share run` automatically starts and supervises the
11
+ managed tunnel independently of the web console. It uses the same generated
12
+ provider config and commands; existing shares need no new generated layout.
13
+ ngrok still uses `tunnel/ngrok-agent.yml` plus the separately attached Cloud
14
+ Endpoint Traffic Policy. SSH still uses the generated reverse-forward target.
15
+ See the [ngrok walkthrough](ngrok-end-to-end.md) for cloud setup.
16
+
17
+ ```bash
18
+ /opt/snulbug/venv/bin/snulbug mcp share run /var/lib/snulbug/share --no-console
19
+ ```
20
+
21
+ - `--no-console` disables only the UI, not tunnel startup or supervision.
22
+ - `--no-tunnel` disables automatic tunnel startup. The console can still
23
+ explicitly start a managed tunnel; combine both flags for a headless gateway
24
+ whose tunnel is externally managed.
25
+ - The existing console **Start tunnel** and **Stop tunnel** controls operate on
26
+ the same managed process. An intentional stop cancels pending restarts and
27
+ keeps the tunnel stopped until an explicit start.
28
+ - `generic`, `cloudflare`, `tailscale`, `pinggy`, `holepunch`, and other supported
29
+ providers remain externally managed. Their generated setup commands still
30
+ apply; snulbug does not start, restart, or stop those external processes.
31
+
32
+ Do not start a second provider process for an automatically managed endpoint.
33
+ The runtime supervises processes, not public reachability: a running ngrok or
34
+ SSH process does not prove that forwarding, DNS, TLS, authentication, or cloud
35
+ policy is correct. Use existing share doctor and health checks, including the
36
+ public URL checks, rather than treating a PID as readiness.
37
+ For ngrok, [doctor's policy drift inspection](ngrok-end-to-end.md#check-for-configuration-drift)
38
+ compares local configuration with the deployed Cloud Endpoint through optional,
39
+ read-only API access. Supply its API credential to the service account to enable
40
+ the same inspection from the console; it is separate from the agent authtoken.
41
+
42
+ ## Runtime settings
43
+
44
+ The following optional table in the share's existing `snulbug.toml` shows all
45
+ defaults. Merge it into the file; do not replace the generated config or add a
46
+ second `[mcp.runtime]` table. Durations are in seconds.
47
+
48
+ ```toml
49
+ [mcp.runtime]
50
+ tunnel_autostart = true
51
+ restart_initial_seconds = 1.0
52
+ restart_max_seconds = 30.0
53
+ restart_max_attempts = 5
54
+ restart_reset_seconds = 60.0
55
+ tunnel_shutdown_timeout = 5.0
56
+ graceful_shutdown_timeout = 30.0
57
+ ```
58
+
59
+ `tunnel_autostart = false` disables automatic startup in the same way as
60
+ `--no-tunnel`; an explicit console start remains available. `--no-tunnel`
61
+ overrides `tunnel_autostart = true` for that invocation.
62
+
63
+ An unexpected managed tunnel exit triggers bounded exponential crash restart:
64
+ the delay begins at `restart_initial_seconds`, doubles on each retry, and is
65
+ capped at `restart_max_seconds`. `restart_max_attempts` limits restart attempts,
66
+ not the initial launch. With defaults, a repeatedly crashing process gets at
67
+ most five retries, after delays of 1, 2, 4, 8, and 16 seconds.
68
+
69
+ After exhaustion, automatic retries stop. Fix the cause, then explicitly start
70
+ the tunnel through the console or restart the whole service. A healthy process
71
+ running continuously for `restart_reset_seconds` resets the retry budget; this
72
+ is process stability, not a successful public endpoint probe. An intentional
73
+ console stop is not a crash and never consumes a retry or triggers a restart.
74
+
75
+ Only the managed tunnel process is restarted here. The OS supervisor handles a
76
+ gateway process failure; it is not a public health watchdog and should not be
77
+ used to bypass exhausted tunnel retries. No MCP tool calls are retried or
78
+ replayed by this runtime. A broken connection can leave a tool's outcome
79
+ unknown; reconnecting does not make a repeated side effect safe.
80
+ The existing decision feed and session reports include
81
+ [request failure explanations](mcp-evidence.md#request-failure-explanations),
82
+ separating admission, upstream credentials/transport, and response-policy failures
83
+ without performing retries or adding background probes.
84
+
85
+ ## Shutdown
86
+
87
+ On SIGTERM or SIGINT, Uvicorn stops accepting new work and drains in-flight
88
+ requests and streams. `graceful_shutdown_timeout` bounds that drain; at the
89
+ deadline, remaining work is cancelled. Only after the gateway drain/cancellation
90
+ does the runtime stop its managed tunnel. `tunnel_shutdown_timeout` bounds the
91
+ tunnel's termination wait before forced cleanup. Tunnel restart is suppressed
92
+ during shutdown. Externally managed provider processes remain their owner's
93
+ responsibility.
94
+
95
+ Give the outer supervisor more than the sum of these two timeouts plus cleanup
96
+ headroom. The recipes below use 45 seconds for the default 30-second drain and
97
+ 5-second tunnel wait. Increase the supervisor timeout if you increase either
98
+ runtime setting. SIGKILL, a host crash, or a shorter supervisor deadline cannot
99
+ provide graceful draining.
100
+
101
+ ## Deployment prerequisites
102
+
103
+ - Install a build containing this runtime into a fixed virtual environment.
104
+ Use an absolute `snulbug` executable path, not an interactive shell's alias,
105
+ `uv run`, or a login-shell activation script. Keep the installation and
106
+ launcher files administrator-owned and non-service-writable.
107
+ - Use a dedicated unprivileged account. Create the share once at its persistent
108
+ absolute path under that account; do not run `share create --force` at boot.
109
+ Keep the same path inside a container, or reconcile recorded paths before
110
+ moving an existing share.
111
+ - Persist the whole share directory: config, policy, `leases.json`, invite
112
+ secrets, canonical session state, tunnel config, and evidence. Give the service
113
+ account write access, restrict the directory to mode `0700`, and protect
114
+ secret-bearing files with mode `0600`. Backups are secret-bearing too.
115
+ - Install `ngrok` or `ssh` on the service's explicit `PATH`. Complete ngrok's
116
+ generated authtoken setup; a placeholder config will not start. For SSH,
117
+ provision non-interactive credentials and verified `known_hosts` for that
118
+ account. Do not disable host-key verification or depend on a desktop agent.
119
+ - Arrange the upstream service separately. A tunnel restart does not restart
120
+ the upstream, renew expired leases, extend invite authorization, or repair
121
+ cloud policy. Use [renewable automation invites](mcp-share.md#renewable-automation-invites)
122
+ where a bot needs approved renewal rather than an unbounded lease.
123
+ - Keep the share console on loopback or disable it with `--no-console`, as these
124
+ recipes do. Never add a public tunnel or shared HTTP route to its admin port
125
+ or ngrok's inspection port. For remote administration, deliberately enable
126
+ the loopback console and use authenticated SSH local forwarding.
127
+
128
+ Do not put tokens in unit files, plists, command arguments, images, or source
129
+ control. Where the generated configuration uses environment references, load
130
+ them from a protected env file. For example, `share.env` can contain:
131
+
132
+ ```dotenv
133
+ SNULBUG_SHARE_TOKEN=replace-with-the-existing-share-token
134
+ ```
135
+
136
+ This is an existing credential, not a request to mint a different token. Keep
137
+ secrets already stored in generated policy/provider files protected as well;
138
+ setting an env variable does not replace a literal ngrok authtoken placeholder.
139
+ Environment variables are still visible to sufficiently privileged local
140
+ processes. Treat service logs and container inspection access as sensitive.
141
+
142
+ For upstream service tokens, prefer the existing
143
+ [credential broker references](credential-brokers.md#always-on-upstream-credentials).
144
+ File sources support atomic replacement without a restart; environment-backed
145
+ sources require a service restart to pick up supervisor environment changes.
146
+ Live share doctor checks availability and hygiene, and status/report plus the
147
+ console's Auth drawer reuse its secret-free diagnostics. Run checks in the
148
+ service environment: local availability is not proof of upstream acceptance.
149
+
150
+ If the same tunnel exposes a web app or API, declare its
151
+ [HTTP mount contract](mcp-proxy.md#mount-contracts-and-readiness): explicit
152
+ auth ownership, methods, credential-header forwarding, and an optional safe
153
+ backend health endpoint. MCP lease revocation does not stop application access.
154
+ Use the existing share doctor and provider drawer to review both kinds of
155
+ exposure; application OIDC/session auth remains the app's responsibility.
156
+
157
+ ## Linux: systemd
158
+
159
+ This example assumes an existing `snulbug` user/group, an installation at
160
+ `/opt/snulbug/venv`, and a share created at `/var/lib/snulbug/share`.
161
+ Store the env file at `/etc/snulbug/share.env`, owned by root with mode `0600`;
162
+ systemd reads it before dropping privileges. Install ngrok at
163
+ `/usr/local/bin/ngrok` or SSH at `/usr/bin/ssh`.
164
+
165
+ Install `/etc/systemd/system/snulbug-share.service`:
166
+
167
+ ```ini
168
+ [Unit]
169
+ Description=snulbug MCP share gateway
170
+ Wants=network-online.target
171
+ After=network-online.target
172
+ StartLimitIntervalSec=300
173
+ StartLimitBurst=5
174
+
175
+ [Service]
176
+ Type=exec
177
+ User=snulbug
178
+ Group=snulbug
179
+ WorkingDirectory=/var/lib/snulbug/share
180
+ Environment=HOME=/var/lib/snulbug
181
+ Environment=PATH=/usr/local/bin:/usr/bin:/bin
182
+ EnvironmentFile=/etc/snulbug/share.env
183
+ ExecStart=/opt/snulbug/venv/bin/snulbug mcp share run /var/lib/snulbug/share --no-console
184
+ Restart=on-failure
185
+ RestartSec=5s
186
+ KillSignal=SIGTERM
187
+ KillMode=mixed
188
+ TimeoutStopSec=45s
189
+ UMask=0077
190
+ NoNewPrivileges=true
191
+
192
+ [Install]
193
+ WantedBy=multi-user.target
194
+ ```
195
+
196
+ `KillMode=mixed` sends the initial SIGTERM to the gateway, allowing it to drain
197
+ before stopping its tunnel; leftover processes are still forcibly cleaned up.
198
+ The default control-group termination would signal the tunnel at the same time
199
+ as the gateway. See [systemd.kill](https://www.freedesktop.org/software/systemd/man/latest/systemd.kill.html).
200
+ `Restart=on-failure` covers gateway failure, not tunnel retry exhaustion or an
201
+ intentional service stop. See [systemd.service](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html).
202
+
203
+ ```bash
204
+ sudo systemctl daemon-reload
205
+ sudo systemctl enable --now snulbug-share.service
206
+ sudo journalctl -u snulbug-share.service -f
207
+ ```
208
+
209
+ Stop with `sudo systemctl stop snulbug-share.service`. After repairing a gateway
210
+ start failure that hit systemd's start limit, use `sudo systemctl reset-failed
211
+ snulbug-share.service`, then `sudo systemctl start snulbug-share.service`.
212
+ Use `sudo systemctl restart snulbug-share.service` for an explicit whole-service
213
+ restart, including recovery after exhausted tunnel retries.
214
+
215
+ ## macOS: launchd
216
+
217
+ For service operation before login, use a LaunchDaemon, not a per-login
218
+ LaunchAgent. This example assumes an existing dedicated `snulbug` account/group
219
+ with home `/Users/snulbug`, an installation at `/opt/snulbug/venv`, and a share
220
+ at `/Users/snulbug/share`. Create `/Users/snulbug/Library/Logs/snulbug` for its
221
+ logs, owned by that account with mode `0700`. The machine must remain awake and
222
+ its share volume must be available; launchd cannot provide uptime during sleep.
223
+ See Apple's [launchd job guide](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html).
224
+
225
+ launchd has no systemd-style `EnvironmentFile`. Store
226
+ `/Users/snulbug/.config/snulbug/share.env` as mode `0600`, owned by `snulbug`,
227
+ inside mode `0700` directories. Use simple shell-compatible `KEY=value` entries
228
+ with quoted values where needed. The following root-owned, non-service-writable
229
+ launcher at `/usr/local/libexec/snulbug-share-run` only loads that trusted file
230
+ and replaces itself with the existing command; it is not a new daemon:
231
+
232
+ ```sh
233
+ #!/bin/sh
234
+ set -eu
235
+ umask 077
236
+ set -a
237
+ . /Users/snulbug/.config/snulbug/share.env
238
+ set +a
239
+ export HOME=/Users/snulbug
240
+ export PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin
241
+ exec /opt/snulbug/venv/bin/snulbug mcp share run /Users/snulbug/share --no-console
242
+ ```
243
+
244
+ Install `/Library/LaunchDaemons/com.snulbug.share.plist`, owned by `root:wheel`
245
+ with mode `0644`; keep secrets out of it:
246
+
247
+ ```xml
248
+ <?xml version="1.0" encoding="UTF-8"?>
249
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
250
+ <plist version="1.0">
251
+ <dict>
252
+ <key>Label</key>
253
+ <string>com.snulbug.share</string>
254
+ <key>UserName</key>
255
+ <string>snulbug</string>
256
+ <key>GroupName</key>
257
+ <string>snulbug</string>
258
+ <key>ProgramArguments</key>
259
+ <array>
260
+ <string>/usr/local/libexec/snulbug-share-run</string>
261
+ </array>
262
+ <key>WorkingDirectory</key>
263
+ <string>/Users/snulbug/share</string>
264
+ <key>RunAtLoad</key>
265
+ <true/>
266
+ <key>KeepAlive</key>
267
+ <true/>
268
+ <key>ThrottleInterval</key>
269
+ <integer>10</integer>
270
+ <key>ExitTimeOut</key>
271
+ <integer>45</integer>
272
+ <key>StandardOutPath</key>
273
+ <string>/Users/snulbug/Library/Logs/snulbug/share.out.log</string>
274
+ <key>StandardErrorPath</key>
275
+ <string>/Users/snulbug/Library/Logs/snulbug/share.err.log</string>
276
+ </dict>
277
+ </plist>
278
+ ```
279
+
280
+ `KeepAlive` restarts a gateway that exits; `ThrottleInterval` limits rapid
281
+ launches, and `ExitTimeOut` leaves time for graceful shutdown. These are outer
282
+ process controls, not public health checks or the managed tunnel retry budget.
283
+ See Apple's [launchd.plist reference](https://github.com/apple-oss-distributions/launchd/blob/main/man/launchd.plist.5).
284
+
285
+ ```bash
286
+ sudo chmod 0755 /usr/local/libexec/snulbug-share-run
287
+ plutil -lint /Library/LaunchDaemons/com.snulbug.share.plist
288
+ sudo launchctl bootstrap system /Library/LaunchDaemons/com.snulbug.share.plist
289
+ sudo launchctl print system/com.snulbug.share
290
+ ```
291
+
292
+ For an orderly stop, use `sudo launchctl bootout system/com.snulbug.share`.
293
+ For an explicit restart, boot out, then bootstrap the plist again. Avoid
294
+ `launchctl kickstart -k` for a draining restart: it kills the existing process.
295
+ A direct signal while `KeepAlive` is enabled may cause a new gateway launch;
296
+ boot out to keep the service stopped. Arrange rotation for the log files.
297
+
298
+ ## Container
299
+
300
+ Use a locally built image containing this runtime and the chosen provider
301
+ binary; no new published image is required. The generated
302
+ `containers/Dockerfile.gateway` is a starting point and installs from its
303
+ `snulbug-src/` snapshot of the checkout or installed package, not a published
304
+ snulbug image. Ensure that snapshot
305
+ contains the runtime. It does not install ngrok or SSH: add a verified,
306
+ architecture-matched Linux ngrok binary or install `openssh-client` in your
307
+ local build before using managed startup. Never copy provider credentials into
308
+ the image. In this example the resulting local tag is
309
+ `snulbug-share-runtime:local`, and snulbug is installed at
310
+ `/usr/local/bin/snulbug` with provider binaries on its `PATH`.
311
+
312
+ The following command is for a Linux Docker host. Create the share at
313
+ `/srv/snulbug/share` and keep its generated gateway bound to `127.0.0.1`.
314
+ Host networking lets the gateway and its managed tunnel use the existing
315
+ loopback origin and reach an upstream on the host's loopback interface without
316
+ changing generated tunnel config. Do not use this unchanged for Docker Desktop
317
+ or bridge networking; those require deliberate upstream/network configuration.
318
+ The example assumes the host service account has UID/GID `10001:10001` and owns
319
+ the persistent share; substitute your account's numeric IDs.
320
+
321
+ Protect `/etc/snulbug/share.env` on the host as root-owned with mode `0600`.
322
+ Use Docker's `KEY=value` env-file syntax, not shell `export` or shell expansion.
323
+ The privileged Docker invocation below can read that file; the container itself
324
+ runs as the unprivileged UID. Use a persistent read-write bind volume at the
325
+ same absolute path:
326
+
327
+ ```bash
328
+ sudo docker run --detach --name snulbug-share \
329
+ --pull=never \
330
+ --init \
331
+ --restart=unless-stopped \
332
+ --stop-signal=SIGTERM \
333
+ --stop-timeout=45 \
334
+ --network=host \
335
+ --user=10001:10001 \
336
+ --env-file=/etc/snulbug/share.env \
337
+ --env HOME=/srv/snulbug \
338
+ --env PATH=/usr/local/bin:/usr/bin:/bin \
339
+ --mount type=bind,src=/srv/snulbug/share,dst=/srv/snulbug/share \
340
+ --workdir=/srv/snulbug/share \
341
+ --entrypoint=/usr/local/bin/snulbug \
342
+ snulbug-share-runtime:local \
343
+ mcp share run /srv/snulbug/share --no-console
344
+ ```
345
+
346
+ For SSH, also mount the service account's SSH config, private key, and verified
347
+ `known_hosts` read-only at `/srv/snulbug/.ssh`, with permissions readable only by
348
+ that UID. Do not mount a general-purpose personal SSH directory. For ngrok,
349
+ complete the authtoken configuration in the protected share volume.
350
+
351
+ `--init` forwards signals and reaps children; `--stop-timeout` allows draining
352
+ before forced termination. No console or inspection port is published. With
353
+ host networking, loopback binds are essential: it does not isolate listening
354
+ ports from the host. See [Docker run reference](https://docs.docker.com/reference/cli/docker/container/run/).
355
+
356
+ Use `sudo docker logs -f snulbug-share` for gateway output and
357
+ `sudo docker stop snulbug-share` for an orderly stop. Use
358
+ `sudo docker restart --timeout 45 snulbug-share` for an explicit restart. The
359
+ restart policy handles container exit, not a live gateway with exhausted tunnel
360
+ retries or failing public checks.
361
+
362
+ ## Verify and operate
363
+
364
+ After starting or restarting, use the existing checks from an account and
365
+ network that can reach the intended client URL:
366
+
367
+ ```bash
368
+ /opt/snulbug/venv/bin/snulbug mcp share status /var/lib/snulbug/share
369
+ /opt/snulbug/venv/bin/snulbug mcp share doctor /var/lib/snulbug/share \
370
+ --url https://YOUR-PUBLIC-HOST/mcp
371
+ ```
372
+
373
+ Use the corresponding paths on macOS or inside the container. An SSH share's
374
+ default client URL is loopback on the remote SSH host, not the gateway host;
375
+ verify from that remote side as described in
376
+ [plain SSH reverse tunnels](mcp-share.md#plain-ssh-reverse-tunnel). For ngrok,
377
+ verify the generated Traffic Policy is attached to the public Cloud Endpoint,
378
+ not just that the private internal Agent Endpoint is running.
379
+
380
+ If a tunnel repeatedly exits, inspect its existing console diagnostics and
381
+ provider log, fix credentials/configuration or duplicate agents, and explicitly
382
+ start it again. With `--no-console`, restart the OS service instead. Do not
383
+ automatically replay failed MCP tool calls as a recovery mechanism. Finish a
384
+ bounded sharing task with the existing [closeout flow](mcp-share.md#close-out);
385
+ stopping a process alone is not lease or invite revocation.
@@ -0,0 +1,172 @@
1
+ # Credential broker plugins
2
+
3
+ snulbug uses credential brokers to inject upstream credentials without
4
+ forwarding caller tokens. Built-ins support:
5
+
6
+ - `type = "env"`: read the secret from an environment variable
7
+ - `type = "file"`: read the secret from a local file
8
+
9
+ External brokers can add other sources such as a local keychain, a vault, a
10
+ container metadata service, or a short-lived token minting service.
11
+
12
+ ## Always-on upstream credentials
13
+
14
+ Keep caller authentication and upstream credentials separate. A client's OAuth
15
+ token or share bearer authenticates to snulbug; an upstream credential reference
16
+ authenticates snulbug to that upstream. Missing upstream credentials fail closed,
17
+ without retrying a tool call, forwarding the caller's token as a fallback, or
18
+ reusing a previously resolved value.
19
+
20
+ Use the existing registry in the share's `snulbug.toml`. File paths are relative
21
+ to that config file, not the launcher's working directory:
22
+
23
+ ```toml
24
+ [mcp.fabric.credentials.service]
25
+ type = "file"
26
+ path = "/run/secrets/mcp-service-token"
27
+ scheme = "bearer"
28
+ header = "Authorization"
29
+
30
+ [mcp.proxy]
31
+ upstream = "https://mcp.example.com/mcp"
32
+ upstream_credential = "service"
33
+ ```
34
+
35
+ Merge the setting into the existing `[mcp.proxy]` table rather than adding a
36
+ duplicate. For a facade, attach `auth = "service"` to the relevant
37
+ `[[mcp.proxy.upstreams]]` entry instead. Do not store secret values in TOML,
38
+ command arguments, images, or the policy source.
39
+
40
+ ### Check and inspect
41
+
42
+ ```bash
43
+ snulbug mcp share doctor /var/lib/snulbug/share
44
+ snulbug mcp share status /var/lib/snulbug/share
45
+ snulbug mcp share report /var/lib/snulbug/share
46
+ ```
47
+
48
+ Doctor's `credentials` check resolves each used source once and adds
49
+ `upstream_credentials` to its JSON result. It reports missing/unreadable,
50
+ empty, malformed, or oversized values as failures. It warns about POSIX
51
+ group/other file permissions, credentials sent over non-loopback plain HTTP,
52
+ client Authorization passthrough, and HTTP credential references on stdio
53
+ transports. Hygiene warnings require review, not an automatic chmod or config
54
+ rewrite. Intentional private Docker networks may explain an HTTP warning;
55
+ snulbug cannot prove that such a network is private.
56
+
57
+ The console's **Auth** section has an **Upstream credentials** drawer with
58
+ source references, attached upstreams, last-check state, rotation guidance,
59
+ and the existing **Run doctor** action. Readiness fails for a recorded source
60
+ failure and requests review for unchecked, changed, or warning states. No
61
+ additional credential-management endpoint or secret-reveal control is added.
62
+
63
+ Offline doctor skips source resolution. Status, reports, and normal console
64
+ refreshes reuse saved diagnostics, never invoke brokers, and never record
65
+ secret values or hashes of them. A reference, route, or file stat change
66
+ invalidates saved availability and requires another doctor run. The input
67
+ digest covers references and file metadata only; it is not a secret fingerprint.
68
+ Doctor results are historical diagnostics, not an authorization decision or a
69
+ guarantee about the next request. The request path always resolves again.
70
+
71
+ Availability means **resolved in the checking process**, not accepted by the
72
+ upstream. Run doctor with the same service account and environment as the
73
+ gateway, or use the running console's action. A CLI process may have different
74
+ environment values or permissions. Opaque token expiry, revocation, audience,
75
+ and upstream privileges cannot be inferred from a successful local read; use
76
+ the existing health checks and request failure explanations to see upstream
77
+ 401/403 responses.
78
+
79
+ ### Rotation
80
+
81
+ - **File:** stage the new value in a protected file on the same filesystem,
82
+ set its service ownership and mode (`0600` or a deliberate read-only mode),
83
+ then atomically rename it over the referenced path. New requests read the
84
+ replacement without a gateway restart. Do not truncate/rewrite the live file.
85
+ Symlinks to regular files are supported, including projected container
86
+ secrets. A missing or invalid replacement fails new requests immediately.
87
+ - **Environment:** update the supervisor's protected environment file and
88
+ restart the gateway. Exporting a variable in another shell cannot modify a
89
+ running process's environment. There is no last-known-good cache to flush.
90
+ - **Plugin:** the broker owns its source-specific refresh and rotation policy.
91
+ Keep network resolution bounded; plugins run trusted Python code and snulbug
92
+ does not supply a universal vault timeout or refresh scheduler.
93
+
94
+ File sources must resolve to regular UTF-8 files, at most 16 KiB, with an
95
+ optional trailing newline. Nonregular sources (including FIFOs/devices) are
96
+ rejected before reading. Every broker's result must be a nonempty, bounded
97
+ single HTTP header value without control characters. Resolver exception text
98
+ is replaced with fixed diagnostics, including for plugin errors.
99
+ POSIX mode checks inspect the target file, not directory permissions, ACLs,
100
+ ownership, or Windows access controls; restrict those separately as part of
101
+ service provisioning.
102
+
103
+ Rotation applies at request/probe boundaries. An established SSE stream or
104
+ managed upstream session is not reauthenticated mid-flight. Arrange upstream
105
+ old/new-token overlap when needed; snulbug does not retry rejected calls or
106
+ guess whether side effects occurred. After replacement, run doctor again and
107
+ verify a safe client operation before revoking the old token upstream.
108
+
109
+ This check covers credentials attached to MCP HTTP/Holepunch upstreams. It does
110
+ not rotate client share bearers, invite renewal secrets, OAuth signing keys,
111
+ ngrok agent authtokens, SSH keys, or mounted applications' sessions. Keep using
112
+ their existing controls. HTTP mounts remain outside the MCP credential broker.
113
+
114
+ ## Custom brokers
115
+
116
+ ```python
117
+ from collections.abc import Mapping
118
+ from pathlib import Path
119
+ from typing import Any
120
+
121
+ from snulbug import CredentialBroker, register_credential_broker
122
+
123
+
124
+ class AcmeVaultBroker(CredentialBroker):
125
+ type = "acme-vault"
126
+
127
+ def normalize_source(
128
+ self,
129
+ entry: Mapping[str, Any],
130
+ *,
131
+ base_dir: Path,
132
+ field: str,
133
+ resolve_relative_paths: bool,
134
+ ) -> Mapping[str, Any]:
135
+ del base_dir, resolve_relative_paths
136
+ name = entry.get("name")
137
+ if not isinstance(name, str) or not name:
138
+ raise ValueError(f"{field}.name must be a non-empty credential name")
139
+ return {"name": name}
140
+
141
+ def resolve(self, credential: Mapping[str, Any]) -> str:
142
+ # Fetch or mint the secret here. Return the raw token value; snulbug
143
+ # applies the configured scheme before forwarding.
144
+ return fetch_from_acme_vault(str(credential["name"]))
145
+
146
+ def metadata(self, credential: Mapping[str, Any]) -> Mapping[str, Any]:
147
+ # Return only audit-safe fields. Never return the resolved secret.
148
+ return {"name": credential.get("name")}
149
+
150
+
151
+ register_credential_broker(AcmeVaultBroker(), replace=True)
152
+ ```
153
+
154
+ Config uses the same `[mcp.fabric.credentials]` table as built-ins:
155
+
156
+ ```toml
157
+ [mcp.fabric.credentials.codespace]
158
+ type = "acme-vault"
159
+ name = "codespace-mcp-token"
160
+ scheme = "bearer"
161
+ header = "Authorization"
162
+
163
+ [[mcp.proxy.upstreams]]
164
+ name = "codespace-files"
165
+ url = "https://example-codespace.github.dev/mcp"
166
+ auth = "codespace"
167
+ ```
168
+
169
+ The broker is called when snulbug forwards a request, probes an upstream, or
170
+ explicitly checks its availability through live doctor/fabric diagnostics.
171
+ Audit logs, share contracts, fabric status, and replay logs receive only
172
+ `credential_metadata()` output.
@@ -10,8 +10,8 @@ servers with a shared control plane.
10
10
  ```json
11
11
  {
12
12
  "features": {
13
- "ghcr.io/nahuaque/snulbug/features/snulbug:0.2.3": {
14
- "version": "0.2.3",
13
+ "ghcr.io/nahuaque/snulbug/features/snulbug:0.2.5": {
14
+ "version": "0.2.5",
15
15
  "mode": "member-agent",
16
16
  "policy_profile": "tunnel-safe",
17
17
  "registry": "redis://redis:6379/0",