From 55768adbe8f7f762acf5f24ecd59b8f3ea7d5f1f Mon Sep 17 00:00:00 2001 From: Sam Gbafa Date: Tue, 13 Oct 2026 14:28:13 +0000 Subject: [PATCH 1/5] deploy(TC-1012): stop publishing node :8000 on the prod CVM; verify gateway does not expose it https://linear.app/tinycloud-labs/issue/TC-1012 --- .github/workflows/docker.yml | 21 +++++++++++++++++++++ docker-compose.dstack-postgres.yaml | 6 ++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index efe1a637..67694467 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -759,3 +759,24 @@ jobs: echo "${route}: mounted (HTTP ${STATUS})" rm -f "${RESPONSE_FILE}" done + + # TC-1012: the node's :8000 must only be reachable through dstack-ingress. + # If the compose publishes it, the Phala gateway serves it directly at + # -8000. and bypasses the ingress limits/logs. + # An unpublished port closes the TLS stream with no HTTP response (000). + - name: Verify app port is not exposed by the Phala gateway + run: | + set -euo pipefail + # tinycloud-node prod CVM app_id on dstack-pha-prod5. + RAW_URL="https://350d124c81d53a57e8899e9f85a904918e663b61-8000.dstack-pha-prod5.phala.network/version" + INGRESS_STATUS="$(curl -sS --max-time 15 -o /dev/null -w '%{http_code}' https://tee.node.tinycloud.xyz/version || true)" + if [ "${INGRESS_STATUS}" != "200" ]; then + echo "::error::TC-1012: https://tee.node.tinycloud.xyz/version returned HTTP ${INGRESS_STATUS}, expected 200 via dstack-ingress." + exit 1 + fi + RAW_STATUS="$(curl -sS --max-time 15 -o /dev/null -w '%{http_code}' "${RAW_URL}" || true)" + if [ "${RAW_STATUS}" != "000" ]; then + echo "::error::TC-1012: ${RAW_URL} returned HTTP ${RAW_STATUS}; port 8000 is published and bypasses dstack-ingress." + exit 1 + fi + echo "Ingress serves /version (200); gateway app port 8000 gives no HTTP response." diff --git a/docker-compose.dstack-postgres.yaml b/docker-compose.dstack-postgres.yaml index 9afa4196..2792f6e1 100644 --- a/docker-compose.dstack-postgres.yaml +++ b/docker-compose.dstack-postgres.yaml @@ -21,8 +21,10 @@ services: options: max-size: "50m" max-file: "3" - ports: - - "8000:8000" + # TC-1012: no `ports:` on purpose. A published port is served by the Phala + # gateway at https://-8000./, bypassing dstack-ingress + # (header/body limits, redacted access log, our TLS). The ingress reaches + # the node over the compose network at http://tinycloud:8000. volumes: - /var/run/dstack.sock:/var/run/dstack.sock environment: From 7f1be8e50ecdd8723cb919543ca511843d8c5fa6 Mon Sep 17 00:00:00 2001 From: Sam Gbafa Date: Tue, 13 Oct 2026 14:40:35 +0000 Subject: [PATCH 2/5] TC-1012: classify gateway probe failures --- .github/workflows/docker.yml | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 67694467..4b2ba1be 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -763,7 +763,8 @@ jobs: # TC-1012: the node's :8000 must only be reachable through dstack-ingress. # If the compose publishes it, the Phala gateway serves it directly at # -8000. and bypasses the ingress limits/logs. - # An unpublished port closes the TLS stream with no HTTP response (000). + # A closed port makes the gateway accept TLS and then close the stream + # (curl exit 52/56, no HTTP status). Other curl errors prove nothing. - name: Verify app port is not exposed by the Phala gateway run: | set -euo pipefail @@ -774,9 +775,26 @@ jobs: echo "::error::TC-1012: https://tee.node.tinycloud.xyz/version returned HTTP ${INGRESS_STATUS}, expected 200 via dstack-ingress." exit 1 fi - RAW_STATUS="$(curl -sS --max-time 15 -o /dev/null -w '%{http_code}' "${RAW_URL}" || true)" - if [ "${RAW_STATUS}" != "000" ]; then - echo "::error::TC-1012: ${RAW_URL} returned HTTP ${RAW_STATUS}; port 8000 is published and bypasses dstack-ingress." + CLOSED=0 + RC=0 + for attempt in $(seq 1 10); do + RC=0 + RAW_STATUS="$(curl -sS --max-time 15 -o /dev/null -w '%{http_code}' "${RAW_URL}" 2>/tmp/tc1012-curl.err)" || RC=$? + if [ "${RAW_STATUS}" != "000" ]; then + echo "::error::TC-1012: ${RAW_URL} returned HTTP ${RAW_STATUS}; port 8000 is published and bypasses dstack-ingress." + exit 1 + fi + if [ "${RC}" = 52 ] || [ "${RC}" = 56 ]; then + CLOSED=$((CLOSED + 1)) + [ "${CLOSED}" -ge 3 ] && break + else + echo "TC-1012 probe inconclusive (curl exit ${RC}): $(cat /tmp/tc1012-curl.err)" + CLOSED=0 + fi + sleep 5 + done + if [ "${CLOSED}" -lt 3 ]; then + echo "::error::TC-1012: could not confirm ${RAW_URL} is closed (last curl exit ${RC})." exit 1 fi - echo "Ingress serves /version (200); gateway app port 8000 gives no HTTP response." + echo "Ingress serves /version (200); gateway app port 8000 is closed (curl exit ${RC}, ${CLOSED} consecutive)." From e99855a75ccc0470c9da6ae075f733b0a16497a0 Mon Sep 17 00:00:00 2001 From: Sam Gbafa Date: Tue, 13 Oct 2026 14:47:33 +0000 Subject: [PATCH 3/5] TC-1012: refuse deploy while node.tinycloud.xyz bypasses the ingress --- .github/workflows/docker.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 4b2ba1be..ae4e7e35 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -574,6 +574,27 @@ jobs: -e TINYCLOUD_KEYS__TYPE \ "${IMAGE}" --validate-config + # TC-1012: this compose no longer publishes :8000, so the gateway stops + # serving -8000. node.tinycloud.xyz (the SDK default) must already + # go through dstack-ingress, or replacing the CVM takes it down. Only the + # ingress serves /evidences/ (the node itself returns 404), so a 200 here + # proves the worker was repointed. Refuse before touching the CVM. + - name: Require node.tinycloud.xyz to route through the ingress + run: | + set -euo pipefail + STATUS="000" + for attempt in 1 2 3; do + STATUS="$(curl -sS --max-time 15 -o /dev/null -w '%{http_code}' https://node.tinycloud.xyz/evidences/ || true)" + if [ "${STATUS}" = "200" ]; then + echo "node.tinycloud.xyz routes through the ingress (/evidences/ 200)." + exit 0 + fi + echo "attempt ${attempt}/3: node.tinycloud.xyz/evidences/ returned HTTP ${STATUS}" + [ "${attempt}" = 3 ] || sleep 5 + done + echo "::error::TC-1012: node.tinycloud.xyz still bypasses the ingress (/evidences/ HTTP ${STATUS}); deploy the tinycloud-dev worker repoint (samgbafa/tinycloud-dev#83) first" + exit 1 + - name: Deploy to Phala Cloud env: PHALA_CLOUD_API_KEY: ${{ secrets.PHALA_CLOUD_API_KEY }} From a58e79daad362b380046f0e3a682ee00810c89a1 Mon Sep 17 00:00:00 2001 From: Sam Gbafa Date: Tue, 13 Oct 2026 15:00:43 +0000 Subject: [PATCH 4/5] TC-1012: gate only on the raw-port 404 --- .github/workflows/docker.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ae4e7e35..93a27871 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -577,8 +577,10 @@ jobs: # TC-1012: this compose no longer publishes :8000, so the gateway stops # serving -8000. node.tinycloud.xyz (the SDK default) must already # go through dstack-ingress, or replacing the CVM takes it down. Only the - # ingress serves /evidences/ (the node itself returns 404), so a 200 here - # proves the worker was repointed. Refuse before touching the CVM. + # ingress serves /evidences/ (the node itself returns 404), so a 404 here + # means the worker still points at the raw port: refuse before touching + # the CVM. Any other non-200 (e.g. a broken ingress) only warns, so the + # gate cannot block the redeploy that fixes it. - name: Require node.tinycloud.xyz to route through the ingress run: | set -euo pipefail @@ -592,8 +594,11 @@ jobs: echo "attempt ${attempt}/3: node.tinycloud.xyz/evidences/ returned HTTP ${STATUS}" [ "${attempt}" = 3 ] || sleep 5 done - echo "::error::TC-1012: node.tinycloud.xyz still bypasses the ingress (/evidences/ HTTP ${STATUS}); deploy the tinycloud-dev worker repoint (samgbafa/tinycloud-dev#83) first" - exit 1 + if [ "${STATUS}" = "404" ]; then + echo "::error::TC-1012: node.tinycloud.xyz still bypasses the ingress (/evidences/ HTTP 404); deploy the tinycloud-dev worker repoint (samgbafa/tinycloud-dev#83) first" + exit 1 + fi + echo "::warning::TC-1012: node.tinycloud.xyz/evidences/ returned HTTP ${STATUS} (not the raw-port 404); proceeding with deploy" - name: Deploy to Phala Cloud env: From 61450f8e42443c81995b1c97c0d430b8b46dbcb4 Mon Sep 17 00:00:00 2001 From: Sam Gbafa Date: Tue, 13 Oct 2026 15:11:11 +0000 Subject: [PATCH 5/5] TC-1012: refuse on any raw-port 404 --- .github/workflows/docker.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 93a27871..23ca73e9 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -591,14 +591,14 @@ jobs: echo "node.tinycloud.xyz routes through the ingress (/evidences/ 200)." exit 0 fi + if [ "${STATUS}" = "404" ]; then + echo "::error::TC-1012: node.tinycloud.xyz still bypasses the ingress (/evidences/ HTTP 404); deploy the tinycloud-dev worker repoint (samgbafa/tinycloud-dev#83) first" + exit 1 + fi echo "attempt ${attempt}/3: node.tinycloud.xyz/evidences/ returned HTTP ${STATUS}" [ "${attempt}" = 3 ] || sleep 5 done - if [ "${STATUS}" = "404" ]; then - echo "::error::TC-1012: node.tinycloud.xyz still bypasses the ingress (/evidences/ HTTP 404); deploy the tinycloud-dev worker repoint (samgbafa/tinycloud-dev#83) first" - exit 1 - fi - echo "::warning::TC-1012: node.tinycloud.xyz/evidences/ returned HTTP ${STATUS} (not the raw-port 404); proceeding with deploy" + echo "::warning::TC-1012: node.tinycloud.xyz/evidences/ returned HTTP ${STATUS} (no 200, no raw-port 404 in 3 attempts); proceeding with deploy" - name: Deploy to Phala Cloud env: