relay-shell 0.5.2__tar.gz → 0.5.3__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 (222) hide show
  1. {relay_shell-0.5.2 → relay_shell-0.5.3}/.env.example +1 -1
  2. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/ISSUE_TEMPLATE/feature.md +1 -1
  3. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/PULL_REQUEST_TEMPLATE.md +4 -2
  4. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/copilot-instructions.md +1 -1
  5. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/ci.yml +1 -1
  6. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/codeql.yml +5 -5
  7. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/docs.yml +2 -2
  8. {relay_shell-0.5.2 → relay_shell-0.5.3}/.pre-commit-config.yaml +5 -5
  9. {relay_shell-0.5.2 → relay_shell-0.5.3}/AGENTS.md +1 -1
  10. {relay_shell-0.5.2 → relay_shell-0.5.3}/BACKLOG.md +5 -0
  11. {relay_shell-0.5.2 → relay_shell-0.5.3}/CHANGELOG.md +20 -1
  12. {relay_shell-0.5.2 → relay_shell-0.5.3}/CONTRIBUTING.md +3 -3
  13. {relay_shell-0.5.2 → relay_shell-0.5.3}/PKG-INFO +30 -11
  14. {relay_shell-0.5.2 → relay_shell-0.5.3}/README.md +28 -9
  15. relay_shell-0.5.3/audit/2026-11-07-documentation-review.md +74 -0
  16. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/architecture.md +21 -10
  17. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/audit-shipper.md +51 -30
  18. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/auth.md +7 -5
  19. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/deployment.md +50 -23
  20. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/index.md +12 -4
  21. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/release-trust.md +39 -3
  22. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/runbook.md +63 -25
  23. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/tools.md +5 -2
  24. {relay_shell-0.5.2 → relay_shell-0.5.3}/mkdocs.yml +3 -4
  25. {relay_shell-0.5.2 → relay_shell-0.5.3}/pyproject.toml +5 -5
  26. {relay_shell-0.5.2 → relay_shell-0.5.3}/renovate.json5 +24 -1
  27. {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements/development-py3.12.txt +506 -499
  28. {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements/development-py3.14.txt +506 -499
  29. relay_shell-0.5.3/requirements/development.in +13 -0
  30. {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements/runtime-py3.12.txt +186 -182
  31. {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements/runtime-py3.14.txt +186 -182
  32. relay_shell-0.5.3/requirements/runtime.in +8 -0
  33. {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-audit.txt +107 -107
  34. relay_shell-0.5.3/requirements-bootstrap.txt +6 -0
  35. {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-build.txt +9 -9
  36. {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-docs.txt +1 -1
  37. {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-sbom.txt +19 -19
  38. {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements.txt +8 -8
  39. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/__init__.py +1 -1
  40. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/audit.py +1 -1
  41. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/broker.py +1 -1
  42. relay_shell-0.5.3/tests/test_audit_rotation_names.py +34 -0
  43. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_release_workflows.py +39 -0
  44. relay_shell-0.5.2/requirements-bootstrap.txt +0 -6
  45. {relay_shell-0.5.2 → relay_shell-0.5.3}/.claude/settings.json +0 -0
  46. {relay_shell-0.5.2 → relay_shell-0.5.3}/.editorconfig +0 -0
  47. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/CODEOWNERS +0 -0
  48. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/ISSUE_TEMPLATE/bug.md +0 -0
  49. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/ISSUE_TEMPLATE/security.md +0 -0
  50. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/ci-gate.yml +0 -0
  51. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/copilot-setup-steps.yml +0 -0
  52. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/dependency-review.yml +0 -0
  53. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/gitleaks.yml +0 -0
  54. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/nightly-fuzz.yml +0 -0
  55. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/pip-audit.yml +0 -0
  56. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/release-rehearsal.yml +0 -0
  57. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/release.yml +0 -0
  58. {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/sbom.yml +0 -0
  59. {relay_shell-0.5.2 → relay_shell-0.5.3}/.gitignore +0 -0
  60. {relay_shell-0.5.2 → relay_shell-0.5.3}/.gitleaks.toml +0 -0
  61. {relay_shell-0.5.2 → relay_shell-0.5.3}/CLAUDE.md +0 -0
  62. {relay_shell-0.5.2 → relay_shell-0.5.3}/CODE_OF_CONDUCT.md +0 -0
  63. {relay_shell-0.5.2 → relay_shell-0.5.3}/LICENSE +0 -0
  64. {relay_shell-0.5.2 → relay_shell-0.5.3}/NOTICE +0 -0
  65. {relay_shell-0.5.2 → relay_shell-0.5.3}/SECURITY.md +0 -0
  66. {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/00-inventory.md +0 -0
  67. {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/01-baseline.md +0 -0
  68. {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/02-security-findings.md +0 -0
  69. {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/03-final-report.md +0 -0
  70. {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-05-27-engagement.md +0 -0
  71. {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-06-01-engagement.md +0 -0
  72. {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-06-21-adversarial-engagement.md +0 -0
  73. {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-06-21-engagement.md +0 -0
  74. {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-07-15-adversarial-engagement.md +0 -0
  75. {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-07-15-engagement.md +0 -0
  76. {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-07-15-release-v0.3.0.md +0 -0
  77. {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-08-04-backlog-followup.md +0 -0
  78. {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-08-04-engagement.md +0 -0
  79. {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-10-14-engagement.md +0 -0
  80. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/Caddyfile +0 -0
  81. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/evidence/README.md +0 -0
  82. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/evidence/approved-resets.json.example +0 -0
  83. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/evidence/audit-evidence.env.example +0 -0
  84. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/evidence/relay-audit-evidence-sync +0 -0
  85. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/evidence/relay-audit-evidence.py +0 -0
  86. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/evidence/relay-audit-evidence.service +0 -0
  87. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/evidence/relay-audit-evidence.timer +0 -0
  88. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/install-edge.sh +0 -0
  89. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/install.sh +0 -0
  90. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/logrotate/relay-shell +0 -0
  91. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/PACKAGE.lock +0 -0
  92. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/README.md +0 -0
  93. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/nftables.conf.snippet +0 -0
  94. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/prometheus-node-exporter.service.d/override.conf +0 -0
  95. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/relay-health-metrics.py +0 -0
  96. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/relay-health-metrics.service +0 -0
  97. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/relay-health-metrics.timer +0 -0
  98. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/relay-monitoring.env.example +0 -0
  99. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/release-signing.pub +0 -0
  100. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/systemd/relay-shell.service +0 -0
  101. {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/systemd/relay-shell.service.d/hardening.conf +0 -0
  102. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0001-runtime-and-sdk.md +0 -0
  103. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0002-no-sandbox-full-access.md +0 -0
  104. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0003-tiered-authority.md +0 -0
  105. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0004-edge-tls-automation.md +0 -0
  106. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0005-codebase-validation.md +0 -0
  107. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0006-seccomp-notify-audit-channel.md +0 -0
  108. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0007-audit-hash-chain.md +0 -0
  109. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0008-operating-guidance-prompt.md +0 -0
  110. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0009-tier3-confirmation-broker.md +0 -0
  111. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0010-rollback-verify-broker.md +0 -0
  112. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0011-windows-openssh-powershell.md +0 -0
  113. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0012-cancel-safe-session-teardown.md +0 -0
  114. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0013-open-by-default-with-unsafe-notices.md +0 -0
  115. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0014-seccomp-launch-and-install-recovery.md +0 -0
  116. {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/README.md +0 -0
  117. {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements/README.md +0 -0
  118. {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-audit.in +0 -0
  119. {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-bootstrap.in +0 -0
  120. {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-build.in +0 -0
  121. {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-sbom.in +0 -0
  122. {relay_shell-0.5.2 → relay_shell-0.5.3}/scripts/check_ci_checks.py +0 -0
  123. {relay_shell-0.5.2 → relay_shell-0.5.3}/scripts/healthcheck.sh +0 -0
  124. {relay_shell-0.5.2 → relay_shell-0.5.3}/scripts/installer_transaction.py +0 -0
  125. {relay_shell-0.5.2 → relay_shell-0.5.3}/scripts/reconcile_runtime.py +0 -0
  126. {relay_shell-0.5.2 → relay_shell-0.5.3}/scripts/rehearse_install.py +0 -0
  127. {relay_shell-0.5.2 → relay_shell-0.5.3}/scripts/rehearse_release.py +0 -0
  128. {relay_shell-0.5.2 → relay_shell-0.5.3}/scripts/rehearse_upgrade.py +0 -0
  129. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/__main__.py +0 -0
  130. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/auth/__init__.py +0 -0
  131. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/auth/admin.py +0 -0
  132. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/auth/oauth.py +0 -0
  133. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/auth/resource.py +0 -0
  134. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/config.py +0 -0
  135. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/errors.py +0 -0
  136. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/inventory.py +0 -0
  137. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/metrics.py +0 -0
  138. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/notices.py +0 -0
  139. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/patterns.py +0 -0
  140. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/policy.py +0 -0
  141. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/py.typed +0 -0
  142. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/redaction.py +0 -0
  143. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/seccomp.py +0 -0
  144. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/server.py +0 -0
  145. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/sessions.py +0 -0
  146. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/shelltools.py +0 -0
  147. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/sshpool.py +0 -0
  148. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/util.py +0 -0
  149. {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/verifier.py +0 -0
  150. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/conftest.py +0 -0
  151. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/fixtures/upgrade-v0.3/README.md +0 -0
  152. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/fixtures/upgrade-v0.3/advisory-description-hashes.json +0 -0
  153. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/fixtures/upgrade-v0.3/advisory-description.txt +0 -0
  154. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/fixtures/upgrade-v0.3/baseline.json +0 -0
  155. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/fixtures/upgrade-v0.3/runtime.in +0 -0
  156. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/fixtures/upgrade-v0.3/runtime.lock +0 -0
  157. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/helpers.py +0 -0
  158. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_a14_rs.py +0 -0
  159. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_a14_t1_gaps.py +0 -0
  160. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_admission_guards.py +0 -0
  161. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_audit.py +0 -0
  162. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_audit_lows.py +0 -0
  163. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_audit_sink.py +0 -0
  164. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_audit_tail_tool.py +0 -0
  165. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_authenticated_audit.py +0 -0
  166. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_broker.py +0 -0
  167. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_broker_client_binding.py +0 -0
  168. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_cancellation_audit.py +0 -0
  169. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_ci_checks.py +0 -0
  170. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_ci_gate.py +0 -0
  171. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_classifier_scan_gates.py +0 -0
  172. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_classifier_v12.py +0 -0
  173. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_classifier_v16.py +0 -0
  174. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_config.py +0 -0
  175. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_config_hygiene.py +0 -0
  176. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_config_template.py +0 -0
  177. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_dependency_floors.py +0 -0
  178. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_deploy_packaging.py +0 -0
  179. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_edge_routing.py +0 -0
  180. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_errors.py +0 -0
  181. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_evidence_deploy.py +0 -0
  182. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_fuzz.py +0 -0
  183. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_healthcheck.py +0 -0
  184. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_install_edge_validation.py +0 -0
  185. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_install_rehearsal.py +0 -0
  186. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_installer_transaction.py +0 -0
  187. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_inventory.py +0 -0
  188. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_logrotate_config.py +0 -0
  189. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_main.py +0 -0
  190. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_metrics.py +0 -0
  191. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_notices.py +0 -0
  192. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_oauth.py +0 -0
  193. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_oauth_approval.py +0 -0
  194. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_oauth_resource.py +0 -0
  195. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_oauth_reuse.py +0 -0
  196. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_oauth_store.py +0 -0
  197. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_patterns.py +0 -0
  198. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_policy.py +0 -0
  199. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_prompts.py +0 -0
  200. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_redaction.py +0 -0
  201. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_redaction_operators.py +0 -0
  202. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_redaction_v16.py +0 -0
  203. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_release_admission.py +0 -0
  204. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_resources.py +0 -0
  205. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_root_execution_contract.py +0 -0
  206. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_runtime_inventory.py +0 -0
  207. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_seccomp.py +0 -0
  208. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_server.py +0 -0
  209. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_session_secret_audit.py +0 -0
  210. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_sessions.py +0 -0
  211. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_sessions_cancel.py +0 -0
  212. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_shell.py +0 -0
  213. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_ssh_fanout_tool.py +0 -0
  214. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_ssh_integration.py +0 -0
  215. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_ssh_keyscan_tool.py +0 -0
  216. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_sshpool_unit.py +0 -0
  217. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_stdio_e2e.py +0 -0
  218. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_tool_wrappers.py +0 -0
  219. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_upgrade_rehearsal.py +0 -0
  220. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_util.py +0 -0
  221. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_verifier.py +0 -0
  222. {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_workflow_hygiene.py +0 -0
@@ -126,7 +126,7 @@ RELAY_SHELL_EDGE_UPSTREAM=127.0.0.1:8080
126
126
  # Set to https://acme-staging-v02.api.letsencrypt.org/directory for dry runs.
127
127
  RELAY_SHELL_EDGE_ACME_CA=https://acme-v02.api.letsencrypt.org/directory
128
128
 
129
- # --- OAuth 2.1 (HTTP transport only; requires the [http] extra) ---
129
+ # --- OAuth 2.1 (HTTP transport only; plain install includes dependencies) ---
130
130
  RELAY_SHELL_AUTH_ENABLED=false
131
131
  RELAY_SHELL_AUTH_ISSUER=https://localhost:8080
132
132
  RELAY_SHELL_AUTH_STATE_DIR=/var/lib/relay-shell/oauth
@@ -49,7 +49,7 @@ plus a script) and the only ask is ergonomics, say so explicitly. -->
49
49
  - an entry in `tests/test_server.py::_EXPECTED` plus the `len()` assertion
50
50
  - an entry in the README capability table
51
51
  - a unit test of the underlying helper
52
- - a wiring test that exercises the tool through FastMCP
52
+ - a wiring test that exercises the tool through `MCPServer`
53
53
  - the `_INSTRUCTIONS` string in `server.py` -->
54
54
 
55
55
  ## Out of scope
@@ -37,8 +37,10 @@ if applicable: "Closes F-6 from docs/runbook.md". -->
37
37
  `docs/deployment.md`.
38
38
  - [ ] A new permissive setting has a matching entry in `notices.config_warnings`, and the
39
39
  default configuration still refuses no command (ADR 0013).
40
- - [ ] No new dependency is added without a justification in the PR body
41
- and a pinned version in `requirements.txt`.
40
+ - [ ] Dependency changes justify the addition in the PR body, align
41
+ `pyproject.toml`, `requirements.txt` and the matching `requirements/*.in`
42
+ scope, and regenerate all affected runtime/development/tooling hash locks
43
+ for both qualified profiles (runbook §6.6 and `requirements/README.md`).
42
44
 
43
45
  ## Security-sensitive diff (runbook §3.3)
44
46
 
@@ -34,5 +34,5 @@ MCP shell/SSH operator infrastructure. Read `docs/runbook.md` (especially its au
34
34
  - Audit arguments must be redacted; output bodies must not be logged, only length/hash. Preserve chain limitations, rotation continuity, off-host integrity requirements, and secret-prompt handling. Redaction is heuristic and must be tested with synthetic secrets.
35
35
  - Tool errors use existing bounded structured error helpers, not raw tracebacks or uncaught transport failures. Preserve cancellation and cleanup, close only owned sessions, and reconcile timeouts before retrying mutations.
36
36
  - Technical map: `src/relay_shell/server.py` registers tools; config, policy, broker, audit, redaction, patterns, sessions and sshpool define the principal boundaries. `tests/test_server.py::_EXPECTED` is the tool-registration contract.
37
- - Setup: `python -m pip install -e ".[dev]"`. Run `ruff check .`, `ruff format --check .`, `mypy`, `pytest -q`. For classifier/redactor changes also run `pytest -m fuzz -v`; update PATTERNS_VERSION when required by the existing contract. Preserve CodeQL, dependency review, dependency audit and secret-scanning gates.
37
+ - Setup: follow the matching hash-locked development profile and no-dependency editable install in `CONTRIBUTING.md` (the canonical local loop). Run `ruff check .`, `ruff format --check .`, `mypy`, `pytest -q`. For classifier/redactor changes also run `pytest -m fuzz -v`; update PATTERNS_VERSION when required by the existing contract. Preserve CodeQL, dependency review, dependency audit and secret-scanning gates.
38
38
  - Use async I/O in tool paths; do not add requests/urllib3 blocking calls. Live shell/SSH smokes require explicit targets and bounded effects. Editing this repo does not authorize restarting or deploying its running service.
@@ -88,7 +88,7 @@ jobs:
88
88
  GODEBUG: http2client=0
89
89
  run: |
90
90
  command -v go
91
- go install github.com/caddyserver/caddy/v2/cmd/caddy@v2.11.7
91
+ go install github.com/caddyserver/caddy/v2/cmd/caddy@v2.11.9
92
92
  echo "CADDY_BINARY=$GOBIN/caddy" >> "$GITHUB_ENV"
93
93
 
94
94
  - name: Lint (ruff)
@@ -38,15 +38,15 @@ jobs:
38
38
  persist-credentials: false
39
39
 
40
40
  - name: Initialize CodeQL
41
- uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4
41
+ uses: github/codeql-action/init@33d7b85da1a04171e211befe2c739303b9a4c066 # v4
42
42
  with:
43
43
  languages: ${{ matrix.language }}
44
44
 
45
45
  - name: Autobuild
46
- uses: github/codeql-action/autobuild@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4
46
+ uses: github/codeql-action/autobuild@33d7b85da1a04171e211befe2c739303b9a4c066 # v4
47
47
 
48
48
  - name: Perform CodeQL Analysis
49
- uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4
49
+ uses: github/codeql-action/analyze@33d7b85da1a04171e211befe2c739303b9a4c066 # v4
50
50
 
51
51
  # A14-D12: the workflows themselves are code that holds tokens. A separate job (not a
52
52
  # matrix entry) so the required check `Analyze (python) (python)` keeps its name. It is in
@@ -63,12 +63,12 @@ jobs:
63
63
  persist-credentials: false
64
64
 
65
65
  - name: Initialize CodeQL
66
- uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4
66
+ uses: github/codeql-action/init@33d7b85da1a04171e211befe2c739303b9a4c066 # v4
67
67
  with:
68
68
  languages: actions
69
69
  build-mode: none
70
70
 
71
71
  - name: Perform CodeQL Analysis
72
- uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4
72
+ uses: github/codeql-action/analyze@33d7b85da1a04171e211befe2c739303b9a4c066 # v4
73
73
  with:
74
74
  category: "/language:actions"
@@ -28,7 +28,7 @@ concurrency:
28
28
  jobs:
29
29
  build:
30
30
  name: Build (MkDocs Material)
31
- runs-on: ubuntu-24.04
31
+ runs-on: ubuntu-26.04
32
32
  timeout-minutes: 10
33
33
  steps:
34
34
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
@@ -55,7 +55,7 @@ jobs:
55
55
  name: Deploy to GitHub Pages
56
56
  needs: build
57
57
  if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
58
- runs-on: ubuntu-24.04
58
+ runs-on: ubuntu-26.04
59
59
  timeout-minutes: 10
60
60
  permissions:
61
61
  pages: write # publish the Pages deployment
@@ -1,6 +1,6 @@
1
1
  # Pre-commit hooks for relay-shell.
2
2
  #
3
- # Install: `pip install pre-commit && pre-commit install`
3
+ # Install the matching locked development profile, then run `pre-commit install`.
4
4
  # Run on a single file: `pre-commit run --files <path>`
5
5
  # Run on the whole tree: `pre-commit run --all-files`
6
6
  #
@@ -15,7 +15,7 @@ repos:
15
15
  # Pin to a known-good ruff release. Update this `rev` and the
16
16
  # `ruff==` pin in `[project.optional-dependencies] dev` together
17
17
  # so local pre-commit and `pip install -e ".[dev]"` agree.
18
- rev: v0.17.2
18
+ rev: v0.17.3
19
19
  hooks:
20
20
  - id: ruff
21
21
  args: [--fix]
@@ -24,11 +24,11 @@ repos:
24
24
  - repo: local
25
25
  hooks:
26
26
  # mypy --strict needs the project's full import graph (asyncssh stubs
27
- # override, FastMCP Context typing, etc.). Run it from the project's
27
+ # override, MCPServer Context typing, etc.). Run it from the project's
28
28
  # own venv rather than an isolated pre-commit env so the configured
29
29
  # overrides in pyproject.toml apply identically here and in CI. This
30
- # assumes the contributor ran `pip install -e ".[dev]"` first, which
31
- # the runbook §4.1 already requires.
30
+ # assumes the contributor installed the matching locked development profile
31
+ # and no-dependency editable checkout from runbook §4.1 first.
32
32
  - id: mypy
33
33
  name: mypy --strict (project venv)
34
34
  entry: mypy
@@ -11,7 +11,7 @@ MCP shell/SSH operator infrastructure. Read `docs/runbook.md` (especially its au
11
11
  - Audit arguments must be redacted; output bodies must not be logged, only length/hash. Preserve chain limitations, rotation continuity, off-host integrity requirements, and secret-prompt handling. Redaction is heuristic and must be tested with synthetic secrets.
12
12
  - Tool errors use existing bounded structured error helpers, not raw tracebacks or uncaught transport failures. Preserve cancellation and cleanup, close only owned sessions, and reconcile timeouts before retrying mutations.
13
13
  - Technical map: `src/relay_shell/server.py` registers tools; config, policy, broker, audit, redaction, patterns, sessions and sshpool define the principal boundaries. `tests/test_server.py::_EXPECTED` is the tool-registration contract.
14
- - Setup: `python -m pip install -e ".[dev]"`. Run `ruff check .`, `ruff format --check .`, `mypy`, `pytest -q`. For classifier/redactor changes also run `pytest -m fuzz -v`; update PATTERNS_VERSION when required by the existing contract. Preserve CodeQL, dependency review, dependency audit and secret-scanning gates.
14
+ - Setup: follow the matching hash-locked development profile and no-dependency editable install in `CONTRIBUTING.md` (the canonical local loop). Run `ruff check .`, `ruff format --check .`, `mypy`, `pytest -q`. For classifier/redactor changes also run `pytest -m fuzz -v`; update PATTERNS_VERSION when required by the existing contract. Preserve CodeQL, dependency review, dependency audit and secret-scanning gates.
15
15
  - Use async I/O in tool paths; do not add requests/urllib3 blocking calls. Live shell/SSH smokes require explicit targets and bounded effects. Editing this repo does not authorize restarting or deploying its running service.
16
16
 
17
17
  ## Working contract
@@ -298,6 +298,11 @@ reauthorization. Operational TLS/clients, broader state-policy reconciliation,
298
298
  binary-only downgrades, automatic rollback and live-host recovery remain open.
299
299
 
300
300
 
301
+ **Runner update, 2026-11-07:** the current cross-version job now runs on
302
+ Ubuntu 26.04 with an explicit legacy Python 3.12 interpreter. The paragraph above
303
+ records the initial qualification; the native qualified profiles remain
304
+ Ubuntu 24.04/Python 3.12 and Ubuntu 26.04/Python 3.14.
305
+
301
306
  ### Installer rollback qualification (2026-10-20)
302
307
 
303
308
  Implemented automatic restoration for ordinary installer errors and handled
@@ -6,6 +6,24 @@ All notable changes to this project are documented here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.5.3] - 2026-11-12
10
+
11
+ ### Fixed
12
+
13
+ - Restrict rotated audit-log selection to the configured log basename so unrelated
14
+ streams cannot supply the restart chain anchor.
15
+
16
+ - Align quickstart/deployment instructions with locked profiles and installer recovery;
17
+ clarify audit, timeout and resource/prompt limits, edge-variable precedence and
18
+ OAuth reauthorization. Refresh release-signature evidence through v0.5.2.
19
+ - Correct the Vector shipping configuration, package-source guidance and parser
20
+ error handling; document protocol and rotation/backfill limits.
21
+ - Use system fonts for the documentation site to avoid third-party font requests.
22
+
23
+ - Give Renovate separate runtime/development lock inputs with manifest alignment
24
+ checks, and normalize the bootstrap regeneration command's option arguments.
25
+ This preserves runtime scope while allowing all hashed profiles to be extracted.
26
+
9
27
  ## [0.5.2] - 2026-11-02
10
28
 
11
29
  ### Fixed
@@ -1829,7 +1847,8 @@ All notable changes to this project are documented here. The format follows
1829
1847
  bundled logrotate config drops and restores the append-only attribute
1830
1848
  across rotation. See `docs/deployment.md` §6.
1831
1849
 
1832
- [Unreleased]: https://github.com/rmednitzer/relay-shell/compare/v0.5.2...HEAD
1850
+ [Unreleased]: https://github.com/rmednitzer/relay-shell/compare/v0.5.3...HEAD
1851
+ [0.5.3]: https://github.com/rmednitzer/relay-shell/compare/v0.5.2...v0.5.3
1833
1852
  [0.5.2]: https://github.com/rmednitzer/relay-shell/compare/v0.5.1...v0.5.2
1834
1853
  [0.5.1]: https://github.com/rmednitzer/relay-shell/compare/v0.5.0...v0.5.1
1835
1854
  [0.5.0]: https://github.com/rmednitzer/relay-shell/compare/v0.4.0...v0.5.0
@@ -99,8 +99,8 @@ short version:
99
99
  | Add or remove a tool | `docs/tools.md`, `tests/test_server.py::_EXPECTED`, README capability tables, `_INSTRUCTIONS` in `server.py` |
100
100
  | Add or remove an env var | `.env.example`, `Settings`, `docs/deployment.md` (and `notices.config_warnings` if it widens access) |
101
101
  | Add or change a module | `docs/architecture.md` module table |
102
- | Add a runtime dependency | `requirements.txt` (pinned), the `pyproject.toml` bound + a justification in the PR body |
103
- | Change release/SBOM tooling or a workflow | `requirements-build.in` / `requirements-sbom.in` then regenerate the hashed `.txt` locks (runbook §6.6), `tests/test_release_workflows.py` |
102
+ | Add a runtime dependency | `pyproject.toml` bound, `requirements.txt` pin, `requirements/runtime.in`, all affected runtime/development hashed profiles, and a justification in the PR body |
103
+ | Change dependency/build/audit tooling | Matching `.in` inputs and all affected hashed outputs using their recorded commands (runbook §6.6), `tests/test_release_workflows.py` |
104
104
  | Change audit-record fields | `docs/architecture.md` request lifecycle, `tests/test_audit.py`, runbook §2.3 |
105
105
  | Change a redaction/tier pattern | `src/relay_shell/patterns.py` (bump `PATTERNS_VERSION`), paired over-scrub/under-scrub (redaction) or positive/near-miss (policy) tests in `tests/test_patterns.py`, runbook §6.4 (policy heuristics) / §6.5 (redaction rules) |
106
106
 
@@ -170,7 +170,7 @@ These recur in the runbook §3.4 "common review failures" list.
170
170
  - `ruff check . && ruff format --check .` must pass. The line length
171
171
  is 100, target version is `py312`.
172
172
  - `mypy` is strict for the package and relaxed only for
173
- `relay_shell.server` (FastMCP) and `asyncssh` (no stubs). Prefer
173
+ `relay_shell.server` (MCPServer) and `asyncssh` (no stubs). Prefer
174
174
  narrowing an override in `pyproject.toml` over `# type: ignore` in
175
175
  the file.
176
176
  - No new runtime dependency without a justification in the PR body.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: relay-shell
3
- Version: 0.5.2
3
+ Version: 0.5.3
4
4
  Summary: A highly reliable, maximally capable MCP server for complete shell and SSH mastery: a governed relay in the shell/SSH path.
5
5
  Project-URL: Homepage, https://github.com/rmednitzer/relay-shell
6
6
  Project-URL: Documentation, https://rmednitzer.github.io/relay-shell/
@@ -36,7 +36,7 @@ Requires-Dist: pre-commit<5,>=4.0; extra == 'dev'
36
36
  Requires-Dist: pyjwt<3,>=2.15.0; extra == 'dev'
37
37
  Requires-Dist: pytest-asyncio<2,>=0.24; extra == 'dev'
38
38
  Requires-Dist: pytest<10,>=8.3; extra == 'dev'
39
- Requires-Dist: ruff==0.17.2; extra == 'dev'
39
+ Requires-Dist: ruff==0.17.3; extra == 'dev'
40
40
  Provides-Extra: http
41
41
  Description-Content-Type: text/markdown
42
42
 
@@ -53,14 +53,20 @@ It is operator infrastructure for hosts you own and administer. It runs with the
53
53
  its service account, with no sandbox, and it is **open by default**: no command is refused for
54
54
  what it does. What it adds is a record and a warning:
55
55
 
56
- - **Every call is audited:** one JSON line with the redacted arguments, the output's hash and
56
+ - **Per-call audit:** one JSON line with the redacted arguments, the output's hash and
57
57
  length (never its body), the exit code, the client and the assessed tier.
58
- - **Every risky call says so:** a call classified Tier 2 (changes state) or Tier 3
58
+ - **Risk notices:** a call classified Tier 2 (changes state) or Tier 3
59
59
  (irreversible), or an SSH call with host-key checking off, ends with a `[NOTICE ...]` line.
60
60
  `server_info` lists the standing unsafe settings.
61
61
  - **Refusal is opt-in:** `guarded` and `readonly` modes, a deny list and a Tier-3 confirmation
62
62
  step are available and all off by default.
63
63
 
64
+ Audit records describe completed, denied and cancelled calls; a process crash can
65
+ leave an execution without a completion record. Opt-in `RELAY_SHELL_AUDIT_INTENT=true`
66
+ adds a pre-execution record. Runtime sink failures mark audit health degraded while
67
+ calls continue; monitor that health and ship evidence off-host. See the
68
+ [audit lifecycle](docs/architecture.md#request-lifecycle).
69
+
64
70
  ## Tools
65
71
 
66
72
  | Local shell | Purpose |
@@ -109,8 +115,11 @@ is CPython 3.12; other interpreter/OS combinations are not qualified.
109
115
 
110
116
  ```bash
111
117
  git clone https://github.com/rmednitzer/relay-shell.git && cd relay-shell
112
- python3 -m venv .venv && . .venv/bin/activate
113
- pip install -e ".[dev]"
118
+ python3.12 -m venv .venv
119
+ . .venv/bin/activate
120
+ python -m pip install --only-binary=:all: --require-hashes -r requirements/development-py3.12.txt
121
+ python -m pip install --no-deps --no-build-isolation -e ".[dev]"
122
+ python -m pip check
114
123
 
115
124
  relay-shell # stdio transport
116
125
  RELAY_SHELL_TRANSPORT=http relay-shell # streamable-http on 127.0.0.1:8080
@@ -118,6 +127,11 @@ relay-shell --check-config # validate config, print unsafe-setting
118
127
  relay-shell --verify-deploy # compare installed templates with the shipped ones
119
128
  ```
120
129
 
130
+ The example uses Ubuntu 24.04 / CPython 3.12. On Ubuntu 26.04, use
131
+ `python3.14` and `requirements/development-py3.14.txt` instead. For a service
132
+ installation using the runtime-only lock and an isolated builder, follow the
133
+ [deployment guide](docs/deployment.md#2-install).
134
+
121
135
  Register with an MCP client (stdio):
122
136
 
123
137
  ```json
@@ -145,19 +159,24 @@ first. Classification is a heuristic, not a boundary]
145
159
  The classifier is a regex heuristic over the command text, not a parse of the shell language.
146
160
  A call without a notice is not thereby safe, and a notice does not block anything.
147
161
 
148
- Only resource bounds and one startup check refuse a call, none of them based on what the
149
- command does:
162
+ Open mode controls command admission; it does not bypass argument validation,
163
+ SSH host verification, transport authentication or resource limits. With the
164
+ default policy, a command is not refused because of its meaning. Important bounds
165
+ and startup checks include:
150
166
 
151
167
  | Bound | Setting (default) | Behaviour |
152
168
  |-------|-------------------|-----------|
153
- | Input size (command, stdin, script, env) | `RELAY_SHELL_MAX_INPUT` (2 MiB) | refused, because the policy scan is synchronous |
154
- | Wall-clock per call | `RELAY_SHELL_MAX_TIMEOUT` (900 s) | killed; use `shell_spawn` / `ssh_spawn` for longer work |
169
+ | Input size (command, stdin, script, env) | `RELAY_SHELL_MAX_INPUT` (2,097,152 characters) | refused, because the policy scan is synchronous |
170
+ | One-shot command timeout | `RELAY_SHELL_MAX_TIMEOUT` (900 s) | local process group terminated; remote termination is best effort; use `shell_spawn` / `ssh_spawn` for longer work |
155
171
  | Live sessions / forwards | `RELAY_SHELL_MAX_SESSIONS` (64), `RELAY_SHELL_MAX_FORWARDS` (64) | a new one is refused at the cap |
156
172
  | Output returned | `RELAY_SHELL_MAX_OUTPUT` (64 KiB) | truncated, not refused |
157
173
  | HTTP without OAuth on a non-loopback address | `RELAY_SHELL_ALLOW_UNAUTH_NETWORK` | refused at startup unless set |
158
174
 
159
175
  Session output buffers are also bounded, and idle sessions (`RELAY_SHELL_SESSION_IDLE_TIMEOUT`,
160
- 1800 s) and exited, drained sessions are reaped.
176
+ 1800 s) and exited, drained sessions are reaped. Persistent sessions have no
177
+ maximum lifetime. SFTP transfers default to `timeout=0` (no per-call deadline);
178
+ set a positive transfer timeout when needed. After a remote timeout or disconnect,
179
+ reconcile the remote operation before retrying a mutation.
161
180
 
162
181
  ### Making it stricter
163
182
 
@@ -11,14 +11,20 @@ It is operator infrastructure for hosts you own and administer. It runs with the
11
11
  its service account, with no sandbox, and it is **open by default**: no command is refused for
12
12
  what it does. What it adds is a record and a warning:
13
13
 
14
- - **Every call is audited:** one JSON line with the redacted arguments, the output's hash and
14
+ - **Per-call audit:** one JSON line with the redacted arguments, the output's hash and
15
15
  length (never its body), the exit code, the client and the assessed tier.
16
- - **Every risky call says so:** a call classified Tier 2 (changes state) or Tier 3
16
+ - **Risk notices:** a call classified Tier 2 (changes state) or Tier 3
17
17
  (irreversible), or an SSH call with host-key checking off, ends with a `[NOTICE ...]` line.
18
18
  `server_info` lists the standing unsafe settings.
19
19
  - **Refusal is opt-in:** `guarded` and `readonly` modes, a deny list and a Tier-3 confirmation
20
20
  step are available and all off by default.
21
21
 
22
+ Audit records describe completed, denied and cancelled calls; a process crash can
23
+ leave an execution without a completion record. Opt-in `RELAY_SHELL_AUDIT_INTENT=true`
24
+ adds a pre-execution record. Runtime sink failures mark audit health degraded while
25
+ calls continue; monitor that health and ship evidence off-host. See the
26
+ [audit lifecycle](docs/architecture.md#request-lifecycle).
27
+
22
28
  ## Tools
23
29
 
24
30
  | Local shell | Purpose |
@@ -67,8 +73,11 @@ is CPython 3.12; other interpreter/OS combinations are not qualified.
67
73
 
68
74
  ```bash
69
75
  git clone https://github.com/rmednitzer/relay-shell.git && cd relay-shell
70
- python3 -m venv .venv && . .venv/bin/activate
71
- pip install -e ".[dev]"
76
+ python3.12 -m venv .venv
77
+ . .venv/bin/activate
78
+ python -m pip install --only-binary=:all: --require-hashes -r requirements/development-py3.12.txt
79
+ python -m pip install --no-deps --no-build-isolation -e ".[dev]"
80
+ python -m pip check
72
81
 
73
82
  relay-shell # stdio transport
74
83
  RELAY_SHELL_TRANSPORT=http relay-shell # streamable-http on 127.0.0.1:8080
@@ -76,6 +85,11 @@ relay-shell --check-config # validate config, print unsafe-setting
76
85
  relay-shell --verify-deploy # compare installed templates with the shipped ones
77
86
  ```
78
87
 
88
+ The example uses Ubuntu 24.04 / CPython 3.12. On Ubuntu 26.04, use
89
+ `python3.14` and `requirements/development-py3.14.txt` instead. For a service
90
+ installation using the runtime-only lock and an isolated builder, follow the
91
+ [deployment guide](docs/deployment.md#2-install).
92
+
79
93
  Register with an MCP client (stdio):
80
94
 
81
95
  ```json
@@ -103,19 +117,24 @@ first. Classification is a heuristic, not a boundary]
103
117
  The classifier is a regex heuristic over the command text, not a parse of the shell language.
104
118
  A call without a notice is not thereby safe, and a notice does not block anything.
105
119
 
106
- Only resource bounds and one startup check refuse a call, none of them based on what the
107
- command does:
120
+ Open mode controls command admission; it does not bypass argument validation,
121
+ SSH host verification, transport authentication or resource limits. With the
122
+ default policy, a command is not refused because of its meaning. Important bounds
123
+ and startup checks include:
108
124
 
109
125
  | Bound | Setting (default) | Behaviour |
110
126
  |-------|-------------------|-----------|
111
- | Input size (command, stdin, script, env) | `RELAY_SHELL_MAX_INPUT` (2 MiB) | refused, because the policy scan is synchronous |
112
- | Wall-clock per call | `RELAY_SHELL_MAX_TIMEOUT` (900 s) | killed; use `shell_spawn` / `ssh_spawn` for longer work |
127
+ | Input size (command, stdin, script, env) | `RELAY_SHELL_MAX_INPUT` (2,097,152 characters) | refused, because the policy scan is synchronous |
128
+ | One-shot command timeout | `RELAY_SHELL_MAX_TIMEOUT` (900 s) | local process group terminated; remote termination is best effort; use `shell_spawn` / `ssh_spawn` for longer work |
113
129
  | Live sessions / forwards | `RELAY_SHELL_MAX_SESSIONS` (64), `RELAY_SHELL_MAX_FORWARDS` (64) | a new one is refused at the cap |
114
130
  | Output returned | `RELAY_SHELL_MAX_OUTPUT` (64 KiB) | truncated, not refused |
115
131
  | HTTP without OAuth on a non-loopback address | `RELAY_SHELL_ALLOW_UNAUTH_NETWORK` | refused at startup unless set |
116
132
 
117
133
  Session output buffers are also bounded, and idle sessions (`RELAY_SHELL_SESSION_IDLE_TIMEOUT`,
118
- 1800 s) and exited, drained sessions are reaped.
134
+ 1800 s) and exited, drained sessions are reaped. Persistent sessions have no
135
+ maximum lifetime. SFTP transfers default to `timeout=0` (no per-call deadline);
136
+ set a positive transfer timeout when needed. After a remote timeout or disconnect,
137
+ reconcile the remote operation before retrying a mutation.
119
138
 
120
139
  ### Making it stricter
121
140
 
@@ -0,0 +1,74 @@
1
+ # Documentation and Pages review — 2026-11-07
2
+
3
+ ## Scope and baseline
4
+
5
+ Public repository baseline: `e7f4256b63477fd2da6bcad8d2437d07cc2c17f5`
6
+ (main), latest release v0.5.2. The inventory contained 54 tracked Markdown
7
+ documents, including contributor/templates, eight site guides, 15 ADR/index
8
+ files, deployment/dependency/fixture READMEs and frozen audit packs.
9
+ [PR #281](https://github.com/rmednitzer/relay-shell/pull/281) carries the corrections.
10
+
11
+ Antigravity was invoked through Vertex for a read-only repository and published
12
+ Pages review. Its completed report supplied a coverage manifest and actionable
13
+ findings. Independent review compared proposed corrections with actual source,
14
+ workflow steps, lock headers and external repository settings. The provider's
15
+ counts, line references and historical descriptions are not authoritative.
16
+ In particular, its claimed missing latest SDK/SSH pin entry in ADR 0001 was
17
+ rejected: the current Consequences section already contains that entry.
18
+
19
+ ## Verified findings and disposition
20
+
21
+ | Area | Correction |
22
+ | --- | --- |
23
+ | Quickstart and agent setup | Use the qualified hashed development profile, then no-dependency editable installation. |
24
+ | Deployment | Replace the superseded manual install with the locked installer's stopped-service procedure and link bounded failure/interruption recovery. |
25
+ | Edge installer | Exported EDGE variables take precedence over the operator file, including explicit empty values; document sudo environment selection. |
26
+ | Audit and resource limits | State crash/degraded-sink limits, audit-before-notice order, character input limits, SFTP/session/remote-timeout exceptions and resource/prompt truncation. |
27
+ | OAuth | Reauthorization also follows grant revocation, reuse detection, resource changes or applicable recovery/migration. |
28
+ | Release/SBOM runbook | Current tags use complete profiles, independent workflow tooling locks and separate runtime/builder/tool environments; preserve historical fallback limits. |
29
+ | Contributor checklists | Include scope inputs and all affected profile/tooling lock regeneration commands. |
30
+ | Release trust | Append dated v0.5.0–v0.5.2 signature, tag-object and main-ancestry evidence; recheck external controls. |
31
+ | Current terminology | Correct MCPServer references, the CLI flag comment, optional HTTP-extra comment and broker operation-key docstring. Executable behavior is unchanged. |
32
+ | Historical runner record | Append a dated current-runner clarification to BACKLOG; preserve the initial qualification and historical ADRs/audit packs. |
33
+ | Vector example | Replace retired repository instructions; declare the metrics source, remove zero-age filtering, use valid compression/routing/buffer settings and handle non-object JSON. Document receiver protocol and rotation/backfill limits. |
34
+ | Pages assets | Use system fonts, removing Google font requests. |
35
+
36
+ ## Validation evidence
37
+
38
+ - Baseline live crawl: all 23 sitemap URLs returned HTTP 200.
39
+ - All 22 registered tools are represented in README and the tool reference.
40
+ - Strict MkDocs builds and documentation-environment `pip check` passed.
41
+ - Relative file-link checks covered the 54 baseline Markdown documents; generated
42
+ file/anchor checks covered all 24 HTML files, including the 404 page.
43
+ - Generated pages contain no Google Fonts requests.
44
+ - Vector 0.61.0's original example failed configuration loading on boolean
45
+ compression. The corrected example passed `vector validate --no-environment`.
46
+ Synthetic VRL cases passed for an object, malformed JSON, a scalar and an array;
47
+ original message/file fields were retained and invalid records gained parse errors.
48
+ The official Linux release archive was checked against the GitHub-reported SHA-256
49
+ `84a27c3ad6b772fdb05bb76ea01a31498bfc8f5e71f32cc78a0d96841f1aa827`
50
+ and its validator ran in a disposable task directory under an unprivileged identity.
51
+ - `pyproject.toml` and pre-commit parsed configuration remain identical to baseline.
52
+ The broker's executable AST remains identical after removing the module docstring.
53
+ - SSH signatures and main ancestry were checked for all seven release tags.
54
+ Ruleset and immutable-release observations are recorded in
55
+ [release trust](../docs/release-trust.md#verification-update-2026-11-07).
56
+ The GitHub `pypi` environment had a required-reviewer rule on this date;
57
+ PyPI account-side trusted-publisher configuration was not independently inspected.
58
+
59
+ The final revision's required CI and Pages deployment receipts belong to PR #281
60
+ and its linked main workflow runs. A submitted job or a green documentation build
61
+ alone does not establish deployment; published content must be checked afterwards.
62
+
63
+ ## Limits and rollback
64
+
65
+ No daemon, edge, collector or operational host was deployed. No release tag or
66
+ package was published. Vector configuration/VRL validation does not establish
67
+ delivery, ACL access, TLS identity, outage backfill or collector recovery.
68
+ Fluent Bit and journal recipes were not exercised against live receivers.
69
+ Historical evidence remains dated evidence; this review does not renew old
70
+ live-host, release-artifact or recovery claims.
71
+
72
+ STPA: no new runtime control action or authority; the changes reconcile existing
73
+ implementation and document limits. Roll back by reverting the documentation,
74
+ comments and site configuration from PR #281, then verify the main Pages redeploy.
@@ -64,10 +64,6 @@ Every tool body is identical in shape (`Relay.run`):
64
64
  (`deployment.md` §6a).
65
65
  4. **Bound** - the body is truncated to the effective output budget (byte
66
66
  safe). An `[exit N]` prefix is added when an exit code is meaningful.
67
- 4a. **Notice** - after the audit record is written, an admitted Tier 2/3 call (or one with
68
- `known_hosts=ignore`) gets one or two trailing `[NOTICE ...]` lines appended to the returned
69
- string; nothing is added to the record ([ADR 0013](adr/0013-open-by-default-with-unsafe-notices.md),
70
- `RELAY_SHELL_UNSAFE_NOTICES`).
71
67
  5. **Audit** - one JSON line: timestamp, tool, tier, denied flag, redacted and
72
68
  length-bounded args, SHA-256 of the final output, output byte length, exit
73
69
  code, plus `request_id`, `client_id` and an `action` marker (`intent`, `confirm_plan`,
@@ -75,6 +71,12 @@ Every tool body is identical in shape (`Relay.run`):
75
71
  `RELAY_SHELL_AUDIT_CHAIN=true` the line additionally carries `seq`/`prev`/`chain`
76
72
  for tamper-evidence ([ADR 0007](adr/0007-audit-hash-chain.md)); default off
77
73
  keeps the record byte-identical.
74
+ 6. **Notice** - after auditing, an admitted Tier 2/3 call (or one with
75
+ `known_hosts=ignore`) gets `[NOTICE ...]` information in its response; nothing
76
+ is added to the audit record ([ADR 0013](adr/0013-open-by-default-with-unsafe-notices.md),
77
+ `RELAY_SHELL_UNSAFE_NOTICES`). `ssh_fanout` keeps its response valid JSON by
78
+ returning notices in a top-level `notices` field when the body is a JSON
79
+ object; errors or truncated bodies fall back to trailing notice lines.
78
80
 
79
81
  The audit file handler validates its opened descriptor at initialization and every
80
82
  rotation reopen. It uses nonblocking open to avoid FIFO stalls, rejects nonregular
@@ -84,8 +86,10 @@ degraded while tool calls continue; existing rotation-file modes are preserved.
84
86
  Regular-file type checks alone do not prove storage durability or constrain an
85
87
  operator-controlled path.
86
88
 
87
- A tool may fail, time out, or be denied, but it always returns a single bounded, audited
88
- string; the one exception is a cancelled call, which is audited and then re-raised.
89
+ Handled tool failures, timeouts and denials return bounded text through the runner;
90
+ a cancelled call is audited and then re-raised. This describes the normal handler
91
+ path, not guaranteed durable evidence after process loss or a degraded sink.
92
+ The optional intent record can expose an execution with no completion record.
89
93
 
90
94
  When `RELAY_SHELL_SECCOMP_NOTIFY=true` (and the host supports it),
91
95
  `Relay.run` also activates a per-call seccomp-notify monitor during step 3: a
@@ -146,8 +150,11 @@ The main `Relay.run` steps above correspond to specific call sites in
146
150
  | 5 Audit | `self.audit.record(...)` from `audit.AuditLogger.record`. |
147
151
 
148
152
  Resource reads (`relay-shell://...`) and prompt fetches (`prompts/get`)
149
- do not flow through `Relay.run` - there is no work to admit, time out,
150
- or truncate. They are still audited with `tool="resource:<name>"` /
153
+ do not flow through `Relay.run`; they do not execute shell work or use its
154
+ command-admission path. Their serialized bodies are byte-safely truncated to
155
+ `app.clamp_output(cfg.max_output)` before hashing and return. An oversized JSON
156
+ resource can therefore contain a truncation marker rather than parseable JSON.
157
+ Successful reads/fetches are still audited with `tool="resource:<name>"` /
151
158
  `tool="prompt:<name>"` and `tier=0` so the operator sees what context
152
159
  the model is pulling in ([ADR 0008](adr/0008-operating-guidance-prompt.md)
153
160
  covers the prompt surface). The `Relay.run` body, the resource handlers,
@@ -170,8 +177,12 @@ produced.
170
177
  connection evicts the least-recently-used entry not in use, and a cache whose entries are
171
178
  all in use (sessions, forwards, running calls) exceeds the cap briefly rather than drop a
172
179
  live connection. `RELAY_SHELL_MAX_FORWARDS` (default 64) caps active forwards.
173
- - Every tool clamps its own timeout and output to the configured limits;
174
- background/blocking filesystem work is offloaded with `asyncio.to_thread`.
180
+ - One-shot command tools clamp timeouts and output to the configured limits.
181
+ SFTP transfers default to `timeout=0` (no per-call deadline); positive transfer
182
+ timeouts are clamped. Persistent PTY lifetime is governed by explicit closure
183
+ and idle reaping, not the one-shot command timeout. A remote timeout is not
184
+ proof that remote effects stopped; reconcile before retrying mutations.
185
+ Background/blocking filesystem work is offloaded with `asyncio.to_thread`.
175
186
 
176
187
  ## Transports
177
188