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.
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.env.example +1 -1
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/ISSUE_TEMPLATE/feature.md +1 -1
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/PULL_REQUEST_TEMPLATE.md +4 -2
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/copilot-instructions.md +1 -1
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/ci.yml +1 -1
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/codeql.yml +5 -5
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/docs.yml +2 -2
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.pre-commit-config.yaml +5 -5
- {relay_shell-0.5.2 → relay_shell-0.5.3}/AGENTS.md +1 -1
- {relay_shell-0.5.2 → relay_shell-0.5.3}/BACKLOG.md +5 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/CHANGELOG.md +20 -1
- {relay_shell-0.5.2 → relay_shell-0.5.3}/CONTRIBUTING.md +3 -3
- {relay_shell-0.5.2 → relay_shell-0.5.3}/PKG-INFO +30 -11
- {relay_shell-0.5.2 → relay_shell-0.5.3}/README.md +28 -9
- relay_shell-0.5.3/audit/2026-11-07-documentation-review.md +74 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/architecture.md +21 -10
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/audit-shipper.md +51 -30
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/auth.md +7 -5
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/deployment.md +50 -23
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/index.md +12 -4
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/release-trust.md +39 -3
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/runbook.md +63 -25
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/tools.md +5 -2
- {relay_shell-0.5.2 → relay_shell-0.5.3}/mkdocs.yml +3 -4
- {relay_shell-0.5.2 → relay_shell-0.5.3}/pyproject.toml +5 -5
- {relay_shell-0.5.2 → relay_shell-0.5.3}/renovate.json5 +24 -1
- {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements/development-py3.12.txt +506 -499
- {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements/development-py3.14.txt +506 -499
- relay_shell-0.5.3/requirements/development.in +13 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements/runtime-py3.12.txt +186 -182
- {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements/runtime-py3.14.txt +186 -182
- relay_shell-0.5.3/requirements/runtime.in +8 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-audit.txt +107 -107
- relay_shell-0.5.3/requirements-bootstrap.txt +6 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-build.txt +9 -9
- {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-docs.txt +1 -1
- {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-sbom.txt +19 -19
- {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements.txt +8 -8
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/__init__.py +1 -1
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/audit.py +1 -1
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/broker.py +1 -1
- relay_shell-0.5.3/tests/test_audit_rotation_names.py +34 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_release_workflows.py +39 -0
- relay_shell-0.5.2/requirements-bootstrap.txt +0 -6
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.claude/settings.json +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.editorconfig +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/CODEOWNERS +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/ISSUE_TEMPLATE/bug.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/ISSUE_TEMPLATE/security.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/ci-gate.yml +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/copilot-setup-steps.yml +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/dependency-review.yml +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/gitleaks.yml +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/nightly-fuzz.yml +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/pip-audit.yml +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/release-rehearsal.yml +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/release.yml +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.github/workflows/sbom.yml +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.gitignore +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/.gitleaks.toml +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/CLAUDE.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/CODE_OF_CONDUCT.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/LICENSE +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/NOTICE +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/SECURITY.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/00-inventory.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/01-baseline.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/02-security-findings.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/03-final-report.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-05-27-engagement.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-06-01-engagement.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-06-21-adversarial-engagement.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-06-21-engagement.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-07-15-adversarial-engagement.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-07-15-engagement.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-07-15-release-v0.3.0.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-08-04-backlog-followup.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-08-04-engagement.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/audit/2026-10-14-engagement.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/Caddyfile +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/evidence/README.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/evidence/approved-resets.json.example +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/evidence/audit-evidence.env.example +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/evidence/relay-audit-evidence-sync +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/evidence/relay-audit-evidence.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/evidence/relay-audit-evidence.service +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/evidence/relay-audit-evidence.timer +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/install-edge.sh +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/install.sh +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/logrotate/relay-shell +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/PACKAGE.lock +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/README.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/nftables.conf.snippet +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/prometheus-node-exporter.service.d/override.conf +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/relay-health-metrics.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/relay-health-metrics.service +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/relay-health-metrics.timer +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/monitoring/relay-monitoring.env.example +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/release-signing.pub +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/systemd/relay-shell.service +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/deploy/systemd/relay-shell.service.d/hardening.conf +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0001-runtime-and-sdk.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0002-no-sandbox-full-access.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0003-tiered-authority.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0004-edge-tls-automation.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0005-codebase-validation.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0006-seccomp-notify-audit-channel.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0007-audit-hash-chain.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0008-operating-guidance-prompt.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0009-tier3-confirmation-broker.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0010-rollback-verify-broker.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0011-windows-openssh-powershell.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0012-cancel-safe-session-teardown.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0013-open-by-default-with-unsafe-notices.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/0014-seccomp-launch-and-install-recovery.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/docs/adr/README.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements/README.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-audit.in +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-bootstrap.in +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-build.in +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/requirements-sbom.in +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/scripts/check_ci_checks.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/scripts/healthcheck.sh +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/scripts/installer_transaction.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/scripts/reconcile_runtime.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/scripts/rehearse_install.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/scripts/rehearse_release.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/scripts/rehearse_upgrade.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/__main__.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/auth/__init__.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/auth/admin.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/auth/oauth.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/auth/resource.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/config.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/errors.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/inventory.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/metrics.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/notices.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/patterns.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/policy.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/py.typed +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/redaction.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/seccomp.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/server.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/sessions.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/shelltools.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/sshpool.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/util.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/src/relay_shell/verifier.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/conftest.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/fixtures/upgrade-v0.3/README.md +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/fixtures/upgrade-v0.3/advisory-description-hashes.json +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/fixtures/upgrade-v0.3/advisory-description.txt +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/fixtures/upgrade-v0.3/baseline.json +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/fixtures/upgrade-v0.3/runtime.in +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/fixtures/upgrade-v0.3/runtime.lock +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/helpers.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_a14_rs.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_a14_t1_gaps.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_admission_guards.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_audit.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_audit_lows.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_audit_sink.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_audit_tail_tool.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_authenticated_audit.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_broker.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_broker_client_binding.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_cancellation_audit.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_ci_checks.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_ci_gate.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_classifier_scan_gates.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_classifier_v12.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_classifier_v16.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_config.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_config_hygiene.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_config_template.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_dependency_floors.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_deploy_packaging.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_edge_routing.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_errors.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_evidence_deploy.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_fuzz.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_healthcheck.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_install_edge_validation.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_install_rehearsal.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_installer_transaction.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_inventory.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_logrotate_config.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_main.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_metrics.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_notices.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_oauth.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_oauth_approval.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_oauth_resource.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_oauth_reuse.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_oauth_store.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_patterns.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_policy.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_prompts.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_redaction.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_redaction_operators.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_redaction_v16.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_release_admission.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_resources.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_root_execution_contract.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_runtime_inventory.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_seccomp.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_server.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_session_secret_audit.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_sessions.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_sessions_cancel.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_shell.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_ssh_fanout_tool.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_ssh_integration.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_ssh_keyscan_tool.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_sshpool_unit.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_stdio_e2e.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_tool_wrappers.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_upgrade_rehearsal.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_util.py +0 -0
- {relay_shell-0.5.2 → relay_shell-0.5.3}/tests/test_verifier.py +0 -0
- {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;
|
|
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
|
|
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
|
-
- [ ]
|
|
41
|
-
|
|
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:
|
|
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.
|
|
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@
|
|
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@
|
|
46
|
+
uses: github/codeql-action/autobuild@33d7b85da1a04171e211befe2c739303b9a4c066 # v4
|
|
47
47
|
|
|
48
48
|
- name: Perform CodeQL Analysis
|
|
49
|
-
uses: github/codeql-action/analyze@
|
|
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@
|
|
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@
|
|
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-
|
|
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-
|
|
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
|
|
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.
|
|
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,
|
|
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
|
|
31
|
-
#
|
|
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:
|
|
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.
|
|
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`
|
|
103
|
-
| Change
|
|
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` (
|
|
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.
|
|
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.
|
|
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
|
-
- **
|
|
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
|
-
- **
|
|
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
|
|
113
|
-
|
|
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
|
-
|
|
149
|
-
|
|
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
|
|
154
|
-
|
|
|
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
|
-
- **
|
|
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
|
-
- **
|
|
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
|
|
71
|
-
|
|
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
|
-
|
|
107
|
-
|
|
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
|
|
112
|
-
|
|
|
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
|
-
|
|
88
|
-
|
|
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
|
|
150
|
-
|
|
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
|
-
-
|
|
174
|
-
|
|
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
|
|