-
Notifications
You must be signed in to change notification settings - Fork 0
fix(actions): migrate hourly product loop off direct NVIDIA NIM call #235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a2ef249
9ee190a
ab679bc
210b5c1
f5036ef
5af5afd
830404d
8c323d7
aa4cc16
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,17 +28,25 @@ jobs: | |
| changed: ${{ steps.package.outputs.changed }} | ||
| steps: | ||
| - name: Harden runner | ||
| # audit, not block: the model call now routes through the vendored | ||
| # contextual-orchestrator gateway's live multi-provider discovery | ||
| # (BYTEZ_API_KEY, NVIDIA_NIM_API_KEY[_SUB], OPENROUTER_API_KEY, | ||
| # OPENAI_API_KEY -- see "Provision the contextual-orchestrator gateway | ||
| # sidecar" below), which resolves its own outbound routes at runtime | ||
| # (including https://models.dev for free-tier pricing metadata) and | ||
| # has no fixed host set to pin. This is not a downgrade invented for | ||
| # this repo: it is the exact egress posture ContextualWisdomLab/.github | ||
| # already uses in production for the same sidecar in | ||
| # pr-review-autofix.yml and strix.yml -- no consumer of this sidecar | ||
| # anywhere in the org runs it under block-mode with a static | ||
| # allowlist, because that combination is not yet solved. The actual | ||
| # behavioral boundary against a prompt-injected model stays the | ||
| # deny-by-default OpenCode permission block below (webfetch, websearch, | ||
| # task, skill, lsp, and every bash command but three read-only ones all | ||
| # stay "deny"), which is unaffected by this runner-network setting. | ||
| uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 | ||
| with: | ||
| egress-policy: block | ||
| allowed-endpoints: > | ||
| api.github.com:443 | ||
| files.pythonhosted.org:443 | ||
| github.com:443 | ||
| integrate.api.nvidia.com:443 | ||
| objects.githubusercontent.com:443 | ||
| pypi.org:443 | ||
| release-assets.githubusercontent.com:443 | ||
| egress-policy: audit | ||
|
|
||
| - name: Check out the protected default branch | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | ||
|
|
@@ -66,17 +74,28 @@ jobs: | |
| echo "::notice::Skipping product development because ${open_prs} pull request(s) remain open." | ||
| fi | ||
|
|
||
| - name: Require the autonomous maintainer model credential | ||
| - name: Require the autonomous maintainer gateway credentials | ||
| if: steps.queue.outputs.develop == 'true' | ||
| env: | ||
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} | ||
| BYTEZ_API_KEY: ${{ secrets.BYTEZ_API_KEY }} | ||
| NVIDIA_NIM_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} | ||
| NVIDIA_NIM_API_KEY_SUB: ${{ secrets.NVIDIA_NIM_API_KEY_SUB }} | ||
| OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} | ||
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | ||
| run: | | ||
| set -euo pipefail | ||
| if [ -z "${NVIDIA_API_KEY:-}" ]; then | ||
| echo "::error::NVIDIA_NIM_API_KEY is required for hourly autonomous product development." | ||
| present=0 | ||
| for secret_name in BYTEZ_API_KEY NVIDIA_NIM_API_KEY NVIDIA_NIM_API_KEY_SUB OPENROUTER_API_KEY OPENAI_API_KEY; do | ||
| value="${!secret_name:-}" | ||
| if [ -n "$value" ]; then | ||
| echo "::add-mask::$value" | ||
| present=$((present + 1)) | ||
| fi | ||
| done | ||
| if [ "$present" -eq 0 ]; then | ||
| echo "::error::At least one of BYTEZ_API_KEY, NVIDIA_NIM_API_KEY, NVIDIA_NIM_API_KEY_SUB, OPENROUTER_API_KEY, OPENAI_API_KEY is required for hourly autonomous product development." | ||
| exit 1 | ||
| fi | ||
| echo "::add-mask::$NVIDIA_API_KEY" | ||
|
|
||
| - name: Set up Python | ||
| if: steps.queue.outputs.develop == 'true' | ||
|
|
@@ -133,12 +152,49 @@ jobs: | |
| ;; | ||
| esac | ||
|
|
||
| - name: Checkout the trusted contextual-orchestrator gateway sidecar source | ||
| if: steps.queue.outputs.develop == 'true' | ||
| env: | ||
| TRUSTED_GATEWAY_SOURCE_SHA: "6958918beaad96d0a67ce264706c828bb7f3f000" | ||
| run: | | ||
| set -euo pipefail | ||
| # Cloned outside $GITHUB_WORKSPACE (not via actions/checkout's | ||
| # workspace-relative `path:`) so this vendored tree never lands | ||
| # inside the git repository that "Enforce the autonomous change | ||
| # boundary" below diffs against the pristine baseline -- an | ||
| # untracked copy of an entire second repository there would be | ||
| # swept into the model's own patch. | ||
| source_dir="${RUNNER_TEMP}/trusted-gateway-source" | ||
| git clone --quiet https://github.com/ContextualWisdomLab/.github.git "$source_dir" | ||
| git -C "$source_dir" -c advice.detachedHead=false checkout --quiet "$TRUSTED_GATEWAY_SOURCE_SHA" | ||
| checked_out="$(git -C "$source_dir" rev-parse HEAD)" | ||
| if [ "$checked_out" != "$TRUSTED_GATEWAY_SOURCE_SHA" ]; then | ||
| echo "::error::vendored ContextualWisdomLab/.github HEAD ${checked_out} != pin ${TRUSTED_GATEWAY_SOURCE_SHA}" | ||
| exit 1 | ||
| fi | ||
| echo "TRUSTED_GATEWAY_SOURCE=$source_dir" >>"$GITHUB_ENV" | ||
|
|
||
| - name: Provision the contextual-orchestrator gateway sidecar | ||
| if: steps.queue.outputs.develop == 'true' | ||
| env: | ||
| BYTEZ_API_KEY: ${{ secrets.BYTEZ_API_KEY }} | ||
| NVIDIA_NIM_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} | ||
| NVIDIA_NIM_API_KEY_SUB: ${{ secrets.NVIDIA_NIM_API_KEY_SUB }} | ||
| OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} | ||
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | ||
| run: | | ||
| set -euo pipefail | ||
| # strix_runs/ is this sidecar's own evidence directory, hardcoded | ||
| # (by the vendored script) to land under $GITHUB_WORKSPACE; it is | ||
| # excluded via .gitignore for the same reason the vendored source | ||
| # checkout above is cloned outside the workspace. | ||
| bash "${TRUSTED_GATEWAY_SOURCE}/scripts/ci/contextual_orchestrator_review_sidecar.sh" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Gateway preflight exhausts development window The sidecar performs an unbounded model completion before OpenCode starts, while the job still ends after 45 minutes. Slow free routes can consume the entire run without producing a patch. Prompt for agentsWas this helpful? React with 👍 or 👎 to provide feedback. |
||
|
|
||
| - name: Run the bounded OpenCode autonomous maintainer | ||
| if: steps.queue.outputs.develop == 'true' | ||
| id: opencode | ||
| env: | ||
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} | ||
| OPENCODE_MODEL: "nvidia/nemotron-3-super-120b-a12b" | ||
| OPENCODE_MODEL: "contextual-orchestrator/orchestrator/free" | ||
| HOME: "${{ runner.temp }}/opencode-home" | ||
| XDG_CONFIG_HOME: "${{ runner.temp }}/opencode-home/config" | ||
| XDG_DATA_HOME: "${{ runner.temp }}/opencode-home/data" | ||
|
|
@@ -151,10 +207,17 @@ jobs: | |
| OPENCODE_DISABLE_CLAUDE_CODE: "true" | ||
| OPENCODE_AUTO_SHARE: "false" | ||
| OPENCODE_CONFIG_CONTENT: >- | ||
| {"$schema":"https://opencode.ai/config.json","model":"nvidia/nemotron-3-super-120b-a12b","small_model":"nvidia/nemotron-3-super-120b-a12b","share":"disabled","autoupdate":false,"provider":{"nvidia":{"options":{"baseURL":"https://integrate.api.nvidia.com/v1","apiKey":"{env:NVIDIA_API_KEY}"},"models":{"nemotron-3-super-120b-a12b":{"name":"NVIDIA Nemotron 3 Super 120B A12B","limit":{"context":1000000,"output":32768}}}}},"permission":{"*":"deny","read":{"*":"allow",".git/**":"deny","*.env":"deny","*.env.*":"deny"},"edit":{"*":"deny","src/egressweave/**":"allow","tests/**":"allow","docs/**":"allow","README.md":"allow","CHANGELOG.md":"allow"},"glob":"allow","grep":"allow","bash":{"*":"deny","git diff --check":"allow","git diff --stat":"allow","git status --short":"allow"},"external_directory":"deny","webfetch":"deny","websearch":"deny","question":"deny","task":"deny","skill":"deny"}} | ||
| {"$schema":"https://opencode.ai/config.json","model":"contextual-orchestrator/orchestrator/free","small_model":"contextual-orchestrator/orchestrator/free","share":"disabled","autoupdate":false,"provider":{"contextual-orchestrator":{"npm":"@ai-sdk/openai-compatible","name":"Contextual Orchestrator Gateway","options":{"baseURL":"{env:CONTEXTUAL_ORCHESTRATOR_BASE_URL}","apiKey":"{env:CONTEXTUAL_ORCHESTRATOR_TOKEN}"},"models":{"orchestrator/free":{"name":"Orchestrator Free (ZDR-first zero-cost pool)","tool_call":true,"reasoning":true,"limit":{"context":200000,"output":32768}}}}},"permission":{"*":"deny","read":{"*":"allow",".git/**":"deny","*.env":"deny","*.env.*":"deny"},"edit":{"*":"deny","src/egressweave/**":"allow","tests/**":"allow","docs/**":"allow","README.md":"allow","CHANGELOG.md":"allow"},"glob":"allow","grep":"allow","bash":{"*":"deny","git diff --check":"allow","git diff --stat":"allow","git status --short":"allow"},"external_directory":"deny","webfetch":"deny","websearch":"deny","question":"deny","task":"deny","skill":"deny"}} | ||
| run: | | ||
| set -euo pipefail | ||
| echo "::add-mask::$NVIDIA_API_KEY" | ||
| if [ -z "${CONTEXTUAL_ORCHESTRATOR_BASE_URL:-}" ] || [ -z "${CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE:-}" ]; then | ||
| echo "::error::The contextual-orchestrator gateway sidecar must be provisioned before the autonomous maintainer runs." | ||
| exit 1 | ||
| fi | ||
| # Loads and masks CONTEXTUAL_ORCHESTRATOR_TOKEN from the sidecar's | ||
| # owner/mode-validated token file into THIS step's shell only -- the | ||
| # raw bearer never crosses a step boundary via $GITHUB_ENV. | ||
| source "${TRUSTED_GATEWAY_SOURCE}/scripts/ci/load_contextual_orchestrator_token.sh" | ||
| mkdir -p "$HOME" "$XDG_CONFIG_HOME" "$XDG_DATA_HOME" "$XDG_CACHE_HOME" | ||
| chmod 0700 "$HOME" "$XDG_CONFIG_HOME" "$XDG_DATA_HOME" "$XDG_CACHE_HOME" | ||
| prompt_file="${RUNNER_TEMP}/opencode-prompt.md" | ||
|
|
@@ -181,16 +244,35 @@ jobs: | |
| - name: Reject model credential disclosure | ||
| if: steps.queue.outputs.develop == 'true' | ||
| env: | ||
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} | ||
| BYTEZ_API_KEY: ${{ secrets.BYTEZ_API_KEY }} | ||
| NVIDIA_NIM_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} | ||
| NVIDIA_NIM_API_KEY_SUB: ${{ secrets.NVIDIA_NIM_API_KEY_SUB }} | ||
| OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} | ||
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | ||
| run: | | ||
| set -euo pipefail | ||
| # None of the five real provider keys are ever this job's OpenCode | ||
| # process's own environment (only the gateway sidecar process ever | ||
| # holds them, seeded once into its in-memory KV); this scan is | ||
| # defense in depth in case the gateway ever echoed one back. The | ||
| # ephemeral per-run CONTEXTUAL_ORCHESTRATOR_TOKEN, which IS in that | ||
| # environment, is scanned for the same reason the old NVIDIA_API_KEY | ||
| # value was. | ||
| source "${TRUSTED_GATEWAY_SOURCE}/scripts/ci/load_contextual_orchestrator_token.sh" | ||
| result_file="${RUNNER_TEMP}/opencode-result.ndjson" | ||
| disclosure_file="${RUNNER_TEMP}/credential-disclosure-paths" | ||
| if grep -R -F -l -- "$NVIDIA_API_KEY" \ | ||
| src/egressweave tests docs README.md CHANGELOG.md "$result_file" \ | ||
| >"$disclosure_file"; then | ||
| echo "::error::The model disclosed its credential in the following path(s):" | ||
| sed 's/^/::error::/' "$disclosure_file" | ||
| : >"$disclosure_file" | ||
| for secret_name in BYTEZ_API_KEY NVIDIA_NIM_API_KEY NVIDIA_NIM_API_KEY_SUB \ | ||
| OPENROUTER_API_KEY OPENAI_API_KEY CONTEXTUAL_ORCHESTRATOR_TOKEN; do | ||
| value="${!secret_name:-}" | ||
| [ -n "$value" ] || continue | ||
| grep -R -F -l -- "$value" \ | ||
| src/egressweave tests docs README.md CHANGELOG.md "$result_file" \ | ||
| >>"$disclosure_file" || true | ||
| done | ||
| if [ -s "$disclosure_file" ]; then | ||
| echo "::error::The model disclosed a credential in the following path(s):" | ||
| sort -u -- "$disclosure_file" | sed 's/^/::error::/' | ||
| exit 1 | ||
| fi | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -68,6 +68,25 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | |
| constructors accept positive integers or ASCII decimal strings and reject | ||
| ambiguous or non-positive configuration before network I/O. | ||
|
|
||
| ### Changed | ||
| - Migrate the hourly product-development workflow off a direct, hardcoded | ||
| NVIDIA NIM API call and onto the org's governed contextual-orchestrator | ||
| gateway, pinned to `orchestrator/free`, following the same vendored-sidecar | ||
|
Comment on lines
+72
to
+74
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 현재 실행 경로와 충돌하는
🤖 Prompt for AI Agents |
||
| pattern already shipped for `ContextualWisdomLab/.github`'s OpenCode Review | ||
| and Strix jobs (`scripts/ci/contextual_orchestrator_review_sidecar.sh` | ||
| vendored at a pinned immutable commit). OpenCode's provider credential is | ||
| now an ephemeral, per-run, loopback-scoped bearer token; none of the five | ||
| bootstrap-only provider secrets (`BYTEZ_API_KEY`, `NVIDIA_NIM_API_KEY`, | ||
| `NVIDIA_NIM_API_KEY_SUB`, `OPENROUTER_API_KEY`, `OPENAI_API_KEY`) ever | ||
| reach the model-consuming process's own environment. The `develop` job's | ||
| runner egress policy moves from block-mode with a static host allowlist to | ||
| audit-mode, matching the only production precedent for this sidecar | ||
| anywhere in the org, because the sidecar's live multi-provider discovery | ||
| has no fixed host set to pin; the deny-by-default OpenCode permission | ||
| boundary (no webfetch, websearch, task, skill, lsp, or unlisted bash) is | ||
| unaffected and remains the actual control against a prompt-injected model. | ||
| See ContextualWisdomLab/EgressWeave#234. | ||
|
|
||
| ### Fixed | ||
| - Restore the hourly PR-maintenance calls to the reviewed immutable | ||
| `ContextualWisdomLab/.github` revision | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -344,9 +344,11 @@ product roadmap moving without bypassing normal governance: | |
| - at minute `07`, the repository calls the organization-owned review-fix and | ||
| merge schedulers to inspect feedback, recheck current-head evidence, and | ||
| update eligible branches; final merges remain operator-controlled; | ||
| - at minute `37`, a bounded OpenCode maintainer backed by | ||
| `NVIDIA_NIM_API_KEY` runs only when there are zero open pull requests and | ||
| implements one test-driven improvement. | ||
| - at minute `37`, a bounded OpenCode maintainer backed by the org's governed | ||
| contextual-orchestrator gateway (pinned to `orchestrator/free`, provisioned | ||
| from `BYTEZ_API_KEY`, `NVIDIA_NIM_API_KEY`, `NVIDIA_NIM_API_KEY_SUB`, | ||
| `OPENROUTER_API_KEY`, and `OPENAI_API_KEY`) runs only when there are zero | ||
| open pull requests and implements one test-driven improvement. | ||
|
Comment on lines
+347
to
+351
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
workflow=".github/workflows/hourly-product-development.yml"
# OpenCode와 gateway sidecar의 네트워크 및 자격 증명 경계를 확인합니다.
rg -n -C 6 \
'egress-policy:|contextual-orchestrator|CONTEXTUAL_ORCHESTRATOR_TOKEN|opencode run' \
"$workflow"Repository: ContextualWisdomLab/EgressWeave Length of output: 8525 🏁 Script executed: sed -n '338,360p' README.mdRepository: ContextualWisdomLab/EgressWeave Length of output: 1531 Security Misconfiguration (CWE-16) Exploitability: Theoretical
현재 문장은 전체 🤖 Prompt for AI Agents |
||
|
|
||
| The product workflow uses two fresh runners. The model job has read-only GitHub | ||
| permissions, no direct network access, and can emit only a guard-checked patch. | ||
|
Comment on lines
353
to
354
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟨 Credentialed runner allows unrestricted egress
Audit mode permits every process on the credentialed model runner to reach arbitrary hosts. A compromised tool or dependency can exfiltrate provider credentials or generated source.
Was this helpful? React with 👍 or 👎 to provide feedback.