relay-shell 0.4.0__tar.gz → 0.5.0__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.4.0 → relay_shell-0.5.0}/.env.example +8 -3
- relay_shell-0.5.0/.github/workflows/ci-gate.yml +70 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.github/workflows/ci.yml +45 -11
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.github/workflows/codeql.yml +14 -5
- relay_shell-0.5.0/.github/workflows/copilot-setup-steps.yml +42 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.github/workflows/dependency-review.yml +9 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.github/workflows/docs.yml +9 -6
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.github/workflows/gitleaks.yml +9 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.github/workflows/nightly-fuzz.yml +5 -2
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.github/workflows/pip-audit.yml +28 -8
- relay_shell-0.5.0/.github/workflows/release-rehearsal.yml +115 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.github/workflows/release.yml +15 -7
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.github/workflows/sbom.yml +33 -22
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.gitignore +8 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.pre-commit-config.yaml +1 -1
- {relay_shell-0.4.0 → relay_shell-0.5.0}/BACKLOG.md +43 -4
- {relay_shell-0.4.0 → relay_shell-0.5.0}/CHANGELOG.md +67 -1
- {relay_shell-0.4.0 → relay_shell-0.5.0}/CONTRIBUTING.md +43 -7
- {relay_shell-0.4.0 → relay_shell-0.5.0}/PKG-INFO +8 -7
- {relay_shell-0.4.0 → relay_shell-0.5.0}/README.md +6 -4
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/install-edge.sh +21 -4
- relay_shell-0.5.0/deploy/install.sh +208 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/adr/0001-runtime-and-sdk.md +8 -3
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/adr/0005-codebase-validation.md +4 -3
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/adr/0006-seccomp-notify-audit-channel.md +8 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/adr/0007-audit-hash-chain.md +4 -3
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/adr/0012-cancel-safe-session-teardown.md +4 -3
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/adr/0013-open-by-default-with-unsafe-notices.md +4 -2
- relay_shell-0.5.0/docs/adr/0014-seccomp-launch-and-install-recovery.md +180 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/adr/README.md +7 -7
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/architecture.md +29 -16
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/audit-shipper.md +40 -4
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/auth.md +8 -5
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/deployment.md +262 -37
- relay_shell-0.5.0/docs/index.md +58 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/release-trust.md +10 -3
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/runbook.md +214 -37
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/tools.md +29 -21
- {relay_shell-0.4.0 → relay_shell-0.5.0}/mkdocs.yml +4 -2
- {relay_shell-0.4.0 → relay_shell-0.5.0}/pyproject.toml +3 -4
- relay_shell-0.5.0/renovate.json5 +203 -0
- relay_shell-0.5.0/requirements/README.md +36 -0
- relay_shell-0.5.0/requirements/development-py3.12.txt +1636 -0
- relay_shell-0.5.0/requirements/development-py3.14.txt +1632 -0
- relay_shell-0.5.0/requirements/runtime-py3.12.txt +598 -0
- relay_shell-0.5.0/requirements/runtime-py3.14.txt +595 -0
- relay_shell-0.5.0/requirements-audit.in +2 -0
- relay_shell-0.5.0/requirements-audit.txt +442 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/requirements-build.txt +13 -13
- relay_shell-0.5.0/requirements-docs.txt +5 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/requirements-sbom.txt +51 -51
- relay_shell-0.5.0/requirements.txt +21 -0
- relay_shell-0.5.0/scripts/check_ci_checks.py +61 -0
- relay_shell-0.5.0/scripts/installer_transaction.py +219 -0
- relay_shell-0.5.0/scripts/reconcile_runtime.py +77 -0
- relay_shell-0.5.0/scripts/rehearse_install.py +532 -0
- relay_shell-0.5.0/scripts/rehearse_release.py +173 -0
- relay_shell-0.5.0/scripts/rehearse_upgrade.py +756 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/__init__.py +1 -1
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/auth/oauth.py +25 -1
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/redaction.py +33 -1
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/seccomp.py +129 -28
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/sessions.py +6 -2
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/shelltools.py +16 -3
- relay_shell-0.5.0/tests/fixtures/upgrade-v0.3/README.md +53 -0
- relay_shell-0.5.0/tests/fixtures/upgrade-v0.3/advisory-description.txt +1 -0
- relay_shell-0.5.0/tests/fixtures/upgrade-v0.3/baseline.json +13 -0
- relay_shell-0.5.0/tests/fixtures/upgrade-v0.3/runtime.in +10 -0
- relay_shell-0.5.0/tests/fixtures/upgrade-v0.3/runtime.lock +582 -0
- relay_shell-0.5.0/tests/test_ci_checks.py +68 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_ci_gate.py +1 -0
- relay_shell-0.5.0/tests/test_config_template.py +31 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_deploy_packaging.py +67 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_install_edge_validation.py +62 -0
- relay_shell-0.5.0/tests/test_install_rehearsal.py +48 -0
- relay_shell-0.5.0/tests/test_installer_transaction.py +106 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_oauth_store.py +19 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_redaction.py +6 -0
- relay_shell-0.5.0/tests/test_release_admission.py +170 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_release_workflows.py +34 -2
- relay_shell-0.5.0/tests/test_runtime_inventory.py +33 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_seccomp.py +107 -2
- relay_shell-0.5.0/tests/test_upgrade_rehearsal.py +102 -0
- relay_shell-0.5.0/tests/test_workflow_hygiene.py +106 -0
- relay_shell-0.4.0/.github/workflows/ci-gate.yml +0 -109
- relay_shell-0.4.0/.hypothesis/.gitignore +0 -9
- relay_shell-0.4.0/.hypothesis/constants/03552e8feb87dd21 +0 -4
- relay_shell-0.4.0/.hypothesis/constants/376080bff0f77cd7 +0 -4
- relay_shell-0.4.0/.hypothesis/constants/488bf38869fa6d62 +0 -4
- relay_shell-0.4.0/.hypothesis/constants/4b4e4855dd69cf11 +0 -4
- relay_shell-0.4.0/.hypothesis/constants/4e8204b0a01bc033 +0 -4
- relay_shell-0.4.0/.hypothesis/constants/4f785f577a444b6a +0 -4
- relay_shell-0.4.0/.hypothesis/constants/4ffce8883f0431d6 +0 -4
- relay_shell-0.4.0/.hypothesis/constants/5a149f13c700550f +0 -4
- relay_shell-0.4.0/.hypothesis/constants/5c2a3b580297d057 +0 -4
- relay_shell-0.4.0/.hypothesis/constants/7f7fe1194ef1e944 +0 -4
- relay_shell-0.4.0/.hypothesis/constants/9a25a08ade8424e9 +0 -4
- relay_shell-0.4.0/.hypothesis/constants/9bb33f940a88174f +0 -4
- relay_shell-0.4.0/.hypothesis/constants/a72b8354efb4636f +0 -4
- relay_shell-0.4.0/.hypothesis/constants/aa40616331558629 +0 -4
- relay_shell-0.4.0/.hypothesis/constants/b6e158e4328d9b9e +0 -4
- relay_shell-0.4.0/.hypothesis/constants/b98a154a8f88cbf3 +0 -4
- relay_shell-0.4.0/.hypothesis/constants/be5269261b77f6de +0 -4
- relay_shell-0.4.0/.hypothesis/constants/cf234c289a36e8fc +0 -4
- relay_shell-0.4.0/.hypothesis/constants/d78536b0c96f72a7 +0 -4
- relay_shell-0.4.0/.hypothesis/constants/daf0a2d5be3bdb3b +0 -4
- relay_shell-0.4.0/.hypothesis/constants/e07beacdd1b67d6e +0 -4
- relay_shell-0.4.0/.hypothesis/constants/ea23723ef0e64c17 +0 -4
- relay_shell-0.4.0/.hypothesis/constants/f0de1053c6198680 +0 -4
- relay_shell-0.4.0/.hypothesis/constants/f58b7c14550debdb +0 -4
- relay_shell-0.4.0/.hypothesis/unicode_data/16.0.0/charmap.json.gz +0 -0
- relay_shell-0.4.0/deploy/install.sh +0 -83
- relay_shell-0.4.0/docs/index.md +0 -40
- relay_shell-0.4.0/renovate.json5 +0 -26
- relay_shell-0.4.0/requirements-docs.txt +0 -5
- relay_shell-0.4.0/requirements.txt +0 -21
- relay_shell-0.4.0/tests/test_workflow_hygiene.py +0 -65
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.claude/settings.json +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.editorconfig +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.github/CODEOWNERS +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.github/ISSUE_TEMPLATE/bug.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.github/ISSUE_TEMPLATE/feature.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.github/ISSUE_TEMPLATE/security.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.github/copilot-instructions.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/.gitleaks.toml +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/AGENTS.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/CLAUDE.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/CODE_OF_CONDUCT.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/LICENSE +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/NOTICE +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/SECURITY.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/audit/00-inventory.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/audit/01-baseline.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/audit/02-security-findings.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/audit/03-final-report.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/audit/2026-05-27-engagement.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/audit/2026-06-01-engagement.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/audit/2026-06-21-adversarial-engagement.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/audit/2026-06-21-engagement.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/audit/2026-07-15-adversarial-engagement.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/audit/2026-07-15-engagement.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/audit/2026-07-15-release-v0.3.0.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/audit/2026-08-04-backlog-followup.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/audit/2026-08-04-engagement.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/audit/2026-10-14-engagement.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/Caddyfile +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/evidence/README.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/evidence/approved-resets.json.example +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/evidence/audit-evidence.env.example +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/evidence/relay-audit-evidence-sync +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/evidence/relay-audit-evidence.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/evidence/relay-audit-evidence.service +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/evidence/relay-audit-evidence.timer +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/logrotate/relay-shell +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/monitoring/PACKAGE.lock +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/monitoring/README.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/monitoring/nftables.conf.snippet +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/monitoring/prometheus-node-exporter.service.d/override.conf +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/monitoring/relay-health-metrics.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/monitoring/relay-health-metrics.service +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/monitoring/relay-health-metrics.timer +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/monitoring/relay-monitoring.env.example +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/release-signing.pub +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/systemd/relay-shell.service +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/deploy/systemd/relay-shell.service.d/hardening.conf +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/adr/0002-no-sandbox-full-access.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/adr/0003-tiered-authority.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/adr/0004-edge-tls-automation.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/adr/0008-operating-guidance-prompt.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/adr/0009-tier3-confirmation-broker.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/adr/0010-rollback-verify-broker.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/docs/adr/0011-windows-openssh-powershell.md +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/requirements-build.in +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/requirements-sbom.in +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/scripts/healthcheck.sh +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/__main__.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/audit.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/auth/__init__.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/auth/admin.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/auth/resource.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/broker.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/config.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/errors.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/inventory.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/metrics.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/notices.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/patterns.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/policy.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/py.typed +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/server.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/sshpool.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/util.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/src/relay_shell/verifier.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/conftest.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/helpers.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_a14_rs.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_a14_t1_gaps.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_admission_guards.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_audit.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_audit_lows.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_audit_tail_tool.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_authenticated_audit.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_broker.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_broker_client_binding.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_cancellation_audit.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_classifier_v12.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_classifier_v16.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_config.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_config_hygiene.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_dependency_floors.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_edge_routing.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_errors.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_evidence_deploy.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_fuzz.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_healthcheck.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_inventory.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_logrotate_config.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_main.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_metrics.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_notices.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_oauth.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_oauth_approval.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_oauth_resource.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_oauth_reuse.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_patterns.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_policy.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_prompts.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_redaction_operators.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_redaction_v16.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_resources.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_root_execution_contract.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_server.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_session_secret_audit.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_sessions.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_sessions_cancel.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_shell.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_ssh_fanout_tool.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_ssh_integration.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_ssh_keyscan_tool.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_sshpool_unit.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_stdio_e2e.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_tool_wrappers.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_util.py +0 -0
- {relay_shell-0.4.0 → relay_shell-0.5.0}/tests/test_verifier.py +0 -0
|
@@ -20,6 +20,7 @@ RELAY_SHELL_MAX_OUTPUT_HARD=1048576
|
|
|
20
20
|
# scanned partially. Raise it only if you really pipe multi-MiB payloads.
|
|
21
21
|
# 1024..134217728.
|
|
22
22
|
RELAY_SHELL_MAX_INPUT=2097152
|
|
23
|
+
# Default for shell_exec and ssh_exec when the call omits timeout.
|
|
23
24
|
RELAY_SHELL_DEFAULT_TIMEOUT=60
|
|
24
25
|
RELAY_SHELL_MAX_TIMEOUT=900
|
|
25
26
|
RELAY_SHELL_MAX_SESSIONS=64
|
|
@@ -69,9 +70,10 @@ RELAY_SHELL_AUDIT_INTENT=false
|
|
|
69
70
|
RELAY_SHELL_AUDIT_SESSION_INPUT=redacted
|
|
70
71
|
# Tamper-evident audit (ADR 0007). When true, each record carries seq/prev/chain
|
|
71
72
|
# so an edit, insertion, reorder, or interior deletion of the on-disk log is
|
|
72
|
-
# detectable with `relay-shell --verify-audit
|
|
73
|
-
#
|
|
74
|
-
#
|
|
73
|
+
# detectable with `relay-shell --verify-audit`. The verifier requires genesis
|
|
74
|
+
# by default; use --segment only for an explicitly partial segment.
|
|
75
|
+
# Tail-truncation and cross-file continuity need an off-host anchor/shipper.
|
|
76
|
+
# Default off keeps the record byte-identical. Requires
|
|
75
77
|
# RELAY_SHELL_AUDIT_FORMAT=jsonl. Enable on a freshly rotated log for a clean chain.
|
|
76
78
|
RELAY_SHELL_AUDIT_CHAIN=false
|
|
77
79
|
|
|
@@ -99,6 +101,9 @@ RELAY_SHELL_SSH_CONFIG=~/.ssh/config
|
|
|
99
101
|
RELAY_SHELL_INVENTORY=
|
|
100
102
|
# strict | accept-new | ignore
|
|
101
103
|
RELAY_SHELL_SSH_KNOWN_HOSTS=accept-new
|
|
104
|
+
# Allow a caller to select weaker host verification than the default. Set false
|
|
105
|
+
# to refuse weaker per-call overrides; stronger overrides remain available.
|
|
106
|
+
RELAY_SHELL_SSH_ALLOW_WEAKER_KNOWN_HOSTS=true
|
|
102
107
|
RELAY_SHELL_SSH_CONNECT_TIMEOUT=10
|
|
103
108
|
RELAY_SHELL_SSH_KEEPALIVE=30
|
|
104
109
|
# Idle eviction for cached SSH connections (seconds). The pool consults
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
name: CI aggregate
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
checks: read
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Coalesce initial PR attempts; isolate reruns and retain non-PR evidence.
|
|
12
|
+
concurrency:
|
|
13
|
+
group: >-
|
|
14
|
+
ci-gate-${{ github.workflow }}-${{ github.event_name }}-${{
|
|
15
|
+
github.run_attempt == '1' && github.event.pull_request.number || github.run_id
|
|
16
|
+
}}-${{ github.run_attempt }}
|
|
17
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == '1' }}
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
ci-success:
|
|
21
|
+
name: ci-success
|
|
22
|
+
if: always()
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
timeout-minutes: 30
|
|
25
|
+
env:
|
|
26
|
+
TARGET_SHA: ${{ github.event.pull_request.head.sha }}
|
|
27
|
+
REQUIRED_CHECKS: |-
|
|
28
|
+
Build (MkDocs Material)
|
|
29
|
+
check (py3.12)
|
|
30
|
+
check (py3.14)
|
|
31
|
+
gitleaks (secret scan)
|
|
32
|
+
pip-audit (CVE scan)
|
|
33
|
+
dependency-review
|
|
34
|
+
Analyze (python) (python)
|
|
35
|
+
Analyze (actions)
|
|
36
|
+
# Empty by design. Add an exact check name here only when a skipped
|
|
37
|
+
# result is an explicitly valid outcome for that protected dependency.
|
|
38
|
+
ALLOWED_SKIPPED_CHECKS: ""
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
41
|
+
with:
|
|
42
|
+
persist-credentials: false
|
|
43
|
+
- name: Require protected checks to pass
|
|
44
|
+
env:
|
|
45
|
+
GH_TOKEN: ${{ github.token }}
|
|
46
|
+
shell: bash
|
|
47
|
+
run: |
|
|
48
|
+
set -euo pipefail
|
|
49
|
+
snapshot=$(mktemp)
|
|
50
|
+
trap 'rm -f "$snapshot"' EXIT
|
|
51
|
+
deadline=$((SECONDS + 1500))
|
|
52
|
+
while :; do
|
|
53
|
+
# One paginated snapshot per poll, rather than one API call per name.
|
|
54
|
+
# An API failure is a failure, never an empty/successful snapshot.
|
|
55
|
+
gh api --method GET --paginate --slurp \
|
|
56
|
+
"repos/${GITHUB_REPOSITORY}/commits/${TARGET_SHA}/check-runs" \
|
|
57
|
+
-f filter=all -F per_page=100 > "$snapshot"
|
|
58
|
+
result=0
|
|
59
|
+
python3 scripts/check_ci_checks.py < "$snapshot" || result=$?
|
|
60
|
+
case "$result" in
|
|
61
|
+
0) exit 0 ;;
|
|
62
|
+
2) ;;
|
|
63
|
+
*) echo '::error::Protected dependency failed or snapshot invalid'; exit 1 ;;
|
|
64
|
+
esac
|
|
65
|
+
if [ "$SECONDS" -ge "$deadline" ]; then
|
|
66
|
+
echo '::error::Timed out waiting for protected dependencies'
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
sleep 10
|
|
70
|
+
done
|
|
@@ -8,20 +8,33 @@ on:
|
|
|
8
8
|
permissions:
|
|
9
9
|
contents: read
|
|
10
10
|
|
|
11
|
+
|
|
12
|
+
# Coalesce initial PR attempts; isolate reruns and retain non-PR evidence.
|
|
13
|
+
concurrency:
|
|
14
|
+
group: >-
|
|
15
|
+
ci-${{ github.workflow }}-${{ github.event_name }}-${{
|
|
16
|
+
github.run_attempt == '1' && github.event.pull_request.number || github.run_id
|
|
17
|
+
}}-${{ github.run_attempt }}
|
|
18
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == '1' }}
|
|
19
|
+
|
|
11
20
|
jobs:
|
|
12
21
|
check:
|
|
13
22
|
permissions:
|
|
14
23
|
contents: read
|
|
15
24
|
timeout-minutes: 20
|
|
16
|
-
runs-on:
|
|
25
|
+
runs-on: ${{ matrix.os }}
|
|
17
26
|
strategy:
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# pyproject.toml; the matrix covers the floor plus the two newer
|
|
21
|
-
# interpreters in active CPython support.
|
|
27
|
+
# Qualify the native Python minor for each supported Ubuntu LTS.
|
|
28
|
+
# Both profiles must pass independently, including binary-only lock admission.
|
|
22
29
|
fail-fast: false
|
|
23
30
|
matrix:
|
|
24
|
-
|
|
31
|
+
include:
|
|
32
|
+
- os: ubuntu-24.04
|
|
33
|
+
os-version: "24.04"
|
|
34
|
+
python-version: "3.12"
|
|
35
|
+
- os: ubuntu-26.04
|
|
36
|
+
os-version: "26.04"
|
|
37
|
+
python-version: "3.14"
|
|
25
38
|
name: check (py${{ matrix.python-version }})
|
|
26
39
|
steps:
|
|
27
40
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
@@ -32,11 +45,29 @@ jobs:
|
|
|
32
45
|
with:
|
|
33
46
|
python-version: ${{ matrix.python-version }}
|
|
34
47
|
cache: "pip"
|
|
48
|
+
cache-dependency-path: |
|
|
49
|
+
requirements/development-py${{ matrix.python-version }}.txt
|
|
50
|
+
|
|
51
|
+
- name: Verify the qualified OS and Python profile
|
|
52
|
+
env:
|
|
53
|
+
EXPECTED_UBUNTU: ${{ matrix.os-version }}
|
|
54
|
+
EXPECTED_PYTHON: ${{ matrix.python-version }}
|
|
55
|
+
run: |
|
|
56
|
+
python - <<'PY'
|
|
57
|
+
import os, platform, sys
|
|
58
|
+
release = platform.freedesktop_os_release()
|
|
59
|
+
actual_python = f"{sys.version_info.major}.{sys.version_info.minor}"
|
|
60
|
+
if release["ID"] != "ubuntu" or release["VERSION_ID"] != os.environ["EXPECTED_UBUNTU"]:
|
|
61
|
+
raise SystemExit(f"Unexpected OS profile: {release}")
|
|
62
|
+
if actual_python != os.environ["EXPECTED_PYTHON"]:
|
|
63
|
+
raise SystemExit(f"Unexpected Python profile: {actual_python}")
|
|
64
|
+
PY
|
|
35
65
|
|
|
36
66
|
- name: Install
|
|
37
67
|
run: |
|
|
38
|
-
python -m pip install --
|
|
39
|
-
pip install -
|
|
68
|
+
python -m pip install --only-binary=:all: --require-hashes -r "requirements/development-py${{ matrix.python-version }}.txt"
|
|
69
|
+
python -m pip install --no-deps --no-build-isolation -e ".[dev]"
|
|
70
|
+
python -m pip check
|
|
40
71
|
# Wire subprocess coverage: the stdio e2e launches `python -m
|
|
41
72
|
# relay_shell` in a child, so without this the server.py wrappers
|
|
42
73
|
# register as uncovered. The .pth runs at every interpreter start;
|
|
@@ -50,9 +81,11 @@ jobs:
|
|
|
50
81
|
# (2.7+; the distro package is older). Built from the pinned module version, whose
|
|
51
82
|
# contents the Go checksum database verifies, on one matrix leg only.
|
|
52
83
|
- name: Install Caddy (edge routing tests)
|
|
53
|
-
if: matrix.python-version == '3.
|
|
84
|
+
if: matrix.python-version == '3.14'
|
|
54
85
|
env:
|
|
55
86
|
GOBIN: ${{ runner.temp }}/caddy-bin
|
|
87
|
+
# Avoid checksum-service HTTP/2 stream failures; TLS and Go sums stay enabled.
|
|
88
|
+
GODEBUG: http2client=0
|
|
56
89
|
run: |
|
|
57
90
|
command -v go
|
|
58
91
|
go install github.com/caddyserver/caddy/v2/cmd/caddy@v2.11.7
|
|
@@ -80,8 +113,9 @@ jobs:
|
|
|
80
113
|
# root-owned files cannot affect the steps before it). They still skip themselves if the
|
|
81
114
|
# runner kernel does not support the notify channel; -rs prints that reason.
|
|
82
115
|
- name: Test (seccomp-notify end to end, privileged)
|
|
83
|
-
if: matrix.python-version == '3.
|
|
116
|
+
if: matrix.python-version == '3.14'
|
|
84
117
|
env:
|
|
85
118
|
PYTHONDONTWRITEBYTECODE: "1"
|
|
86
119
|
run: |
|
|
87
|
-
sudo -
|
|
120
|
+
sudo --preserve-env=PYTHONDONTWRITEBYTECODE "$(command -v python)" -c 'from relay_shell.seccomp import platform_support; s=platform_support(); print(s); s.ok or exit("privileged seccomp coverage unavailable")'
|
|
121
|
+
sudo --preserve-env=PYTHONDONTWRITEBYTECODE "$(command -v python)" -m pytest -m seccomp -q -rs -p no:cacheprovider tests/test_seccomp.py
|
|
@@ -12,6 +12,15 @@ permissions:
|
|
|
12
12
|
contents: read
|
|
13
13
|
security-events: write
|
|
14
14
|
|
|
15
|
+
|
|
16
|
+
# Coalesce initial PR attempts; isolate reruns and retain non-PR evidence.
|
|
17
|
+
concurrency:
|
|
18
|
+
group: >-
|
|
19
|
+
codeql-${{ github.workflow }}-${{ github.event_name }}-${{
|
|
20
|
+
github.run_attempt == '1' && github.event.pull_request.number || github.run_id
|
|
21
|
+
}}-${{ github.run_attempt }}
|
|
22
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == '1' }}
|
|
23
|
+
|
|
15
24
|
jobs:
|
|
16
25
|
analyze:
|
|
17
26
|
name: Analyze (python)
|
|
@@ -29,15 +38,15 @@ jobs:
|
|
|
29
38
|
persist-credentials: false
|
|
30
39
|
|
|
31
40
|
- name: Initialize CodeQL
|
|
32
|
-
uses: github/codeql-action/init@
|
|
41
|
+
uses: github/codeql-action/init@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4
|
|
33
42
|
with:
|
|
34
43
|
languages: ${{ matrix.language }}
|
|
35
44
|
|
|
36
45
|
- name: Autobuild
|
|
37
|
-
uses: github/codeql-action/autobuild@
|
|
46
|
+
uses: github/codeql-action/autobuild@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4
|
|
38
47
|
|
|
39
48
|
- name: Perform CodeQL Analysis
|
|
40
|
-
uses: github/codeql-action/analyze@
|
|
49
|
+
uses: github/codeql-action/analyze@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4
|
|
41
50
|
|
|
42
51
|
# A14-D12: the workflows themselves are code that holds tokens. A separate job (not a
|
|
43
52
|
# matrix entry) so the required check `Analyze (python) (python)` keeps its name. It is in
|
|
@@ -54,12 +63,12 @@ jobs:
|
|
|
54
63
|
persist-credentials: false
|
|
55
64
|
|
|
56
65
|
- name: Initialize CodeQL
|
|
57
|
-
uses: github/codeql-action/init@
|
|
66
|
+
uses: github/codeql-action/init@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4
|
|
58
67
|
with:
|
|
59
68
|
languages: actions
|
|
60
69
|
build-mode: none
|
|
61
70
|
|
|
62
71
|
- name: Perform CodeQL Analysis
|
|
63
|
-
uses: github/codeql-action/analyze@
|
|
72
|
+
uses: github/codeql-action/analyze@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4
|
|
64
73
|
with:
|
|
65
74
|
category: "/language:actions"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Copilot development setup
|
|
2
|
+
on:
|
|
3
|
+
workflow_dispatch:
|
|
4
|
+
pull_request:
|
|
5
|
+
paths:
|
|
6
|
+
- .github/workflows/copilot-setup-steps.yml
|
|
7
|
+
- requirements.txt
|
|
8
|
+
- requirements-build.txt
|
|
9
|
+
- "requirements/**"
|
|
10
|
+
- pyproject.toml
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
# Coalesce initial PR attempts; isolate reruns and retain non-PR evidence.
|
|
15
|
+
concurrency:
|
|
16
|
+
group: >-
|
|
17
|
+
copilot-setup-steps-${{ github.workflow }}-${{ github.event_name }}-${{
|
|
18
|
+
github.run_attempt == '1' && github.event.pull_request.number || github.run_id
|
|
19
|
+
}}-${{ github.run_attempt }}
|
|
20
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == '1' }}
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
copilot-setup-steps:
|
|
24
|
+
runs-on: ubuntu-26.04
|
|
25
|
+
timeout-minutes: 10
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
28
|
+
with:
|
|
29
|
+
persist-credentials: false
|
|
30
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
|
31
|
+
with:
|
|
32
|
+
python-version: "3.14"
|
|
33
|
+
- name: Prepare the constrained development environment
|
|
34
|
+
run: |
|
|
35
|
+
python -m pip install --only-binary=:all: --require-hashes -r requirements/development-py3.14.txt
|
|
36
|
+
python -m pip install --no-deps --no-build-isolation -e ".[dev]"
|
|
37
|
+
python -m pip check
|
|
38
|
+
- name: Verify tools and representative test discovery
|
|
39
|
+
run: |
|
|
40
|
+
ruff --version
|
|
41
|
+
mypy --version
|
|
42
|
+
python -m pytest --collect-only -q tests/test_stdio_e2e.py tests/test_ssh_integration.py
|
|
@@ -6,6 +6,15 @@ on:
|
|
|
6
6
|
permissions:
|
|
7
7
|
contents: read
|
|
8
8
|
|
|
9
|
+
|
|
10
|
+
# Coalesce initial PR attempts; isolate reruns and retain non-PR evidence.
|
|
11
|
+
concurrency:
|
|
12
|
+
group: >-
|
|
13
|
+
dependency-review-${{ github.workflow }}-${{ github.event_name }}-${{
|
|
14
|
+
github.run_attempt == '1' && github.event.pull_request.number || github.run_id
|
|
15
|
+
}}-${{ github.run_attempt }}
|
|
16
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == '1' }}
|
|
17
|
+
|
|
9
18
|
jobs:
|
|
10
19
|
dependency-review:
|
|
11
20
|
runs-on: ubuntu-latest
|
|
@@ -13,6 +13,7 @@ on:
|
|
|
13
13
|
- "mkdocs.yml"
|
|
14
14
|
- "requirements-docs.txt"
|
|
15
15
|
- ".github/workflows/docs.yml"
|
|
16
|
+
pull_request:
|
|
16
17
|
workflow_dispatch:
|
|
17
18
|
|
|
18
19
|
# Least-privilege default; the deploy job adds only the Pages-specific scopes.
|
|
@@ -21,13 +22,13 @@ permissions:
|
|
|
21
22
|
|
|
22
23
|
# One Pages deployment at a time; let an in-flight publish finish.
|
|
23
24
|
concurrency:
|
|
24
|
-
group: pages
|
|
25
|
+
group: pages-${{ github.event.pull_request.number || github.ref }}
|
|
25
26
|
cancel-in-progress: false
|
|
26
27
|
|
|
27
28
|
jobs:
|
|
28
29
|
build:
|
|
29
30
|
name: Build (MkDocs Material)
|
|
30
|
-
runs-on: ubuntu-
|
|
31
|
+
runs-on: ubuntu-24.04
|
|
31
32
|
timeout-minutes: 10
|
|
32
33
|
steps:
|
|
33
34
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
@@ -35,16 +36,17 @@ jobs:
|
|
|
35
36
|
persist-credentials: false
|
|
36
37
|
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
|
37
38
|
with:
|
|
38
|
-
python-version: "3.
|
|
39
|
+
python-version: "3.12"
|
|
39
40
|
cache: pip
|
|
40
41
|
cache-dependency-path: requirements-docs.txt
|
|
41
42
|
- name: Install MkDocs Material
|
|
42
43
|
run: |
|
|
43
|
-
python -m pip install
|
|
44
|
-
|
|
44
|
+
python -m pip install -r requirements-docs.txt
|
|
45
|
+
python -m pip check
|
|
45
46
|
- name: Build site (strict)
|
|
46
47
|
run: mkdocs build --strict
|
|
47
48
|
- name: Upload Pages artifact
|
|
49
|
+
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
|
48
50
|
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
|
|
49
51
|
with:
|
|
50
52
|
path: site
|
|
@@ -52,7 +54,8 @@ jobs:
|
|
|
52
54
|
deploy:
|
|
53
55
|
name: Deploy to GitHub Pages
|
|
54
56
|
needs: build
|
|
55
|
-
|
|
57
|
+
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
|
58
|
+
runs-on: ubuntu-24.04
|
|
56
59
|
timeout-minutes: 10
|
|
57
60
|
permissions:
|
|
58
61
|
pages: write # publish the Pages deployment
|
|
@@ -22,6 +22,15 @@ on:
|
|
|
22
22
|
permissions:
|
|
23
23
|
contents: read
|
|
24
24
|
|
|
25
|
+
|
|
26
|
+
# Coalesce initial PR attempts; isolate reruns and retain non-PR evidence.
|
|
27
|
+
concurrency:
|
|
28
|
+
group: >-
|
|
29
|
+
gitleaks-${{ github.workflow }}-${{ github.event_name }}-${{
|
|
30
|
+
github.run_attempt == '1' && github.event.pull_request.number || github.run_id
|
|
31
|
+
}}-${{ github.run_attempt }}
|
|
32
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == '1' }}
|
|
33
|
+
|
|
25
34
|
jobs:
|
|
26
35
|
scan:
|
|
27
36
|
name: gitleaks (secret scan)
|
|
@@ -30,11 +30,14 @@ jobs:
|
|
|
30
30
|
with:
|
|
31
31
|
python-version: "3.14"
|
|
32
32
|
cache: "pip"
|
|
33
|
+
cache-dependency-path: |
|
|
34
|
+
requirements/development-py3.14.txt
|
|
33
35
|
|
|
34
36
|
- name: Install
|
|
35
37
|
run: |
|
|
36
|
-
python -m pip install --
|
|
37
|
-
pip install -
|
|
38
|
+
python -m pip install --only-binary=:all: --require-hashes -r requirements/development-py3.14.txt
|
|
39
|
+
python -m pip install --no-deps --no-build-isolation -e ".[dev]"
|
|
40
|
+
python -m pip check
|
|
38
41
|
|
|
39
42
|
- name: Run fuzz suite (HYPOTHESIS_PROFILE=ci)
|
|
40
43
|
env:
|
|
@@ -19,6 +19,15 @@ on:
|
|
|
19
19
|
permissions:
|
|
20
20
|
contents: read
|
|
21
21
|
|
|
22
|
+
|
|
23
|
+
# Coalesce initial PR attempts; isolate reruns and retain non-PR evidence.
|
|
24
|
+
concurrency:
|
|
25
|
+
group: >-
|
|
26
|
+
pip-audit-${{ github.workflow }}-${{ github.event_name }}-${{
|
|
27
|
+
github.run_attempt == '1' && github.event.pull_request.number || github.run_id
|
|
28
|
+
}}-${{ github.run_attempt }}
|
|
29
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == '1' }}
|
|
30
|
+
|
|
22
31
|
jobs:
|
|
23
32
|
audit:
|
|
24
33
|
name: pip-audit (CVE scan)
|
|
@@ -33,14 +42,21 @@ jobs:
|
|
|
33
42
|
with:
|
|
34
43
|
python-version: "3.14"
|
|
35
44
|
cache: "pip"
|
|
45
|
+
cache-dependency-path: |
|
|
46
|
+
requirements/development-py3.14.txt
|
|
47
|
+
requirements-audit.txt
|
|
36
48
|
|
|
37
|
-
- name:
|
|
49
|
+
- name: Prepare separate locked subject and audit-tool environments
|
|
38
50
|
run: |
|
|
39
|
-
python -m
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
python -m venv .audit-runtime
|
|
52
|
+
.audit-runtime/bin/python -m pip install --only-binary=:all: --require-hashes -r requirements/development-py3.14.txt
|
|
53
|
+
.audit-runtime/bin/python -m pip install --no-deps --no-build-isolation -e ".[dev,http]"
|
|
54
|
+
.audit-runtime/bin/python -m pip check
|
|
55
|
+
python -m venv .audit-tool
|
|
56
|
+
.audit-tool/bin/python -m pip install --only-binary=:all: --require-hashes -r requirements-audit.txt
|
|
57
|
+
.audit-tool/bin/python -m pip check
|
|
58
|
+
audit_path=$(.audit-runtime/bin/python -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')
|
|
59
|
+
echo "AUDIT_SITE_PACKAGES=$audit_path" >> "$GITHUB_ENV"
|
|
44
60
|
|
|
45
61
|
- name: Run pip-audit
|
|
46
62
|
# `--skip-editable` excludes the local editable `relay-shell` install:
|
|
@@ -52,10 +68,14 @@ jobs:
|
|
|
52
68
|
# audit and fails ("distribution marked as editable"). Every actual
|
|
53
69
|
# third-party dependency (incl. the [dev]/[http] extras) is on PyPI and
|
|
54
70
|
# still audited, and pip-audit still exits non-zero on any CVE found.
|
|
55
|
-
run: pip-audit --skip-editable
|
|
71
|
+
run: .audit-tool/bin/pip-audit --path "$AUDIT_SITE_PACKAGES" --skip-editable
|
|
56
72
|
|
|
57
73
|
- name: Audit the hash-locked release and SBOM tooling
|
|
58
74
|
# requirements-build.txt / requirements-sbom.txt feed the jobs that build,
|
|
59
75
|
# sign and publish (A14-D7); they are not part of the package's own tree, so
|
|
60
76
|
# audit them explicitly. They are fully pinned, so no resolution is needed.
|
|
61
|
-
run:
|
|
77
|
+
run: |
|
|
78
|
+
.audit-tool/bin/pip-audit --disable-pip --no-deps -r requirements-build.txt
|
|
79
|
+
.audit-tool/bin/pip-audit --disable-pip --no-deps -r requirements-sbom.txt
|
|
80
|
+
.audit-tool/bin/pip-audit --disable-pip --no-deps -r requirements-audit.txt
|
|
81
|
+
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
name: Release preparation rehearsal
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Coalesce initial PR attempts; isolate reruns and retain non-PR evidence.
|
|
14
|
+
concurrency:
|
|
15
|
+
group: >-
|
|
16
|
+
release-rehearsal-${{ github.workflow }}-${{ github.event_name }}-${{
|
|
17
|
+
github.run_attempt == '1' && github.event.pull_request.number || github.run_id
|
|
18
|
+
}}-${{ github.run_attempt }}
|
|
19
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == '1' }}
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
rehearse:
|
|
23
|
+
name: package-and-install-rehearsal (${{ matrix.os }})
|
|
24
|
+
runs-on: ${{ matrix.os }}
|
|
25
|
+
strategy:
|
|
26
|
+
fail-fast: false
|
|
27
|
+
matrix:
|
|
28
|
+
os: [ubuntu-24.04, ubuntu-26.04]
|
|
29
|
+
timeout-minutes: 15
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
32
|
+
with:
|
|
33
|
+
persist-credentials: false
|
|
34
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
|
35
|
+
with:
|
|
36
|
+
python-version: "3.14"
|
|
37
|
+
- name: Prepare the locked rehearsal driver
|
|
38
|
+
run: |
|
|
39
|
+
python -m pip install --only-binary=:all: --require-hashes -r requirements/development-py3.14.txt
|
|
40
|
+
- name: Rehearse package preparation and complete runtime restoration
|
|
41
|
+
run: python scripts/rehearse_release.py --output "$RUNNER_TEMP/release-rehearsal"
|
|
42
|
+
- name: Qualify actual installation and service-state recovery on the disposable VM
|
|
43
|
+
run: sudo --preserve-env=GITHUB_ACTIONS,RUNNER_ENVIRONMENT /usr/bin/python3 scripts/rehearse_install.py --output "$RUNNER_TEMP/install-rehearsal"
|
|
44
|
+
- name: Retain installer recovery receipt
|
|
45
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
|
|
46
|
+
with:
|
|
47
|
+
name: install-rehearsal-${{ matrix.os }}-${{ github.sha }}
|
|
48
|
+
path: ${{ runner.temp }}/install-rehearsal/receipt.json
|
|
49
|
+
if-no-files-found: error
|
|
50
|
+
retention-days: 30
|
|
51
|
+
- name: Retain source-bound rehearsal evidence
|
|
52
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
|
|
53
|
+
with:
|
|
54
|
+
name: release-rehearsal-${{ matrix.os }}-${{ github.sha }}
|
|
55
|
+
path: ${{ runner.temp }}/release-rehearsal/
|
|
56
|
+
if-no-files-found: error
|
|
57
|
+
retention-days: 30
|
|
58
|
+
|
|
59
|
+
cross-version:
|
|
60
|
+
name: cross-version-recovery
|
|
61
|
+
runs-on: ubuntu-26.04
|
|
62
|
+
timeout-minutes: 15
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
65
|
+
with:
|
|
66
|
+
persist-credentials: false
|
|
67
|
+
fetch-depth: 0
|
|
68
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
|
69
|
+
id: build-python
|
|
70
|
+
with:
|
|
71
|
+
python-version: "3.14"
|
|
72
|
+
- name: Prepare hash-locked build tools
|
|
73
|
+
run: |
|
|
74
|
+
python -m pip install --only-binary=:all: --require-hashes -r requirements/development-py3.12.txt
|
|
75
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
|
76
|
+
with:
|
|
77
|
+
python-version: "3.14"
|
|
78
|
+
- name: Prepare independent audit tools with their locked interpreter
|
|
79
|
+
run: |
|
|
80
|
+
python -m venv .legacy-audit-tool
|
|
81
|
+
.legacy-audit-tool/bin/python -m pip install --only-binary=:all: --require-hashes -r requirements-audit.txt
|
|
82
|
+
- name: Audit the complete frozen baseline without executing it
|
|
83
|
+
run: |
|
|
84
|
+
set +e
|
|
85
|
+
.legacy-audit-tool/bin/pip-audit --requirement tests/fixtures/upgrade-v0.3/runtime.lock --require-hashes --disable-pip --strict --progress-spinner off --format json --output "$RUNNER_TEMP/legacy-audit.json"
|
|
86
|
+
audit_result=$?
|
|
87
|
+
set -e
|
|
88
|
+
test "$audit_result" -eq 0 || test "$audit_result" -eq 1
|
|
89
|
+
test -s "$RUNNER_TEMP/legacy-audit.json"
|
|
90
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
|
91
|
+
id: legacy-python
|
|
92
|
+
with:
|
|
93
|
+
python-version: "3.12"
|
|
94
|
+
- name: Qualify admitted release upgrade and explicit rollback
|
|
95
|
+
run: sudo --preserve-env=GITHUB_ACTIONS,RUNNER_ENVIRONMENT /usr/bin/python3 scripts/rehearse_upgrade.py --build-python "${{ steps.build-python.outputs.python-path }}" --legacy-python "${{ steps.legacy-python.outputs.python-path }}" --audit-report "$RUNNER_TEMP/legacy-audit.json" --output "$RUNNER_TEMP/upgrade-rehearsal"
|
|
96
|
+
- name: Retain cross-version source-bound receipt
|
|
97
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
|
|
98
|
+
with:
|
|
99
|
+
name: upgrade-rehearsal-${{ github.sha }}
|
|
100
|
+
path: ${{ runner.temp }}/upgrade-rehearsal/receipt.json
|
|
101
|
+
if-no-files-found: error
|
|
102
|
+
retention-days: 30
|
|
103
|
+
|
|
104
|
+
release-rehearsal:
|
|
105
|
+
name: release-rehearsal
|
|
106
|
+
if: ${{ always() }}
|
|
107
|
+
needs: [rehearse, cross-version]
|
|
108
|
+
runs-on: ubuntu-26.04
|
|
109
|
+
timeout-minutes: 2
|
|
110
|
+
steps:
|
|
111
|
+
- name: Require package, installer and cross-version recovery
|
|
112
|
+
env:
|
|
113
|
+
PACKAGE_RESULT: ${{ needs.rehearse.result }}
|
|
114
|
+
UPGRADE_RESULT: ${{ needs.cross-version.result }}
|
|
115
|
+
run: test "$PACKAGE_RESULT" = success && test "$UPGRADE_RESULT" = success
|
|
@@ -6,9 +6,9 @@ name: release
|
|
|
6
6
|
# 1. verify - the tag is annotated, SSH-signed against the trusted key
|
|
7
7
|
# in deploy/release-signing.pub, and matches the version
|
|
8
8
|
# declared in pyproject.toml
|
|
9
|
-
# 2. build -
|
|
10
|
-
# constraints), full test suite on Python 3.14 (per-PR CI
|
|
11
|
-
#
|
|
9
|
+
# 2. build - complete hashed development profile (historical tags retain
|
|
10
|
+
# direct constraints), full test suite on Python 3.14 (per-PR CI
|
|
11
|
+
# qualifies Ubuntu 24.04/3.12 and Ubuntu 26.04/3.14), then `python -m build`
|
|
12
12
|
# to produce wheel + sdist. Build tooling comes from the
|
|
13
13
|
# hash-locked requirements-build.txt and the build runs with
|
|
14
14
|
# --no-isolation so no unpinned backend is fetched. This job
|
|
@@ -67,7 +67,7 @@ jobs:
|
|
|
67
67
|
else
|
|
68
68
|
tag="${REF#refs/tags/}"
|
|
69
69
|
fi
|
|
70
|
-
if !
|
|
70
|
+
if ! [[ "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([+.-][0-9A-Za-z.-]+)?$ ]]; then
|
|
71
71
|
echo "ERROR: $tag does not match the v<MAJOR>.<MINOR>.<PATCH> pattern" >&2
|
|
72
72
|
exit 1
|
|
73
73
|
fi
|
|
@@ -117,6 +117,7 @@ jobs:
|
|
|
117
117
|
fi
|
|
118
118
|
release_commit=$(git rev-list -n1 "${TAG}^{}")
|
|
119
119
|
test -n "$release_commit"
|
|
120
|
+
test "$(git rev-parse HEAD)" = "$release_commit"
|
|
120
121
|
# A tag is not an authority for its own signer trust anchor.
|
|
121
122
|
# Fetch the protected default branch independently and require the
|
|
122
123
|
# tagged commit to have passed the main-branch merge gate.
|
|
@@ -189,8 +190,15 @@ jobs:
|
|
|
189
190
|
# --no-build-isolation installs and `python -m build` use these exact
|
|
190
191
|
# (not freshly resolved) hatchling/build/twine.
|
|
191
192
|
python -m pip install --require-hashes -r "$RUNNER_TEMP/requirements-build.txt"
|
|
192
|
-
|
|
193
|
-
|
|
193
|
+
if [ -r requirements/development-py3.14.txt ]; then
|
|
194
|
+
# Refuse disagreement with the workflow's independently selected build tools.
|
|
195
|
+
python -m pip install --only-binary=:all: --require-hashes -r requirements/development-py3.14.txt -c "$RUNNER_TEMP/requirements-build.txt"
|
|
196
|
+
python -m pip install --no-deps --no-build-isolation -e ".[dev]"
|
|
197
|
+
else
|
|
198
|
+
# Historical signed tags predate complete profiles; retain their documented recovery path.
|
|
199
|
+
python -m pip install --no-build-isolation -c requirements.txt -e ".[dev]"
|
|
200
|
+
fi
|
|
201
|
+
python -m pip check
|
|
194
202
|
|
|
195
203
|
- name: Run full test suite
|
|
196
204
|
run: pytest -q
|
|
@@ -263,7 +271,7 @@ jobs:
|
|
|
263
271
|
path: dist/
|
|
264
272
|
|
|
265
273
|
- name: Publish via PyPI trusted publishing
|
|
266
|
-
uses: pypa/gh-action-pypi-publish@
|
|
274
|
+
uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
|
|
267
275
|
with:
|
|
268
276
|
# Make a workflow re-run after a successful upload idempotent
|
|
269
277
|
# instead of failing on "file already exists" - the runbook
|