relay-shell 0.5.0__tar.gz → 0.5.2__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.
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/workflows/ci.yml +1 -1
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/workflows/codeql.yml +5 -5
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/workflows/copilot-setup-steps.yml +1 -1
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/workflows/docs.yml +1 -1
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/workflows/nightly-fuzz.yml +1 -1
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/workflows/pip-audit.yml +3 -2
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/workflows/release-rehearsal.yml +4 -4
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/workflows/release.yml +4 -1
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/workflows/sbom.yml +1 -1
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.pre-commit-config.yaml +1 -1
- {relay_shell-0.5.0 → relay_shell-0.5.2}/CHANGELOG.md +39 -1
- {relay_shell-0.5.0 → relay_shell-0.5.2}/PKG-INFO +3 -3
- {relay_shell-0.5.0 → relay_shell-0.5.2}/README.md +1 -1
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/install.sh +6 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/adr/0003-tiered-authority.md +23 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/adr/0007-audit-hash-chain.md +36 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/architecture.md +25 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/index.md +1 -1
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/runbook.md +33 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/pyproject.toml +2 -2
- {relay_shell-0.5.0 → relay_shell-0.5.2}/renovate.json5 +3 -2
- {relay_shell-0.5.0 → relay_shell-0.5.2}/requirements/README.md +12 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/requirements/development-py3.12.txt +22 -22
- {relay_shell-0.5.0 → relay_shell-0.5.2}/requirements/development-py3.14.txt +22 -22
- relay_shell-0.5.2/requirements-bootstrap.in +1 -0
- relay_shell-0.5.2/requirements-bootstrap.txt +6 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/requirements.txt +1 -1
- {relay_shell-0.5.0 → relay_shell-0.5.2}/scripts/rehearse_install.py +27 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/scripts/rehearse_upgrade.py +123 -12
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/__init__.py +1 -1
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/audit.py +63 -28
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/patterns.py +72 -26
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/policy.py +10 -9
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/fixtures/upgrade-v0.3/README.md +21 -3
- relay_shell-0.5.2/tests/fixtures/upgrade-v0.3/advisory-description-hashes.json +14 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/fixtures/upgrade-v0.3/baseline.json +2 -1
- relay_shell-0.5.2/tests/test_audit_sink.py +273 -0
- relay_shell-0.5.2/tests/test_classifier_scan_gates.py +101 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_deploy_packaging.py +36 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_install_rehearsal.py +21 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_patterns.py +2 -2
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_upgrade_rehearsal.py +70 -15
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.claude/settings.json +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.editorconfig +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.env.example +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/CODEOWNERS +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/ISSUE_TEMPLATE/bug.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/ISSUE_TEMPLATE/feature.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/ISSUE_TEMPLATE/security.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/copilot-instructions.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/workflows/ci-gate.yml +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/workflows/dependency-review.yml +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.github/workflows/gitleaks.yml +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.gitignore +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/.gitleaks.toml +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/AGENTS.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/BACKLOG.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/CLAUDE.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/CODE_OF_CONDUCT.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/CONTRIBUTING.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/LICENSE +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/NOTICE +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/SECURITY.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/audit/00-inventory.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/audit/01-baseline.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/audit/02-security-findings.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/audit/03-final-report.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/audit/2026-05-27-engagement.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/audit/2026-06-01-engagement.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/audit/2026-06-21-adversarial-engagement.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/audit/2026-06-21-engagement.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/audit/2026-07-15-adversarial-engagement.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/audit/2026-07-15-engagement.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/audit/2026-07-15-release-v0.3.0.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/audit/2026-08-04-backlog-followup.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/audit/2026-08-04-engagement.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/audit/2026-10-14-engagement.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/Caddyfile +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/evidence/README.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/evidence/approved-resets.json.example +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/evidence/audit-evidence.env.example +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/evidence/relay-audit-evidence-sync +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/evidence/relay-audit-evidence.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/evidence/relay-audit-evidence.service +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/evidence/relay-audit-evidence.timer +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/install-edge.sh +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/logrotate/relay-shell +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/monitoring/PACKAGE.lock +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/monitoring/README.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/monitoring/nftables.conf.snippet +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/monitoring/prometheus-node-exporter.service.d/override.conf +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/monitoring/relay-health-metrics.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/monitoring/relay-health-metrics.service +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/monitoring/relay-health-metrics.timer +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/monitoring/relay-monitoring.env.example +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/release-signing.pub +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/systemd/relay-shell.service +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/deploy/systemd/relay-shell.service.d/hardening.conf +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/adr/0001-runtime-and-sdk.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/adr/0002-no-sandbox-full-access.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/adr/0004-edge-tls-automation.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/adr/0005-codebase-validation.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/adr/0006-seccomp-notify-audit-channel.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/adr/0008-operating-guidance-prompt.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/adr/0009-tier3-confirmation-broker.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/adr/0010-rollback-verify-broker.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/adr/0011-windows-openssh-powershell.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/adr/0012-cancel-safe-session-teardown.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/adr/0013-open-by-default-with-unsafe-notices.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/adr/0014-seccomp-launch-and-install-recovery.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/adr/README.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/audit-shipper.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/auth.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/deployment.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/release-trust.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/docs/tools.md +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/mkdocs.yml +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/requirements/runtime-py3.12.txt +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/requirements/runtime-py3.14.txt +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/requirements-audit.in +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/requirements-audit.txt +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/requirements-build.in +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/requirements-build.txt +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/requirements-docs.txt +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/requirements-sbom.in +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/requirements-sbom.txt +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/scripts/check_ci_checks.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/scripts/healthcheck.sh +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/scripts/installer_transaction.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/scripts/reconcile_runtime.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/scripts/rehearse_release.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/__main__.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/auth/__init__.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/auth/admin.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/auth/oauth.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/auth/resource.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/broker.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/config.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/errors.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/inventory.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/metrics.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/notices.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/py.typed +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/redaction.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/seccomp.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/server.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/sessions.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/shelltools.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/sshpool.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/util.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/src/relay_shell/verifier.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/conftest.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/fixtures/upgrade-v0.3/advisory-description.txt +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/fixtures/upgrade-v0.3/runtime.in +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/fixtures/upgrade-v0.3/runtime.lock +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/helpers.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_a14_rs.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_a14_t1_gaps.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_admission_guards.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_audit.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_audit_lows.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_audit_tail_tool.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_authenticated_audit.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_broker.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_broker_client_binding.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_cancellation_audit.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_ci_checks.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_ci_gate.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_classifier_v12.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_classifier_v16.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_config.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_config_hygiene.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_config_template.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_dependency_floors.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_edge_routing.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_errors.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_evidence_deploy.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_fuzz.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_healthcheck.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_install_edge_validation.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_installer_transaction.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_inventory.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_logrotate_config.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_main.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_metrics.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_notices.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_oauth.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_oauth_approval.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_oauth_resource.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_oauth_reuse.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_oauth_store.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_policy.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_prompts.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_redaction.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_redaction_operators.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_redaction_v16.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_release_admission.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_release_workflows.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_resources.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_root_execution_contract.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_runtime_inventory.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_seccomp.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_server.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_session_secret_audit.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_sessions.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_sessions_cancel.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_shell.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_ssh_fanout_tool.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_ssh_integration.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_ssh_keyscan_tool.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_sshpool_unit.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_stdio_e2e.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_tool_wrappers.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_util.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_verifier.py +0 -0
- {relay_shell-0.5.0 → relay_shell-0.5.2}/tests/test_workflow_hygiene.py +0 -0
|
@@ -41,7 +41,7 @@ jobs:
|
|
|
41
41
|
with:
|
|
42
42
|
persist-credentials: false
|
|
43
43
|
|
|
44
|
-
- uses: actions/setup-python@
|
|
44
|
+
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7
|
|
45
45
|
with:
|
|
46
46
|
python-version: ${{ matrix.python-version }}
|
|
47
47
|
cache: "pip"
|
|
@@ -38,15 +38,15 @@ jobs:
|
|
|
38
38
|
persist-credentials: false
|
|
39
39
|
|
|
40
40
|
- name: Initialize CodeQL
|
|
41
|
-
uses: github/codeql-action/init@
|
|
41
|
+
uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4
|
|
42
42
|
with:
|
|
43
43
|
languages: ${{ matrix.language }}
|
|
44
44
|
|
|
45
45
|
- name: Autobuild
|
|
46
|
-
uses: github/codeql-action/autobuild@
|
|
46
|
+
uses: github/codeql-action/autobuild@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4
|
|
47
47
|
|
|
48
48
|
- name: Perform CodeQL Analysis
|
|
49
|
-
uses: github/codeql-action/analyze@
|
|
49
|
+
uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # 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@
|
|
66
|
+
uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # 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@
|
|
72
|
+
uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4
|
|
73
73
|
with:
|
|
74
74
|
category: "/language:actions"
|
|
@@ -27,7 +27,7 @@ jobs:
|
|
|
27
27
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
28
28
|
with:
|
|
29
29
|
persist-credentials: false
|
|
30
|
-
- uses: actions/setup-python@
|
|
30
|
+
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7
|
|
31
31
|
with:
|
|
32
32
|
python-version: "3.14"
|
|
33
33
|
- name: Prepare the constrained development environment
|
|
@@ -34,7 +34,7 @@ jobs:
|
|
|
34
34
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
35
35
|
with:
|
|
36
36
|
persist-credentials: false
|
|
37
|
-
- uses: actions/setup-python@
|
|
37
|
+
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7
|
|
38
38
|
with:
|
|
39
39
|
python-version: "3.12"
|
|
40
40
|
cache: pip
|
|
@@ -38,7 +38,7 @@ jobs:
|
|
|
38
38
|
with:
|
|
39
39
|
persist-credentials: false
|
|
40
40
|
|
|
41
|
-
- uses: actions/setup-python@
|
|
41
|
+
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7
|
|
42
42
|
with:
|
|
43
43
|
python-version: "3.14"
|
|
44
44
|
cache: "pip"
|
|
@@ -70,7 +70,7 @@ jobs:
|
|
|
70
70
|
# still audited, and pip-audit still exits non-zero on any CVE found.
|
|
71
71
|
run: .audit-tool/bin/pip-audit --path "$AUDIT_SITE_PACKAGES" --skip-editable
|
|
72
72
|
|
|
73
|
-
- name: Audit the hash-locked release and SBOM tooling
|
|
73
|
+
- name: Audit the hash-locked bootstrap, release and SBOM tooling
|
|
74
74
|
# requirements-build.txt / requirements-sbom.txt feed the jobs that build,
|
|
75
75
|
# sign and publish (A14-D7); they are not part of the package's own tree, so
|
|
76
76
|
# audit them explicitly. They are fully pinned, so no resolution is needed.
|
|
@@ -78,4 +78,5 @@ jobs:
|
|
|
78
78
|
.audit-tool/bin/pip-audit --disable-pip --no-deps -r requirements-build.txt
|
|
79
79
|
.audit-tool/bin/pip-audit --disable-pip --no-deps -r requirements-sbom.txt
|
|
80
80
|
.audit-tool/bin/pip-audit --disable-pip --no-deps -r requirements-audit.txt
|
|
81
|
+
.audit-tool/bin/pip-audit --disable-pip --no-deps -r requirements-bootstrap.txt
|
|
81
82
|
|
|
@@ -31,7 +31,7 @@ jobs:
|
|
|
31
31
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
32
32
|
with:
|
|
33
33
|
persist-credentials: false
|
|
34
|
-
- uses: actions/setup-python@
|
|
34
|
+
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7
|
|
35
35
|
with:
|
|
36
36
|
python-version: "3.14"
|
|
37
37
|
- name: Prepare the locked rehearsal driver
|
|
@@ -65,14 +65,14 @@ jobs:
|
|
|
65
65
|
with:
|
|
66
66
|
persist-credentials: false
|
|
67
67
|
fetch-depth: 0
|
|
68
|
-
- uses: actions/setup-python@
|
|
68
|
+
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7
|
|
69
69
|
id: build-python
|
|
70
70
|
with:
|
|
71
71
|
python-version: "3.14"
|
|
72
72
|
- name: Prepare hash-locked build tools
|
|
73
73
|
run: |
|
|
74
74
|
python -m pip install --only-binary=:all: --require-hashes -r requirements/development-py3.12.txt
|
|
75
|
-
- uses: actions/setup-python@
|
|
75
|
+
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7
|
|
76
76
|
with:
|
|
77
77
|
python-version: "3.14"
|
|
78
78
|
- name: Prepare independent audit tools with their locked interpreter
|
|
@@ -87,7 +87,7 @@ jobs:
|
|
|
87
87
|
set -e
|
|
88
88
|
test "$audit_result" -eq 0 || test "$audit_result" -eq 1
|
|
89
89
|
test -s "$RUNNER_TEMP/legacy-audit.json"
|
|
90
|
-
- uses: actions/setup-python@
|
|
90
|
+
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7
|
|
91
91
|
id: legacy-python
|
|
92
92
|
with:
|
|
93
93
|
python-version: "3.12"
|
|
@@ -161,7 +161,7 @@ jobs:
|
|
|
161
161
|
ref: ${{ needs.verify.outputs.release_commit }}
|
|
162
162
|
persist-credentials: false
|
|
163
163
|
|
|
164
|
-
- uses: actions/setup-python@
|
|
164
|
+
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7
|
|
165
165
|
with:
|
|
166
166
|
python-version: "3.14"
|
|
167
167
|
# No pip cache on the release path: nothing restored from a shared
|
|
@@ -201,6 +201,9 @@ jobs:
|
|
|
201
201
|
python -m pip check
|
|
202
202
|
|
|
203
203
|
- name: Run full test suite
|
|
204
|
+
env:
|
|
205
|
+
# Keep generated test data outside the source distribution input tree.
|
|
206
|
+
HYPOTHESIS_STORAGE_DIRECTORY: ${{ runner.temp }}/release-hypothesis
|
|
204
207
|
run: pytest -q
|
|
205
208
|
|
|
206
209
|
- name: Build wheel + sdist
|
|
@@ -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.
|
|
18
|
+
rev: v0.17.2
|
|
19
19
|
hooks:
|
|
20
20
|
- id: ruff
|
|
21
21
|
args: [--fix]
|
|
@@ -6,6 +6,42 @@ All notable changes to this project are documented here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.5.2] - 2026-11-02
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Validate the actual audit descriptor before initial permission hardening and on
|
|
14
|
+
rotation reopens. Unsupported devices/FIFOs now degrade to stderr without
|
|
15
|
+
metadata changes or blocking; regular append sinks retain their behavior.
|
|
16
|
+
- Validate regular descriptors for audit-tail and verification reads;
|
|
17
|
+
handle invalid paths and unavailable home directories through audit fallback.
|
|
18
|
+
- Report rotation-open failures through audit health and preserve the first
|
|
19
|
+
degraded reason. Skip chain-tail reads after sink initialization fails.
|
|
20
|
+
- Preserve operator-created rotation-file modes; newly created files remain
|
|
21
|
+
private. See [ADR 0007](docs/adr/0007-audit-hash-chain.md#audit-sink-descriptor-validation-2026-11-02).
|
|
22
|
+
|
|
23
|
+
## [0.5.1] - 2026-11-02
|
|
24
|
+
|
|
25
|
+
### Performance
|
|
26
|
+
|
|
27
|
+
- Avoid unnecessary delete/format scans and privilege-context parsing; use an
|
|
28
|
+
ASCII fast path while preserving Unicode input and published-table overrides.
|
|
29
|
+
Keep classification semantics and timing thresholds unchanged. See
|
|
30
|
+
[ADR 0003](docs/adr/0003-tiered-authority.md#2026-11-02-equivalent-scan-gates-patterns-version-17).
|
|
31
|
+
|
|
32
|
+
### Security
|
|
33
|
+
|
|
34
|
+
- Hash-pin and audit installer pip independently; upgrade both the disposable
|
|
35
|
+
builder and transaction-protected runtime before installing dependency closures.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- Refresh Ruff, virtualenv development tooling and pinned GitHub Actions.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- Keep release Hypothesis caches outside package source inputs.
|
|
44
|
+
|
|
9
45
|
## [0.5.0] - 2026-10-22
|
|
10
46
|
|
|
11
47
|
- Change edge-installer precedence: explicitly exported `RELAY_SHELL_EDGE_*` values override the operator environment file; explicitly empty optional values now fail validation instead of selecting defaults. Review exported values before reinstalling the edge.
|
|
@@ -1793,7 +1829,9 @@ All notable changes to this project are documented here. The format follows
|
|
|
1793
1829
|
bundled logrotate config drops and restores the append-only attribute
|
|
1794
1830
|
across rotation. See `docs/deployment.md` §6.
|
|
1795
1831
|
|
|
1796
|
-
[Unreleased]: https://github.com/rmednitzer/relay-shell/compare/v0.5.
|
|
1832
|
+
[Unreleased]: https://github.com/rmednitzer/relay-shell/compare/v0.5.2...HEAD
|
|
1833
|
+
[0.5.2]: https://github.com/rmednitzer/relay-shell/compare/v0.5.1...v0.5.2
|
|
1834
|
+
[0.5.1]: https://github.com/rmednitzer/relay-shell/compare/v0.5.0...v0.5.1
|
|
1797
1835
|
[0.5.0]: https://github.com/rmednitzer/relay-shell/compare/v0.4.0...v0.5.0
|
|
1798
1836
|
[0.4.0]: https://github.com/rmednitzer/relay-shell/compare/v0.3.0...v0.4.0
|
|
1799
1837
|
[0.3.0]: https://github.com/rmednitzer/relay-shell/compare/v0.2.0...v0.3.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: relay-shell
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
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,13 +36,13 @@ 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.
|
|
39
|
+
Requires-Dist: ruff==0.17.2; extra == 'dev'
|
|
40
40
|
Provides-Extra: http
|
|
41
41
|
Description-Content-Type: text/markdown
|
|
42
42
|
|
|
43
43
|
# Relay Shell
|
|
44
44
|
|
|
45
|
-
v0.5.
|
|
45
|
+
v0.5.2 · Python 3.12 / 3.14 · transports `stdio` and `streamable-http` · MCP SDK
|
|
46
46
|
`mcp>=2,<3` · Apache-2.0
|
|
47
47
|
|
|
48
48
|
`relay-shell` is a [Model Context Protocol](https://modelcontextprotocol.io) server that gives
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Relay Shell
|
|
2
2
|
|
|
3
|
-
v0.5.
|
|
3
|
+
v0.5.2 · Python 3.12 / 3.14 · transports `stdio` and `streamable-http` · MCP SDK
|
|
4
4
|
`mcp>=2,<3` · Apache-2.0
|
|
5
5
|
|
|
6
6
|
`relay-shell` is a [Model Context Protocol](https://modelcontextprotocol.io) server that gives
|
|
@@ -129,6 +129,8 @@ esac
|
|
|
129
129
|
RUNTIME_LOCK="$SRC_DIR/requirements/runtime-py$PYTHON_PROFILE.txt"
|
|
130
130
|
[ -r "$RUNTIME_LOCK" ] || die "runtime dependency lock missing"
|
|
131
131
|
[ -r "$SRC_DIR/requirements-build.txt" ] || die "build tooling lock missing"
|
|
132
|
+
BOOTSTRAP_LOCK="$SRC_DIR/requirements-bootstrap.txt"
|
|
133
|
+
[ -r "$BOOTSTRAP_LOCK" ] || die "installer bootstrap lock missing"
|
|
132
134
|
|
|
133
135
|
log "Ensuring service account 'relay-shell'"
|
|
134
136
|
if ! id relay-shell >/dev/null 2>&1; then
|
|
@@ -139,6 +141,8 @@ fi
|
|
|
139
141
|
# Build with locked tooling in an owned disposable environment, not in the runtime.
|
|
140
142
|
BUILD_DIR=$(sudo -u relay-shell mktemp -d "$PREFIX/.build-XXXXXXXX")
|
|
141
143
|
sudo -u relay-shell "$INSTALL_PYTHON" -m venv "$BUILD_DIR/venv"
|
|
144
|
+
sudo -u relay-shell "$BUILD_DIR/venv/bin/python" -m pip install --quiet \
|
|
145
|
+
--only-binary=:all: --no-deps --require-hashes -r "$BOOTSTRAP_LOCK"
|
|
142
146
|
sudo -u relay-shell "$BUILD_DIR/venv/bin/python" -m pip install --quiet \
|
|
143
147
|
--only-binary=:all: --require-hashes -r "$SRC_DIR/requirements-build.txt"
|
|
144
148
|
sudo -u relay-shell "$BUILD_DIR/venv/bin/python" -m build --wheel --no-isolation \
|
|
@@ -165,6 +169,8 @@ log "Creating venv at $PREFIX/venv"
|
|
|
165
169
|
if [ ! -x "$PREFIX/venv/bin/python" ]; then
|
|
166
170
|
sudo -u relay-shell python3 -m venv "$PREFIX/venv"
|
|
167
171
|
fi
|
|
172
|
+
sudo -u relay-shell "$PREFIX/venv/bin/python" -m pip install --quiet \
|
|
173
|
+
--only-binary=:all: --no-deps --require-hashes -r "$BOOTSTRAP_LOCK"
|
|
168
174
|
sudo -u relay-shell "$PREFIX/venv/bin/python" -m pip install --quiet \
|
|
169
175
|
--only-binary=:all: --require-hashes -r "$RUNTIME_LOCK"
|
|
170
176
|
sudo -u relay-shell "$PREFIX/venv/bin/python" -m pip install --quiet \
|
|
@@ -148,3 +148,26 @@ review.
|
|
|
148
148
|
nothing) the tier and basis now also reach the caller as a trailing `[NOTICE ...]` line for
|
|
149
149
|
Tier 2 and Tier 3 calls. Admission rules are unchanged. See
|
|
150
150
|
[ADR 0013](0013-open-by-default-with-unsafe-notices.md).
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
## 2026-11-02: equivalent scan gates (patterns version 17)
|
|
154
|
+
|
|
155
|
+
This performance repair addresses SC-4/UCA-4 without changing SC-1/SC-2
|
|
156
|
+
classification intent. The risk is a false-negative prefilter (H-1/UCA-1), so
|
|
157
|
+
gates use necessary syntax only: every separated delete rule consumes whitespace
|
|
158
|
+
immediately before a dash or slash; the drive-format rule consumes a colon. The
|
|
159
|
+
complete matcher and optimized matcher share rule sources. Unicode whitespace,
|
|
160
|
+
positive/near-miss fixtures, generated command sequences and arbitrary Unicode
|
|
161
|
+
inputs compare the optimized path to the full matcher. The privilege fast path
|
|
162
|
+
skips context processing only when no original privilege keyword exists; replacing
|
|
163
|
+
whole reader segments with a space cannot create one. Regex heuristics remain
|
|
164
|
+
heuristics, not containment. No new dependency or runtime posture change.
|
|
165
|
+
|
|
166
|
+
Rollback restores the previous complete runtime. Acceptance requires unchanged
|
|
167
|
+
classification on the regression corpus, existing timing limits, full tests and
|
|
168
|
+
fuzz gates; measured improvements are host- and workload-dependent.
|
|
169
|
+
|
|
170
|
+
ASCII-only inputs also use equivalent ASCII regex variants for common Tier-3
|
|
171
|
+
and Tier-2 scans. Inputs containing any non-ASCII character retain the original
|
|
172
|
+
Unicode regexes; replacing a published table disables its optimized variant.
|
|
173
|
+
Differential tests cover both paths.
|
|
@@ -245,3 +245,39 @@ unchained, head-truncated (with and without `--segment`), and genesis logs.
|
|
|
245
245
|
No change to policy admission, tier semantics, the no-sandbox posture, or
|
|
246
246
|
any tool's response shape. This pass hardened a compensating control
|
|
247
247
|
(ADR 0002's first one); it did not move the trust boundary.
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
## Audit sink descriptor validation (2026-11-02)
|
|
251
|
+
|
|
252
|
+
A privileged validation run exposed path-based permission hardening of a device
|
|
253
|
+
before file-type validation. FIFO opens could also block, including chain resume
|
|
254
|
+
after initialization failure, while a rotation-open error could escape the
|
|
255
|
+
handler's degraded-state update.
|
|
256
|
+
|
|
257
|
+
Validate the descriptor used for writes with fstat, both initially and after
|
|
258
|
+
rotation. Open nonblocking so unsupported FIFOs cannot stall initialization or
|
|
259
|
+
reopen. Only the initial regular descriptor is subject to best-effort fchmod;
|
|
260
|
+
preserve existing rotation-file permissions and create missing files at 0600.
|
|
261
|
+
Initialization failure uses the existing stderr fallback and skips chain-tail
|
|
262
|
+
reads. Reopen failures mark degraded through the existing first-reason-wins API.
|
|
263
|
+
|
|
264
|
+
This changes unsupported sink handling, with no record-schema, hash-chain,
|
|
265
|
+
execution-authority or policy-mode change. Control-authority STPA changes are
|
|
266
|
+
not applicable; the operational constraints are nonblocking FIFO failure, no device
|
|
267
|
+
metadata mutation, visible degradation and unchanged valid-file append/rotation.
|
|
268
|
+
Device/symlink, FIFO with/without readers, rotation, path replacement, descriptor
|
|
269
|
+
cleanup and chmod-refusal regressions test these constraints. Qualified Python
|
|
270
|
+
profiles and privileged file-mode evidence must be retained at release.
|
|
271
|
+
|
|
272
|
+
Operator-controlled paths and other filesystem actors remain trusted. This does
|
|
273
|
+
not establish durable storage, fsync guarantees, a hostile-directory boundary or
|
|
274
|
+
independent off-host evidence. Roll back the complete runtime if necessary while
|
|
275
|
+
preserving accepted audit records; no data migration is required.
|
|
276
|
+
|
|
277
|
+
Audit-tail reads (plain and gzip) and chain-verification opens use the same
|
|
278
|
+
regular-descriptor validation. This also prevents a regular-to-FIFO replacement
|
|
279
|
+
from blocking between a path check and the actual read. Invalid paths and an
|
|
280
|
+
unavailable home directory degrade initialization through the existing fallback.
|
|
281
|
+
|
|
282
|
+
O_NONBLOCK is not a general storage or network-filesystem timeout; regular-file
|
|
283
|
+
I/O latency remains governed by the operating system and storage.
|
|
@@ -28,6 +28,13 @@ MCP client (Claude / Inspector / SDK)
|
|
|
28
28
|
\---- SessionRegistry -------/ (unified local + SSH PTYs)
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
Tier classification uses the shared pattern tables. Necessary-syntax gates skip
|
|
32
|
+
delete/format rule families only when their required syntax is absent. The full
|
|
33
|
+
Tier-3 regex remains the reference and replacement contract; replacing it disables
|
|
34
|
+
the built-in optimization. Privilege context is parsed only when a privilege
|
|
35
|
+
keyword is present. These optimizations do not truncate input or alter policy
|
|
36
|
+
precedence.
|
|
37
|
+
|
|
31
38
|
## Request lifecycle
|
|
32
39
|
|
|
33
40
|
Every tool body is identical in shape (`Relay.run`):
|
|
@@ -69,6 +76,14 @@ Every tool body is identical in shape (`Relay.run`):
|
|
|
69
76
|
for tamper-evidence ([ADR 0007](adr/0007-audit-hash-chain.md)); default off
|
|
70
77
|
keeps the record byte-identical.
|
|
71
78
|
|
|
79
|
+
The audit file handler validates its opened descriptor at initialization and every
|
|
80
|
+
rotation reopen. It uses nonblocking open to avoid FIFO stalls, rejects nonregular
|
|
81
|
+
sinks and hardens only the initial regular descriptor. Invalid initialization
|
|
82
|
+
falls back to stderr and skips chain-tail reads. Reopen failures mark audit health
|
|
83
|
+
degraded while tool calls continue; existing rotation-file modes are preserved.
|
|
84
|
+
Regular-file type checks alone do not prove storage durability or constrain an
|
|
85
|
+
operator-controlled path.
|
|
86
|
+
|
|
72
87
|
A tool may fail, time out, or be denied, but it always returns a single bounded, audited
|
|
73
88
|
string; the one exception is a cancelled call, which is audited and then re-raised.
|
|
74
89
|
|
|
@@ -180,3 +195,13 @@ bounds), see [`runbook.md`](runbook.md) §2. The reproducible validation
|
|
|
180
195
|
pass against the upstream `mcp` / `asyncssh` / OAuth surfaces lives in
|
|
181
196
|
[ADR 0005](adr/0005-codebase-validation.md), which records the
|
|
182
197
|
methodology and the most recent pass outcome.
|
|
198
|
+
|
|
199
|
+
ASCII-only inputs also use equivalent ASCII regex variants for common Tier-3
|
|
200
|
+
and Tier-2 scans. Inputs containing any non-ASCII character retain the original
|
|
201
|
+
Unicode regexes; replacing a published table disables its optimized variant.
|
|
202
|
+
Differential tests cover both paths.
|
|
203
|
+
|
|
204
|
+
Audit-tail reads (plain and gzip) and chain-verification opens use the same
|
|
205
|
+
regular-descriptor validation. This also prevents a regular-to-FIFO replacement
|
|
206
|
+
from blocking between a path check and the actual read. Invalid paths and an
|
|
207
|
+
unavailable home directory degrade initialization through the existing fallback.
|
|
@@ -6,7 +6,7 @@ title: Overview
|
|
|
6
6
|
|
|
7
7
|
A [Model Context Protocol](https://modelcontextprotocol.io) server for **complete shell and
|
|
8
8
|
SSH operation**: local commands and scripts, interactive PTY sessions, fleet operation over
|
|
9
|
-
SSH, SFTP transfers and port forwards. Current release: **v0.
|
|
9
|
+
SSH, SFTP transfers and port forwards. Current release: **[v0.5.2](https://github.com/rmednitzer/relay-shell/releases/tag/v0.5.2)** (2026-11-02).
|
|
10
10
|
|
|
11
11
|
It is operator infrastructure for hosts you own and administer. It runs with the privileges
|
|
12
12
|
of its service account, with no sandbox, and it is **open by default**: with the default
|
|
@@ -271,6 +271,14 @@ alertable; any HTTP response (including 401/403/404) means the listener is up.
|
|
|
271
271
|
|
|
272
272
|
### 2.9 Audit-log hygiene
|
|
273
273
|
|
|
274
|
+
Configure a regular audit file. Device/FIFO sinks are unsupported: initialization
|
|
275
|
+
degrades to stderr without changing their permissions or waiting for a reader.
|
|
276
|
+
Each rotation reopen also validates the actual descriptor; failure marks runtime
|
|
277
|
+
audit health degraded. Initial regular-file hardening is best effort (append-only
|
|
278
|
+
files may refuse chmod), and existing operator-created rotation-file modes remain
|
|
279
|
+
unchanged. New files use mode 0600. Do not interpret a regular-file check as a
|
|
280
|
+
proof of durable persistence.
|
|
281
|
+
|
|
274
282
|
On a real deployment:
|
|
275
283
|
|
|
276
284
|
```bash
|
|
@@ -1106,6 +1114,8 @@ caches, signing or publishing rights. `scripts/rehearse_release.py` archives the
|
|
|
1106
1114
|
committed source into a disposable directory and executes the named release
|
|
1107
1115
|
install/build/distribution-check and SBOM preparation/generation shell steps
|
|
1108
1116
|
verbatim. It uses Python 3.14 and the same workflow-revision tooling locks.
|
|
1117
|
+
The release workflow keeps Hypothesis storage under the runner temporary directory
|
|
1118
|
+
so its pre-build test run cannot add generated caches to the source distribution.
|
|
1109
1119
|
The normal two-profile Ubuntu/Python CI matrix runs the full test suite; the rehearsal does
|
|
1110
1120
|
not run it again. Package and actual installer/recovery rehearsals run on both
|
|
1111
1121
|
Ubuntu 24.04 and 26.04, using each host's native Python for the installed runtime. Its receipt binds source, interpreter, distribution/SBOM hashes
|
|
@@ -1184,3 +1194,26 @@ it does not make PR-edited workflows immutable or establish independent human
|
|
|
1184
1194
|
review. Zero required approvals and CODEOWNERS enforcement remain separate policy
|
|
1185
1195
|
choices. Restore settings only from a reviewed prior ruleset, preserving required
|
|
1186
1196
|
check identity; removing an aggregate is not an equivalent rollback.
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
### Classifier scan gates (patterns version 17)
|
|
1200
|
+
|
|
1201
|
+
Tier-3 matching partitions common rules from delete rules requiring whitespace
|
|
1202
|
+
followed by a dash/slash, and format rules requiring a colon. Necessary-syntax
|
|
1203
|
+
gates skip only families that cannot match; the complete regex is assembled from
|
|
1204
|
+
the same sources and retained as a differential-test reference. Privilege-context
|
|
1205
|
+
parsing runs only when the original text contains a privilege keyword. Reader
|
|
1206
|
+
replacement removes whole command segments and cannot introduce such a keyword.
|
|
1207
|
+
No command text is truncated, no flags are normalized and no policy mode, denial,
|
|
1208
|
+
confirmation or audit behavior changes. Keep the existing absolute timing limits
|
|
1209
|
+
and run the complete test suite plus fuzz tests when modifying these gates.
|
|
1210
|
+
|
|
1211
|
+
ASCII-only inputs also use equivalent ASCII regex variants for common Tier-3
|
|
1212
|
+
and Tier-2 scans. Inputs containing any non-ASCII character retain the original
|
|
1213
|
+
Unicode regexes; replacing a published table disables its optimized variant.
|
|
1214
|
+
Differential tests cover both paths.
|
|
1215
|
+
|
|
1216
|
+
Audit-tail reads (plain and gzip) and chain-verification opens use the same
|
|
1217
|
+
regular-descriptor validation. This also prevents a regular-to-FIFO replacement
|
|
1218
|
+
from blocking between a path check and the actual read. Invalid paths and an
|
|
1219
|
+
unavailable home directory degrade initialization through the existing fallback.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "relay-shell"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.2"
|
|
8
8
|
description = "A highly reliable, maximally capable MCP server for complete shell and SSH mastery: a governed relay in the shell/SSH path."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -45,7 +45,7 @@ dev = [
|
|
|
45
45
|
# -e ".[dev]"` without the development lock no longer provides it. Declared explicitly
|
|
46
46
|
# because a package the suite imports should never be a transitive.
|
|
47
47
|
"httpx>=0.28,<1",
|
|
48
|
-
"ruff==0.17.
|
|
48
|
+
"ruff==0.17.2",
|
|
49
49
|
"mypy>=1.13,<3",
|
|
50
50
|
"coverage[toml]>=7.6,<8",
|
|
51
51
|
"pre-commit>=4.0,<5",
|
|
@@ -137,7 +137,8 @@
|
|
|
137
137
|
"requirements-build.txt",
|
|
138
138
|
"requirements-sbom.txt",
|
|
139
139
|
"requirements-audit.txt",
|
|
140
|
-
"requirements/*.txt"
|
|
140
|
+
"requirements/*.txt",
|
|
141
|
+
"requirements-bootstrap.txt"
|
|
141
142
|
],
|
|
142
143
|
"enabled": false
|
|
143
144
|
},
|
|
@@ -196,7 +197,7 @@
|
|
|
196
197
|
},
|
|
197
198
|
"pip-compile": {
|
|
198
199
|
"managerFilePatterns": [
|
|
199
|
-
"/^requirements-(?:build|sbom|audit)\\.txt$/",
|
|
200
|
+
"/^requirements-(?:build|sbom|audit|bootstrap)\\.txt$/",
|
|
200
201
|
"/^requirements/(?:runtime|development)-py3\\.(?:12|14)\\.txt$/"
|
|
201
202
|
]
|
|
202
203
|
}
|
|
@@ -34,3 +34,15 @@ A lock fixes selected package bytes; it does not establish provenance by itself,
|
|
|
34
34
|
absence of vulnerabilities, cross-platform qualification or reproducible native
|
|
35
35
|
compilation. CI exercises each selected profile. The package is installed after
|
|
36
36
|
dependencies with --no-deps; source builds require the separately locked backend.
|
|
37
|
+
|
|
38
|
+
The requirements-bootstrap.txt lock, generated from requirements-bootstrap.in,
|
|
39
|
+
pins installer pip independently of application profiles and build dependencies.
|
|
40
|
+
The installer upgrades this hash-locked, binary-only bootstrap in its disposable
|
|
41
|
+
builder and in the stopped, transaction-protected runtime before installing their
|
|
42
|
+
dependency closures. Pip remains excluded from application-inventory reconciliation;
|
|
43
|
+
that exception does not mean its bootstrap version is unmanaged.
|
|
44
|
+
Regenerate the lock with its recorded command, review hashes/advisories, and run
|
|
45
|
+
the actual installer/recovery rehearsals on both qualified OS/Python profiles.
|
|
46
|
+
The pip-audit workflow audits this lock explicitly; Renovate's pip-compile manager
|
|
47
|
+
owns its generated output. Bootstrap failure retains the installer's existing
|
|
48
|
+
rollback and stopped-service behavior.
|
|
@@ -1522,25 +1522,25 @@ rpds-py==2026.9.1 \
|
|
|
1522
1522
|
# via
|
|
1523
1523
|
# jsonschema
|
|
1524
1524
|
# referencing
|
|
1525
|
-
ruff==0.17.
|
|
1526
|
-
--hash=sha256:
|
|
1527
|
-
--hash=sha256:
|
|
1528
|
-
--hash=sha256:
|
|
1529
|
-
--hash=sha256:
|
|
1530
|
-
--hash=sha256:
|
|
1531
|
-
--hash=sha256:
|
|
1532
|
-
--hash=sha256:
|
|
1533
|
-
--hash=sha256:
|
|
1534
|
-
--hash=sha256:
|
|
1535
|
-
--hash=sha256:
|
|
1536
|
-
--hash=sha256:
|
|
1537
|
-
--hash=sha256:
|
|
1538
|
-
--hash=sha256:
|
|
1539
|
-
--hash=sha256:
|
|
1540
|
-
--hash=sha256:
|
|
1541
|
-
--hash=sha256:
|
|
1542
|
-
--hash=sha256:
|
|
1543
|
-
--hash=sha256:
|
|
1525
|
+
ruff==0.17.2 \
|
|
1526
|
+
--hash=sha256:122258f6ecde0e0dd1bb60b24263c3edde4338caf723fca711cc729f9bfd414a \
|
|
1527
|
+
--hash=sha256:195ad918027f817c119310ca89a42d347887dfc28934aaa869325e9c5dc9d962 \
|
|
1528
|
+
--hash=sha256:1b59d447f197bef9ef48a22ba1a6016137753c6099e9bac9c36eb566e422424c \
|
|
1529
|
+
--hash=sha256:2d086be452eed686aa51271ecf2cbe5037956517cc6ee39a17b4489ac12f8bbe \
|
|
1530
|
+
--hash=sha256:39c5b55c960515cdb32be16e69efb5734c589c06a0eb38a22694524a336b889d \
|
|
1531
|
+
--hash=sha256:583bd67bb9e7061dd09bc46a0a884bae2bbd214dbcd48e509813612619683869 \
|
|
1532
|
+
--hash=sha256:5875a16b0de70b43cb81eabea5ae5e8f5d1b2fd1b5e6ed07f80cfe5019d0bd5a \
|
|
1533
|
+
--hash=sha256:6bce7d1f5165701ae18f5df85c65afe78cddfa6c5fb4b9bdb0014d60388f3edd \
|
|
1534
|
+
--hash=sha256:7cfd783590edf2b77317882631900f6a13ea2e8eacefe6ebf2370d2715299160 \
|
|
1535
|
+
--hash=sha256:7f8fba429d6ac4128ab29cfb8fa6240d2fa671d04adf7f84d3b22fc7b6b6bea4 \
|
|
1536
|
+
--hash=sha256:84d632402d14c23b488f941db23712d6736a013225162062d34558c3f3c780eb \
|
|
1537
|
+
--hash=sha256:8c21b4ab24485358f17291a4f8dc5399b834e963b8efedd920fe4fdb02081bb4 \
|
|
1538
|
+
--hash=sha256:92a80d2f44584dabb0189b3a6c393e37e525e04942838482010575cb81945be7 \
|
|
1539
|
+
--hash=sha256:96a65e755d8dba094707fb23ab602111d9a31ec2835b3a6992a1914a8fcb211e \
|
|
1540
|
+
--hash=sha256:b7d0906677d89bbf3cf73cde64b5bbe2ab35df3ae43e49ee2627301e91265bac \
|
|
1541
|
+
--hash=sha256:ba405cbd5ec7bda1f555d5e52be1fd68d02608d4d95d24591bdac92cfacb579f \
|
|
1542
|
+
--hash=sha256:c555a877f8f41447e318101cfefea9bf12dfa9f82eedf90c376e57e069f917d0 \
|
|
1543
|
+
--hash=sha256:c60f93fec7142dba5b8f487f94bf2f6231dae24dc11e4c48648347c8b0720004
|
|
1544
1544
|
# via
|
|
1545
1545
|
# -c requirements.txt
|
|
1546
1546
|
# relay-shell (pyproject.toml)
|
|
@@ -1630,7 +1630,7 @@ uvicorn==0.54.0 \
|
|
|
1630
1630
|
# -c requirements.txt
|
|
1631
1631
|
# relay-shell (pyproject.toml)
|
|
1632
1632
|
# mcp
|
|
1633
|
-
virtualenv==21.14.
|
|
1634
|
-
--hash=sha256:
|
|
1635
|
-
--hash=sha256:
|
|
1633
|
+
virtualenv==21.14.13 \
|
|
1634
|
+
--hash=sha256:5fbb04138a4861580b78ba9110d529a26bc48fcc93c8ec36bc0202eeed96a788 \
|
|
1635
|
+
--hash=sha256:de5307ba4b9a0001d9c8ea02832d38f63ca432880f416c1bface61c4323fd905
|
|
1636
1636
|
# via pre-commit
|
|
@@ -1522,25 +1522,25 @@ rpds-py==2026.9.1 \
|
|
|
1522
1522
|
# via
|
|
1523
1523
|
# jsonschema
|
|
1524
1524
|
# referencing
|
|
1525
|
-
ruff==0.17.
|
|
1526
|
-
--hash=sha256:
|
|
1527
|
-
--hash=sha256:
|
|
1528
|
-
--hash=sha256:
|
|
1529
|
-
--hash=sha256:
|
|
1530
|
-
--hash=sha256:
|
|
1531
|
-
--hash=sha256:
|
|
1532
|
-
--hash=sha256:
|
|
1533
|
-
--hash=sha256:
|
|
1534
|
-
--hash=sha256:
|
|
1535
|
-
--hash=sha256:
|
|
1536
|
-
--hash=sha256:
|
|
1537
|
-
--hash=sha256:
|
|
1538
|
-
--hash=sha256:
|
|
1539
|
-
--hash=sha256:
|
|
1540
|
-
--hash=sha256:
|
|
1541
|
-
--hash=sha256:
|
|
1542
|
-
--hash=sha256:
|
|
1543
|
-
--hash=sha256:
|
|
1525
|
+
ruff==0.17.2 \
|
|
1526
|
+
--hash=sha256:122258f6ecde0e0dd1bb60b24263c3edde4338caf723fca711cc729f9bfd414a \
|
|
1527
|
+
--hash=sha256:195ad918027f817c119310ca89a42d347887dfc28934aaa869325e9c5dc9d962 \
|
|
1528
|
+
--hash=sha256:1b59d447f197bef9ef48a22ba1a6016137753c6099e9bac9c36eb566e422424c \
|
|
1529
|
+
--hash=sha256:2d086be452eed686aa51271ecf2cbe5037956517cc6ee39a17b4489ac12f8bbe \
|
|
1530
|
+
--hash=sha256:39c5b55c960515cdb32be16e69efb5734c589c06a0eb38a22694524a336b889d \
|
|
1531
|
+
--hash=sha256:583bd67bb9e7061dd09bc46a0a884bae2bbd214dbcd48e509813612619683869 \
|
|
1532
|
+
--hash=sha256:5875a16b0de70b43cb81eabea5ae5e8f5d1b2fd1b5e6ed07f80cfe5019d0bd5a \
|
|
1533
|
+
--hash=sha256:6bce7d1f5165701ae18f5df85c65afe78cddfa6c5fb4b9bdb0014d60388f3edd \
|
|
1534
|
+
--hash=sha256:7cfd783590edf2b77317882631900f6a13ea2e8eacefe6ebf2370d2715299160 \
|
|
1535
|
+
--hash=sha256:7f8fba429d6ac4128ab29cfb8fa6240d2fa671d04adf7f84d3b22fc7b6b6bea4 \
|
|
1536
|
+
--hash=sha256:84d632402d14c23b488f941db23712d6736a013225162062d34558c3f3c780eb \
|
|
1537
|
+
--hash=sha256:8c21b4ab24485358f17291a4f8dc5399b834e963b8efedd920fe4fdb02081bb4 \
|
|
1538
|
+
--hash=sha256:92a80d2f44584dabb0189b3a6c393e37e525e04942838482010575cb81945be7 \
|
|
1539
|
+
--hash=sha256:96a65e755d8dba094707fb23ab602111d9a31ec2835b3a6992a1914a8fcb211e \
|
|
1540
|
+
--hash=sha256:b7d0906677d89bbf3cf73cde64b5bbe2ab35df3ae43e49ee2627301e91265bac \
|
|
1541
|
+
--hash=sha256:ba405cbd5ec7bda1f555d5e52be1fd68d02608d4d95d24591bdac92cfacb579f \
|
|
1542
|
+
--hash=sha256:c555a877f8f41447e318101cfefea9bf12dfa9f82eedf90c376e57e069f917d0 \
|
|
1543
|
+
--hash=sha256:c60f93fec7142dba5b8f487f94bf2f6231dae24dc11e4c48648347c8b0720004
|
|
1544
1544
|
# via
|
|
1545
1545
|
# -c requirements.txt
|
|
1546
1546
|
# relay-shell (pyproject.toml)
|
|
@@ -1626,7 +1626,7 @@ uvicorn==0.54.0 \
|
|
|
1626
1626
|
# -c requirements.txt
|
|
1627
1627
|
# relay-shell (pyproject.toml)
|
|
1628
1628
|
# mcp
|
|
1629
|
-
virtualenv==21.14.
|
|
1630
|
-
--hash=sha256:
|
|
1631
|
-
--hash=sha256:
|
|
1629
|
+
virtualenv==21.14.13 \
|
|
1630
|
+
--hash=sha256:5fbb04138a4861580b78ba9110d529a26bc48fcc93c8ec36bc0202eeed96a788 \
|
|
1631
|
+
--hash=sha256:de5307ba4b9a0001d9c8ea02832d38f63ca432880f416c1bface61c4323fd905
|
|
1632
1632
|
# via pre-commit
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pip>=26.2.2,<27
|