Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .echo/sdk.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"echo.certforge.projects.create",
"echo.certforge.projects.list",
"echo.certforge.public_verification",
"echo.certforge.publish",
"echo.certforge.r5.status",
"echo.certforge.r5.submit_async",
"echo.certforge.run",
Expand Down
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
# Changelog

## [1.2.0] - 2026-08-30

### Added

- A pinned-key-signed production E2E gate required before any `PRODUCTION_READY` verdict.
- Exact E2E bindings for source, deployment, target, environment, accounts, and clients.

All notable changes to Echo Certification Forge are documented here. Versions follow Semantic
Versioning; dates use ISO 8601.

## [Unreleased]

### Added

- Return secret-safe public target and production-E2E projections plus the canonical environment
identity from the public verification endpoint, bind each projection to a dedicated digest in
the signed verdict, and fail the public result closed on any serialization mismatch.
- Publish only signed aggregate production-E2E outcomes; raw accounts, private repository samples,
credential routes, and client fingerprints remain in the private evidence record.
- Require a bounded, credential-free HTTPS canonical target for generic production-E2E proofs.
- Reject credential-bearing Git source URLs before acquisition, and add exact three-account
Autonomy plus four-client Continuity production-E2E profiles.

### Changed

- Allow operators to select a validated 128 MiB to 4 GiB journey cgroup while preserving no-swap
containment, and record the effective memory, CPU, PID, and scratch limits in journey evidence.
- Bind the exact sandbox image digest and resource profile into the certification environment identity.

## [1.1.0] - 2026-08-09

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
![Release gate](https://img.shields.io/badge/release%20gate-fail--closed-caa85e)
![License](https://img.shields.io/badge/license-proprietary-59636e)

[Architecture](docs/ARCHITECTURE.md) · [Operations](docs/OPERATIONS.md) · [Security](SECURITY.md) · [Contributing](CONTRIBUTING.md) · [Changelog](CHANGELOG.md)
[Architecture](docs/ARCHITECTURE.md) · [Public verification](docs/PUBLIC_VERIFICATION_CONTRACT.md) · [Operations](docs/OPERATIONS.md) · [Security](SECURITY.md) · [Contributing](CONTRIBUTING.md) · [Changelog](CHANGELOG.md)

Echo Certification Forge is a deterministic, evidence-backed release authority for EchoForge. Every run begins at `NOT_READY`. Exact target, environment, policy, evidence, anchor, key, and lifecycle identities must verify before any signed verdict can be trusted.

Expand Down
27 changes: 15 additions & 12 deletions artifacts/p1_acceptance_report.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"manifest_digest": "0ca325c3e6d39b7f22cdd3d01068c8f99dcd976c4422200b5b4d4cfe452f29f7",
"manifest_digest": "9e9f963377374e827ccc9c8b07ea82cc7966e25a2392838997d3c1371f5a4e5c",
"manifest_id": "certforge.release-strict.v1",
"passed": true,
"phase": "P1",
"platform": "Windows-11-10.0.22631-SP0",
"python": "3.13.0 (tags/v3.13.0:60403a5, Oct 7 2024, 09:38:07) [MSC v.1941 64 bit (AMD64)]",
"platform": "Windows-11-10.0.26200-SP0",
"python": "3.12.10 (tags/v3.12.10:0cc8128, Apr 8 2025, 12:21:36) [MSC v.1943 64 bit (AMD64)]",
"scenarios": {
"default_block": {
"passed": true,
Expand All @@ -17,27 +17,30 @@
"mandatory_rule_missing:environment_identity",
"mandatory_rule_missing:evidence_integrity",
"mandatory_rule_missing:immutable_target_identity",
"mandatory_rule_missing:production_e2e",
"mandatory_rule_missing:runner_control_channel",
"mandatory_rule_missing:signing_authority_separation",
"mandatory_rule_missing:tenant_isolation",
"production_e2e_attestation_missing",
"run_outcome_inconclusive"
],
"verdict": "NOT_READY"
},
"source_only_blocks": {
"deploy_gate_allowed": false,
"evidence_merkle_root": "41a4fc282a478767dc16f6c64128c93ab2535866e620a3f5b0ae922220ffb449",
"passed": true,
"payload_sha256": "a7255f5bae53fcd49b1e049b3edd1feb6c9b03f4520b0e2abb0e44f0da7138e7",
"signing_key_id": "ed25519:e222f1c31e85f1b8a6d8cfed30fd2300",
"verdict": "NOT_READY"
},
"tamper_blocks": {
"deploy_gate_allowed": false,
"passed": true,
"reasons": [
"current_evidence_invalid"
"current_evidence_invalid",
"verdict_not_production_ready"
]
},
"verified_ready": {
"deploy_gate_allowed": true,
"evidence_merkle_root": "438efda29a515669d0db0472f6527c201c55d0d80afebf2963123d11c77a7d73",
"passed": true,
"payload_sha256": "db3fe179c7c950a9ffaf90cfef6d475fc915af87f0d1b10a85071c7d8411b975",
"signing_key_id": "ed25519:4592da038060823a957506d477e49114",
"verdict": "PRODUCTION_READY"
}
},
"schema_version": "1.0.0"
Expand Down
22 changes: 11 additions & 11 deletions artifacts/p6_acceptance.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"phase": "P6",
"title": "deployment enforcement and platform integration \u2014 live local acceptance",
"generated_at": "2026-07-26T02:18:04.411111Z",
"python": "3.13.0 (tags/v3.13.0:60403a5, Oct 7 2024, 09:38:07) [MSC v.1941 64 bit (AMD64)]",
"generated_at": "2026-08-30T12:40:31.960114Z",
"python": "3.12.10 (tags/v3.12.10:0cc8128, Apr 8 2025, 12:21:36) [MSC v.1943 64 bit (AMD64)]",
"passed": true,
"checks": [
{
Expand All @@ -11,7 +11,7 @@
"passed": true,
"detail": {
"exit": 2,
"stdout": "{\"admission_id\": \"dep-3413cd5f5334d3b106b9ce81f7c7a26b\", \"allowed\": false, \"artifact_sha256\": \"cc9a65d96c25c8f96ba720e01628b734d6df4f5a1057238506ab6d504fb998fd\", \"chain_hash\": \"ab88eb7a583dc68353ca39d53cd4e37d9737ac4b6fa0e6c9b0ab8c659325a64d\", \"created_at\": \"2026-07-26T02:17:59.537952Z\", \"deployment_environment\": \"production\", \"deployment_id\": \"deploy-a1\", \"reasons\": [\"artifact_not_certified\", \"staging_acceptance_missing\"], \"rollback_candidate\": null, \"run_id\": null, \"tenant_id\": \"tenant-alpha\"}"
"stdout": "{\"admission_id\": \"dep-39149ed9c3643c355bbe8f4f09eec260\", \"allowed\": false, \"artifact_sha256\": \"cc9a65d96c25c8f96ba720e01628b734d6df4f5a1057238506ab6d504fb998fd\", \"chain_hash\": \"ff5f335a43355fdc85e2b64c11e7429ad3aa9070dca78655a74bf0d929da28b0\", \"created_at\": \"2026-08-30T12:40:27.987414Z\", \"deployment_environment\": \"production\", \"deployment_id\": \"deploy-a1\", \"reasons\": [\"artifact_not_certified\", \"staging_acceptance_missing\"], \"rollback_candidate\": null, \"run_id\": null, \"tenant_id\": \"tenant-alpha\"}"
}
},
{
Expand All @@ -29,7 +29,7 @@
"passed": true,
"detail": {
"exit": 2,
"stdout": "{\"admission_id\": \"dep-f49980b48837ae58dec6fa3ffbc216e4\", \"allowed\": false, \"artifact_sha256\": \"11d7d626a19af7cf0b64c0862b3214a15fac77e3aa1400d70e6902f1a77fdb86\", \"chain_hash\": \"40c1d83374649745ee6fcd3c42c58ce5b6257e362aa6e22954f224c7284b61f5\", \"created_at\": \"2026-07-26T02:17:59.744469Z\", \"deployment_environment\": \"staging\", \"deployment_id\": \"deploy-a2\", \"reasons\": [\"artifact_not_certified\"], \"rollback_candidate\": null, \"run_id\": null, \"tenant_id\": \"tenant-alpha\"}"
"stdout": "{\"admission_id\": \"dep-de226c922ca25e4fe770043fa5cce489\", \"allowed\": false, \"artifact_sha256\": \"11d7d626a19af7cf0b64c0862b3214a15fac77e3aa1400d70e6902f1a77fdb86\", \"chain_hash\": \"1b08dbf5ba6527bbb6b743ab658e726bb487454d6ab95bd8ec1ec7889764501a\", \"created_at\": \"2026-08-30T12:40:28.129813Z\", \"deployment_environment\": \"staging\", \"deployment_id\": \"deploy-a2\", \"reasons\": [\"artifact_not_certified\"], \"rollback_candidate\": null, \"run_id\": null, \"tenant_id\": \"tenant-alpha\"}"
}
},
{
Expand All @@ -38,7 +38,7 @@
"passed": true,
"detail": {
"exit": 2,
"stdout": "{\"admission_id\": \"dep-8ac38cbd2ade07ff1bb5c59d63d2582c\", \"allowed\": false, \"artifact_sha256\": \"67b7548d601f8ca9e8689fced7d40d8f407894ac7d6a2c09d2e524ee5514e7b0\", \"chain_hash\": \"5d31c6a43518753dc06259bee965f1e8fb91e4114f86c47bf61adde8a84ff4dd\", \"created_at\": \"2026-07-26T02:17:59.912909Z\", \"deployment_environment\": \"production\", \"deployment_id\": \"deploy-a4\", \"reasons\": [\"staging_acceptance_missing\"], \"rollback_candidate\": null, \"run_id\": \"cert-p6-v1\", \"tenant_id\": \"tenant-alpha\"}"
"stdout": "{\"admission_id\": \"dep-393b3f1c13aeaf883a659c67fe3a6062\", \"allowed\": false, \"artifact_sha256\": \"67b7548d601f8ca9e8689fced7d40d8f407894ac7d6a2c09d2e524ee5514e7b0\", \"chain_hash\": \"4adc5c6ca6c45d5664c517904d6b86304b4ae573fa525cef6397e6a315d30422\", \"created_at\": \"2026-08-30T12:40:28.260911Z\", \"deployment_environment\": \"production\", \"deployment_id\": \"deploy-a4\", \"reasons\": [\"staging_acceptance_missing\"], \"rollback_candidate\": null, \"run_id\": \"cert-p6-v1\", \"tenant_id\": \"tenant-alpha\"}"
}
},
{
Expand All @@ -57,21 +57,21 @@
"passed": true,
"detail": {
"failure_candidate": {
"admission_id": "dep-c3f042d865f9fcd3c85e9d0a1c91484a",
"admitted_at": "2026-07-26T02:18:00.438507Z",
"admission_id": "dep-88177f711ac3ea76d71d3f5657753a8a",
"admitted_at": "2026-08-30T12:40:28.640308Z",
"artifact_sha256": "67b7548d601f8ca9e8689fced7d40d8f407894ac7d6a2c09d2e524ee5514e7b0",
"deployment_id": "deploy-a3-prd",
"run_id": "cert-p6-v1"
},
"rollback_target": {
"artifact_sha256": "67b7548d601f8ca9e8689fced7d40d8f407894ac7d6a2c09d2e524ee5514e7b0",
"run_id": "cert-p6-v1",
"admission_id": "dep-c3f042d865f9fcd3c85e9d0a1c91484a",
"admission_id": "dep-88177f711ac3ea76d71d3f5657753a8a",
"deployment_id": "deploy-a3-prd",
"admitted_at": "2026-07-26T02:18:00.438507Z"
"admitted_at": "2026-08-30T12:40:28.640308Z"
},
"rolled_back": {
"admission_id": "dep-d1f547a64a4ddb086ca7a521e0d1a4ce",
"admission_id": "dep-368a34d3be4daeb9870a2b93e41ebf45",
"artifact_sha256": "6cf9eb2e75fbaf4037393188b98be82a05499dcfc0c45a6eeeb840bd7f775c20",
"deployment_environment": "production",
"detail": "restored v1",
Expand All @@ -87,7 +87,7 @@
"passed": true,
"detail": {
"exit": 2,
"stdout": "{\"admission_id\": \"dep-4ec864d16bf5447abf4145975d62ed0a\", \"allowed\": false, \"artifact_sha256\": \"67b7548d601f8ca9e8689fced7d40d8f407894ac7d6a2c09d2e524ee5514e7b0\", \"chain_hash\": \"928bee7494d599c95422e4d3f6c8db2a7e80c2ec1ff5837056aead46be211115\", \"created_at\": \"2026-07-26T02:18:01.842335Z\", \"deployment_environment\": \"production\", \"deployment_id\": \"deploy-a5\", \"reasons\": [\"verdict_revoked\"], \"rollback_candidate\": null, \"run_id\": \"cert-p6-v1\", \"tenant_id\": \"tenant-alpha\"}"
"stdout": "{\"admission_id\": \"dep-1d051283684978484ff174d2f3cc5b7b\", \"allowed\": false, \"artifact_sha256\": \"67b7548d601f8ca9e8689fced7d40d8f407894ac7d6a2c09d2e524ee5514e7b0\", \"chain_hash\": \"8cbb0ce94045d583aaaaa7e150f5a997759f188f1de824d89f973711ef9a5754\", \"created_at\": \"2026-08-30T12:40:29.545903Z\", \"deployment_environment\": \"production\", \"deployment_id\": \"deploy-a5\", \"reasons\": [\"verdict_revoked\"], \"rollback_candidate\": null, \"run_id\": \"cert-p6-v1\", \"tenant_id\": \"tenant-alpha\"}"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion artifacts/p6_acceptance.summary.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"passed": true,
"checks_total": 12,
"checks_passed": 12,
"generated_at": "2026-07-26T02:18:04.411111Z"
"generated_at": "2026-08-30T12:40:31.960114Z"
}
29 changes: 28 additions & 1 deletion contracts/certforge-sdk-capabilities.v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -1412,6 +1412,33 @@
"type": "object"
}
},
"echo.certforge.publish": {
"input_schema": {
"additionalProperties": false,
"properties": {
"command": {
"maxLength": 64,
"minLength": 1,
"type": "string"
},
"run_id": {
"type": "string"
}
},
"required": [
"command",
"run_id"
],
"type": "object"
},
"operation": "POST /v1/subscriber/certifications/{run_id}/publish",
"output_schema": {
"additionalProperties": {
"type": "string"
},
"type": "object"
}
},
"echo.certforge.r5.status": {
"input_schema": {
"additionalProperties": false,
Expand Down Expand Up @@ -2295,7 +2322,7 @@
}
}
},
"capability_count": 60,
"capability_count": 61,
"policy": "Every Certification Forge SDK action has a command-bearing strict input schema and an output schema derived from its production route contract.",
"schema_version": "1.0.0"
}
21 changes: 20 additions & 1 deletion contracts/schemas/certification-run.v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"$id": "https://cert.echoforge.com/contracts/certification-run.v1.json",
"title": "CertificationRunV1",
"type": "object",
"required": ["run_id", "tenant_id", "state", "run_outcome", "release_verdict", "target_identity_digest", "environment_identity_digest", "policy_version"],
"required": ["run_id", "tenant_id", "state", "run_outcome", "release_verdict", "target_identity_digest", "environment_identity_digest", "policy_version", "production_e2e"],
"properties": {
"run_id": {"type": "string", "minLength": 1, "maxLength": 128},
"tenant_id": {"type": "string", "minLength": 1, "maxLength": 128},
Expand All @@ -14,6 +14,25 @@
"environment_identity_digest": {"type": "string", "pattern": "^[0-9a-f]{64}$"},
"policy_version": {"type": "string", "minLength": 1, "maxLength": 128},
"evidence_merkle_root": {"type": ["string", "null"], "pattern": "^[0-9a-f]{64}$"},
"production_e2e": {
"type": "object",
"required": ["verified", "profile", "attestation_id", "source_commit", "deployment_sha", "target_identity_digest", "environment_identity_digest", "canonical_target", "tool_count", "signature_verified", "collector_key_id", "attestation_envelope_sha256"],
"properties": {
"verified": {"type": "boolean"},
"profile": {"type": ["string", "null"]},
"attestation_id": {"type": ["string", "null"]},
"source_commit": {"type": ["string", "null"], "pattern": "^[0-9a-f]{40}(?:[0-9a-f]{24})?$"},
"deployment_sha": {"type": ["string", "null"], "pattern": "^[0-9a-f]{40}(?:[0-9a-f]{24})?$"},
"target_identity_digest": {"type": ["string", "null"], "pattern": "^[0-9a-f]{64}$"},
"environment_identity_digest": {"type": ["string", "null"], "pattern": "^[0-9a-f]{64}$"},
"canonical_target": {"type": ["string", "null"]},
"tool_count": {"type": ["integer", "null"], "minimum": 0},
"signature_verified": {"type": "boolean"},
"collector_key_id": {"type": ["string", "null"], "pattern": "^ed25519:[0-9a-f]{32}$"},
"attestation_envelope_sha256": {"type": ["string", "null"], "pattern": "^[0-9a-f]{64}$"}
},
"additionalProperties": false
},
"created_at": {"type": "string", "format": "date-time"},
"updated_at": {"type": "string", "format": "date-time"}
},
Expand Down
34 changes: 28 additions & 6 deletions deploy/deploy_forge.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ CURRENT_LINK="${CERTFORGE_CURRENT_LINK:-/home/forge/echo-certification-forge-cur
STATE_ROOT="${CERTFORGE_STATE_ROOT:-/home/forge/echo-certification-forge/var}"
ADAPTER_DIR="${ECHO_CERTFORGE_PROD_ADAPTER_DIR:-$STATE_ROOT/p5}"
ADAPTER_MODE="${CERTFORGE_ADAPTER_MODE:-required}"
TRUSTED_MANIFEST_SHA256="${ECHO_CERTFORGE_TRUSTED_MANIFEST_SHA256:-7dc98e0e95e6dd2c000ec069a8c46c4d1d49a4fe869ad4eae25e059d103644f4}"
TRUSTED_MANIFEST_SHA256="${ECHO_CERTFORGE_TRUSTED_MANIFEST_SHA256:-965106b00917268d556b325719f26f5096e6c3746551658ffecb9fd4a95ec342}"
UNIT_PATH="/etc/systemd/system/$SERVICE.service"
DISPATCH_UNIT_PATH="/etc/systemd/system/$DISPATCH_SERVICE.service"
RELEASE_DROPIN="/etc/systemd/system/$SERVICE.service.d/10-release.conf"
Expand Down Expand Up @@ -173,6 +173,28 @@ set -a
set +a
PROD_PEPPER="${ECHO_CERTFORGE_API_KEY_PEPPER:-}"
STAGING_PEPPER="${ECHO_CERTFORGE_STAGING_API_KEY_PEPPER:-}"
PROD_DB_PATH="${ECHO_CERTFORGE_DB:-}"
if [ -z "$PROD_DB_PATH" ]; then
CURRENT_SERVICE_PID="$(systemctl show "$SERVICE.service" --property MainPID --value 2>/dev/null || true)"
if [ "${CURRENT_SERVICE_PID:-0}" -gt 1 ] 2>/dev/null && \
[ -r "/proc/$CURRENT_SERVICE_PID/environ" ]; then
PROD_DB_PATH="$(python3 - "$CURRENT_SERVICE_PID" <<'PY'
from pathlib import Path
import sys

for entry in Path(f"/proc/{sys.argv[1]}/environ").read_bytes().split(b"\0"):
if entry.startswith(b"ECHO_CERTFORGE_DB="):
print(entry.split(b"=", 1)[1].decode("utf-8"))
break
PY
)"
fi
fi
PROD_DB_PATH="${PROD_DB_PATH:-$STATE_ROOT/certforge.sqlite3}"
case "$(realpath -m "$PROD_DB_PATH")" in
"$STATE_ROOT"/*|/mnt/documentation/echo/certforge/*) ;;
*) echo "!! effective production database is outside an approved state root"; exit 1 ;;
esac
test "${#PROD_PEPPER}" -ge 32 || {
echo "!! production ECHO_CERTFORGE_API_KEY_PEPPER must be at least 32 bytes"
exit 1
Expand Down Expand Up @@ -305,7 +327,7 @@ if sudo test -f "$DISPATCH_RELEASE_DROPIN"; then
sudo cat "$DISPATCH_RELEASE_DROPIN" >"$DISPATCH_RELEASE_DROPIN_BACKUP"
HAD_DISPATCH_RELEASE_DROPIN=1
fi
DB_PATH="$STATE_ROOT/certforge.sqlite3"
DB_PATH="$PROD_DB_PATH"
DB_BACKUP="$STATE_ROOT/deploy-scratch/echo-certforge-db.$RELEASE_ID"
HAD_DB=0
DB_SNAPSHOT_READY=0
Expand Down Expand Up @@ -503,7 +525,7 @@ Type=simple
User=forge
WorkingDirectory=$CURRENT_LINK
Environment=PYTHONUNBUFFERED=1
Environment=ECHO_CERTFORGE_DB=$STATE_ROOT/certforge.sqlite3
Environment=ECHO_CERTFORGE_DB=$DB_PATH
Environment=ECHO_CERTFORGE_EVIDENCE_ROOT=$STATE_ROOT/evidence
Environment=ECHO_CERTFORGE_POLICY=$CURRENT_LINK/policies/mandatory-rules.v2.json
Environment=ECHO_CERTFORGE_SUBSCRIBER_POLICY=$CURRENT_LINK/policies/subscriber-governance.v1.json
Expand Down Expand Up @@ -546,7 +568,7 @@ Type=simple
User=forge
WorkingDirectory=$CURRENT_LINK
Environment=PYTHONUNBUFFERED=1
Environment=ECHO_CERTFORGE_DB=$STATE_ROOT/certforge.sqlite3
Environment=ECHO_CERTFORGE_DB=$DB_PATH
Environment=ECHO_CERTFORGE_EVIDENCE_ROOT=$STATE_ROOT/evidence
Environment=ECHO_CERTFORGE_POLICY=$CURRENT_LINK/policies/mandatory-rules.v2.json
Environment=ECHO_CERTFORGE_SUBSCRIBER_POLICY=$CURRENT_LINK/policies/subscriber-governance.v1.json
Expand Down Expand Up @@ -593,7 +615,7 @@ for _ in $(seq 1 40); do
}
sleep 0.5
done
if [ "$ready" != 1 ] || ! ECHO_CERTFORGE_DB="$STATE_ROOT/certforge.sqlite3" \
if [ "$ready" != 1 ] || ! ECHO_CERTFORGE_DB="$DB_PATH" \
ECHO_CERTFORGE_SUBSCRIBER_POLICY="$RELEASE_DIR/policies/subscriber-governance.v1.json" \
ECHO_CERTFORGE_API_KEY_PEPPER="$PROD_PEPPER" \
ECHO_CERTFORGE_EXPECT_PRODUCT_READY=1 \
Expand Down Expand Up @@ -621,7 +643,7 @@ if [ "$dispatcher_ready" != 1 ]; then
exit 1
fi
if [ "$ADAPTER_MODE" = required ]; then
ECHO_CERTFORGE_DB="$STATE_ROOT/certforge.sqlite3" \
ECHO_CERTFORGE_DB="$DB_PATH" \
ECHO_CERTFORGE_SUBSCRIBER_POLICY="$RELEASE_DIR/policies/subscriber-governance.v1.json" \
ECHO_CERTFORGE_API_KEY_PEPPER="$PROD_PEPPER" \
"$RELEASE_DIR/.venv/bin/python" "$RELEASE_DIR/deploy/smoke_dispatch.py" \
Expand Down
Loading
Loading