From f30766c7f4c3420e9159d00009974c3e81e1e8b3 Mon Sep 17 00:00:00 2001 From: BossChaos Date: Tue, 5 May 2026 02:51:01 +0800 Subject: [PATCH 1/2] ci: disable workflow_dispatch for bottube-digest-bot (missing secrets) --- .github/workflows/bottube-digest-bot.yml | 52 ++++++++++++------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/bottube-digest-bot.yml b/.github/workflows/bottube-digest-bot.yml index d5a0cdfd2..0699e46b1 100644 --- a/.github/workflows/bottube-digest-bot.yml +++ b/.github/workflows/bottube-digest-bot.yml @@ -7,32 +7,32 @@ on: schedule: - cron: '0 9 * * MON' - # Allow manual trigger from GitHub Actions tab - workflow_dispatch: - inputs: - dry_run: - description: 'Run in dry-run mode (no actual sends)' - required: false - default: 'false' - type: choice - options: - - 'true' - - 'false' - send_discord: - description: 'Send to Discord' - required: false - default: 'true' - type: boolean - send_telegram: - description: 'Send to Telegram' - required: false - default: 'false' - type: boolean - send_email: - description: 'Send via Email' - required: false - default: 'false' - type: boolean + # Manual trigger disabled (requires secrets not configured in this fork) + # workflow_dispatch: + # inputs: + # dry_run: + # description: 'Run in dry-run mode (no actual sends)' + # required: false + # default: 'false' + # type: choice + # options: + # - 'true' + # - 'false' + # send_discord: + # description: 'Send to Discord' + # required: false + # default: 'true' + # type: boolean + # send_telegram: + # description: 'Send to Telegram' + # required: false + # default: 'false' + # type: boolean + # send_email: + # description: 'Send via Email' + # required: false + # default: 'false' + # type: boolean jobs: send-digest: From ccc3497eb830028863032b8e4ef25e199ba6935f Mon Sep 17 00:00:00 2001 From: BossChaos Date: Thu, 7 May 2026 13:17:19 +0800 Subject: [PATCH 2/2] fix: add API key authentication to GPU render escrow endpoints --- node/gpu_render_protocol.py | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/node/gpu_render_protocol.py b/node/gpu_render_protocol.py index aff802f56..356af2e80 100644 --- a/node/gpu_render_protocol.py +++ b/node/gpu_render_protocol.py @@ -26,6 +26,7 @@ import uuid import json import os +import hmac import logging from functools import wraps @@ -406,9 +407,23 @@ def register_routes(app): """Register GPU Render Protocol routes with a Flask app.""" protocol = GPURenderProtocol() + +def _require_api_key(): + """Validate X-API-Key header for escrow endpoints.""" + api_key = request.headers.get("X-API-Key", "") + expected_key = os.environ.get("RC_RENDER_API_KEY", "") + if not expected_key: + return None, (jsonify({"error": "RC_RENDER_API_KEY not configured"}), 503) + if not hmac.compare_digest(api_key, expected_key): + return None, (jsonify({"error": "Unauthorized"}), 401) + return api_key, None + @app.route("/gpu/attest", methods=["POST"]) def gpu_attest(): from flask import request, jsonify + _, err = _require_api_key() + if err: + return err data = request.get_json(force=True) miner_id = data.get("miner_id") if not miner_id: @@ -430,6 +445,9 @@ def gpu_nodes(): @app.route("/llm/escrow", methods=["POST"]) def create_escrow(): from flask import request, jsonify + _, err = _require_api_key() + if err: + return err data = request.get_json(force=True) # Infer job_type from path path = request.path @@ -455,16 +473,28 @@ def create_escrow(): @app.route("/llm/release", methods=["POST"]) def release_escrow(): from flask import request, jsonify + _, err = _require_api_key() + if err: + return err data = request.get_json(force=True) - result = protocol.release_escrow(data.get("job_id", "")) + job_id = data.get("job_id", "") + if not job_id: + return jsonify({"error": "job_id required"}), 400 + result = protocol.release_escrow(job_id) status_code = 200 if "error" not in result else 400 return jsonify(result), status_code @app.route("/render/refund", methods=["POST"]) def refund_escrow(): from flask import request, jsonify + _, err = _require_api_key() + if err: + return err data = request.get_json(force=True) - result = protocol.refund_escrow(data.get("job_id", "")) + job_id = data.get("job_id", "") + if not job_id: + return jsonify({"error": "job_id required"}), 400 + result = protocol.refund_escrow(job_id) status_code = 200 if "error" not in result else 400 return jsonify(result), status_code