Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e95fed3
ci(strix): bootstrap orchestrator gateway patch
seonghobae Aug 28, 2026
e98d08e
fix(ci): make Strix bootstrap workflow parseable
seonghobae Aug 28, 2026
f73486e
ci(strix): bootstrap orchestrator gateway patch
seonghobae Aug 28, 2026
2e1db52
ci(strix): rerun orchestrator gateway patch
seonghobae Aug 28, 2026
3af96b4
ci(strix): rerun orchestrator gateway patch
seonghobae Aug 28, 2026
6a2eddb
fix(strix): route default scans through contextual-orchestrator
seonghobae Aug 28, 2026
2a904b1
fix(strix): hash-lock orchestrator sidecar dependencies
seonghobae Aug 28, 2026
198db58
fix(strix): preserve dispatch and private-source routing boundaries
seonghobae Aug 28, 2026
5ff0e96
fix(strix): exclude the requested NVIDIA diagnostic model
seonghobae Aug 28, 2026
5b37c9b
fix(strix): restore exact quick-gate source bytes
seonghobae Aug 28, 2026
48f893b
fix(strix): correct diagnostic fallback warning
seonghobae Aug 28, 2026
6beae6b
fix(strix): mask gateway token before sidecar startup
seonghobae Aug 28, 2026
4c2bdcb
fix(strix): syntax-check every required smoke script
seonghobae Aug 28, 2026
5f1c1da
Merge main into Strix remediation branch
seonghobae Aug 28, 2026
2ef49da
Merge protected main and retain Strix gateway hardening
seonghobae Aug 28, 2026
30540d2
Merge branch 'main' into feat/strix-orchestrator-free-zdr
seonghobae Aug 28, 2026
cc6bb05
test(strix): bind syntax regression to sidecar check
seonghobae Aug 28, 2026
4a694d5
docs(strix): record integrated catalog fix state
seonghobae Aug 28, 2026
4112970
fix(review): keep orchestrator bearer out of step env
seonghobae Aug 28, 2026
dd46e76
fix(strix): qualify gateway child model
seonghobae Aug 28, 2026
0dabda5
fix(ci): restore complete Strix gate fixture
seonghobae Aug 28, 2026
d9ecfaf
test(strix): isolate malformed-sidecar smoke failure
seonghobae Aug 28, 2026
986cfc2
Merge protected main into gateway credential repair
seonghobae Aug 28, 2026
7d24923
fix(review): isolate token loader shell state
seonghobae Aug 28, 2026
dc03010
fix(review): mask gateway tokens only on Actions
seonghobae Aug 28, 2026
7b3c3b2
test(review): isolate non-Actions mask fixture
seonghobae Aug 28, 2026
2c9588e
Merge branch 'main' into feat/strix-orchestrator-free-zdr
seonghobae Aug 28, 2026
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
94 changes: 76 additions & 18 deletions .github/workflows/strix.yml
Comment thread
seonghobae marked this conversation as resolved.
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Comment thread
seonghobae marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,22 @@ jobs:
printf 'Materialized central Strix dependency lock from same-repository PR head.\n'
fi

- name: Provision contextual-orchestrator Strix sidecar
if: >-
github.event_name != 'repository_dispatch'
|| github.event.client_payload.strix_llm == ''
|| github.event.client_payload.strix_llm == 'contextual-orchestrator/orchestrator/free'
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 }}
ORCHESTRATOR_REQUIRE_ZDR: ${{ steps.target_visibility.outputs.is_private }}
run: |
set -euo pipefail
bash "$TRUSTED_STRIX_SOURCE/scripts/ci/contextual_orchestrator_review_sidecar.sh"
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Comment thread
seonghobae marked this conversation as resolved.

- name: Resolve live NVIDIA NIM Strix models
id: resolve_nvidia_models
env:
Expand All @@ -557,30 +573,29 @@ jobs:
NVIDIA_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }}
run: |
set -euo pipefail
if [ -n "$STRIX_MODEL_REQUESTED" ] || [ "$TARGET_REPOSITORY_PRIVATE" != "false" ] || [ -z "${NVIDIA_API_KEY:-}" ]; then
case "$STRIX_MODEL_REQUESTED" in
nvidia_nim/*) ;;
*)
echo 'Skipping NVIDIA model resolution for non-NVIDIA Strix request.'
printf 'primary=\nfallback=\n' >> "$GITHUB_OUTPUT"
exit 0
;;
esac
if [ "$TARGET_REPOSITORY_PRIVATE" != "false" ] || [ -z "${NVIDIA_API_KEY:-}" ]; then
printf 'primary=\nfallback=\n' >> "$GITHUB_OUTPUT"
exit 0
fi
resolver="$TRUSTED_STRIX_SOURCE/scripts/ci/select_nvidia_nim_model.py"
primary_rc=0
primary="$(python3 "$resolver" --role strix-primary --candidates "$STRIX_NVIDIA_ALLOWED_MODELS")" || primary_rc=$?
if [ "$primary_rc" -eq 75 ]; then
echo '::warning::NVIDIA NIM model catalog is unavailable; using the contracted OpenAI fallback.'
printf 'primary=\nfallback=\n' >> "$GITHUB_OUTPUT"
exit 0
fi
[ "$primary_rc" -eq 0 ] || exit "$primary_rc"

fallback_rc=0
fallback="$(python3 "$resolver" --role strix-fallback --candidates "$STRIX_NVIDIA_ALLOWED_MODELS" --exclude "$primary")" || fallback_rc=$?
fallback="$(python3 "$resolver" --role strix-fallback --candidates "$STRIX_NVIDIA_ALLOWED_MODELS" --exclude "${STRIX_MODEL_REQUESTED#nvidia_nim/}")" || fallback_rc=$?
if [ "$fallback_rc" -eq 75 ]; then
echo '::warning::NVIDIA NIM fallback resolution is unavailable; retaining the resolved primary and contracted OpenAI fallback.'
echo '::warning::NVIDIA NIM fallback resolution is unavailable; retaining only the contracted OpenAI fallback.'
fallback=""
else
[ "$fallback_rc" -eq 0 ] || exit "$fallback_rc"
fi
{
printf 'primary=nvidia_nim/%s\n' "$primary"
printf 'primary=\n'
if [ -n "$fallback" ]; then
printf 'fallback=nvidia_nim/%s\n' "$fallback"
else
Expand All @@ -591,18 +606,39 @@ jobs:
- name: Gate Strix secrets
id: gate
env:
STRIX_MODEL: ${{ github.event.client_payload.strix_llm || (steps.target_visibility.outputs.is_private == 'false' && steps.resolve_nvidia_models.outputs.primary || 'gpt-5.4') }}
STRIX_MODEL: ${{ github.event.client_payload.strix_llm || 'contextual-orchestrator/orchestrator/free' }}
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Outdated
STRIX_MODEL_REQUESTED: ${{ github.event.client_payload.strix_llm || '' }}
STRIX_OPENAI_API_KEY: ${{ secrets.STRIX_OPENAI_API_KEY || secrets.OPENAI_API_KEY }}
STRIX_OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
STRIX_NVIDIA_NIM_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }}
STRIX_VERTEX_CREDENTIALS: ${{ secrets.GCP_SA_KEY }}
STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }}
CONTEXTUAL_ORCHESTRATOR_BASE_URL: ${{ env.CONTEXTUAL_ORCHESTRATOR_BASE_URL }}
CONTEXTUAL_ORCHESTRATOR_TOKEN: ${{ env.CONTEXTUAL_ORCHESTRATOR_TOKEN }}
TARGET_REPOSITORY_PRIVATE: ${{ steps.target_visibility.outputs.is_private }}
run: |
strix_model="$(printf '%s' "$STRIX_MODEL" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
echo "strix_model=$strix_model" >> "$GITHUB_OUTPUT"
case "$strix_model" in
contextual-orchestrator/orchestrator/free)
echo 'enabled=true' >> "$GITHUB_OUTPUT"
echo 'provider_mode=contextual_orchestrator' >> "$GITHUB_OUTPUT"
if ! [[ "$CONTEXTUAL_ORCHESTRATOR_BASE_URL" =~ ^http://127\.0\.0\.1:[0-9]{1,5}$ ]]; then
echo '::error::The contextual-orchestrator Strix sidecar must use an exact IPv4 loopback URL and explicit port.'
exit 1
fi
sidecar_port="${CONTEXTUAL_ORCHESTRATOR_BASE_URL##*:}"
if [ "$sidecar_port" -lt 1 ] || [ "$sidecar_port" -gt 65535 ]; then
echo '::error::The contextual-orchestrator Strix sidecar port must be between 1 and 65535.'
exit 1
fi
sanitized_orchestrator_token="$(printf '%s' "$CONTEXTUAL_ORCHESTRATOR_TOKEN" | tr -d '\r\n')"
trimmed_orchestrator_token="$(printf '%s' "$sanitized_orchestrator_token" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
if [ -z "$trimmed_orchestrator_token" ] || [ "$trimmed_orchestrator_token" != "$CONTEXTUAL_ORCHESTRATOR_TOKEN" ]; then
echo '::error::The contextual-orchestrator Strix sidecar requires one non-empty, line-safe bearer token.'
exit 1
fi
;;
openai/gpt-5-mini* | openai/gpt-5-nano* | \
openai/openai/gpt-5-mini* | openai/openai/gpt-5-nano* | \
github_models/openai/gpt-5-mini* | github_models/openai/gpt-5-nano*)
Expand Down Expand Up @@ -677,7 +713,7 @@ jobs:
fi
;;
*)
echo '::error::STRIX_LLM must select NVIDIA NIM Nemotron, GitHub Models openai/gpt-5 or newer, direct OpenAI GPT-5.4 or newer, OpenRouter openrouter/free, or an approved organization Vertex AI model.'
echo '::error::STRIX_LLM must select contextual-orchestrator/orchestrator/free, NVIDIA NIM Nemotron, GitHub Models openai/gpt-5 or newer, direct OpenAI GPT-5.4 or newer, OpenRouter openrouter/free, or an approved organization Vertex AI model.'
exit 1
;;
esac
Expand Down Expand Up @@ -752,7 +788,7 @@ jobs:
- name: Mask LLM API key
if: steps.gate.outputs.enabled == 'true'
env:
LLM_API_KEY: ${{ steps.gate.outputs.provider_mode == 'github_models' && (secrets.STRIX_GITHUB_MODELS_TOKEN || github.token) || steps.gate.outputs.provider_mode == 'openai_direct' && (secrets.STRIX_OPENAI_API_KEY || secrets.OPENAI_API_KEY) || steps.gate.outputs.provider_mode == 'openrouter' && secrets.OPENROUTER_API_KEY || steps.gate.outputs.provider_mode == 'nvidia_nim' && secrets.NVIDIA_NIM_API_KEY || '' }}
LLM_API_KEY: ${{ steps.gate.outputs.provider_mode == 'contextual_orchestrator' && env.CONTEXTUAL_ORCHESTRATOR_TOKEN || steps.gate.outputs.provider_mode == 'github_models' && (secrets.STRIX_GITHUB_MODELS_TOKEN || github.token) || steps.gate.outputs.provider_mode == 'openai_direct' && (secrets.STRIX_OPENAI_API_KEY || secrets.OPENAI_API_KEY) || steps.gate.outputs.provider_mode == 'openrouter' && secrets.OPENROUTER_API_KEY || steps.gate.outputs.provider_mode == 'nvidia_nim' && secrets.NVIDIA_NIM_API_KEY || '' }}
run: |
# Sanitize CR/LF before masking to prevent broken ::add-mask::
# commands and potential workflow command injection.
Expand All @@ -768,11 +804,15 @@ jobs:
- name: Prepare LLM API key input file
if: steps.gate.outputs.enabled == 'true'
env:
LLM_API_KEY_SECRET: ${{ steps.gate.outputs.provider_mode == 'github_models' && (secrets.STRIX_GITHUB_MODELS_TOKEN || github.token) || steps.gate.outputs.provider_mode == 'openai_direct' && (secrets.STRIX_OPENAI_API_KEY || secrets.OPENAI_API_KEY) || steps.gate.outputs.provider_mode == 'openrouter' && secrets.OPENROUTER_API_KEY || steps.gate.outputs.provider_mode == 'nvidia_nim' && secrets.NVIDIA_NIM_API_KEY || '' }}
LLM_API_KEY_SECRET: ${{ steps.gate.outputs.provider_mode == 'contextual_orchestrator' && env.CONTEXTUAL_ORCHESTRATOR_TOKEN || steps.gate.outputs.provider_mode == 'github_models' && (secrets.STRIX_GITHUB_MODELS_TOKEN || github.token) || steps.gate.outputs.provider_mode == 'openai_direct' && (secrets.STRIX_OPENAI_API_KEY || secrets.OPENAI_API_KEY) || steps.gate.outputs.provider_mode == 'openrouter' && secrets.OPENROUTER_API_KEY || steps.gate.outputs.provider_mode == 'nvidia_nim' && secrets.NVIDIA_NIM_API_KEY || '' }}
PROVIDER_MODE: ${{ steps.gate.outputs.provider_mode }}
run: |
sanitized="$(printf '%s' "$LLM_API_KEY_SECRET" | tr -d '\r\n')"
trimmed="$(printf '%s' "$sanitized" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
if [ -z "$trimmed" ] && [ "$PROVIDER_MODE" = "contextual_orchestrator" ]; then
echo '::error::CONTEXTUAL_ORCHESTRATOR_TOKEN is required for gateway-backed Strix scans.'
exit 1
fi
if [ -z "$trimmed" ] && [ "$PROVIDER_MODE" = "github_models" ]; then
echo '::error::STRIX_GITHUB_MODELS_TOKEN is required for GitHub Models Strix scans.'
exit 1
Expand All @@ -794,6 +834,21 @@ jobs:
printf '%s' "$trimmed" > "$llm_api_key_file"
echo "LLM_API_KEY_FILE=$llm_api_key_file" >> "$GITHUB_ENV"

- name: Prepare contextual-orchestrator API base
if: steps.gate.outputs.provider_mode == 'contextual_orchestrator'
env:
CONTEXTUAL_ORCHESTRATOR_BASE_URL: ${{ env.CONTEXTUAL_ORCHESTRATOR_BASE_URL }}
run: |
set -euo pipefail
if ! [[ "$CONTEXTUAL_ORCHESTRATOR_BASE_URL" =~ ^http://127\.0\.0\.1:[0-9]{1,5}$ ]]; then
echo '::error::The contextual-orchestrator API base must remain on exact IPv4 loopback.'
exit 1
fi
umask 077
llm_api_base_file="$RUNNER_TEMP/llm_api_base.txt"
printf '%s' "${CONTEXTUAL_ORCHESTRATOR_BASE_URL}/v1" > "$llm_api_base_file"
echo "LLM_API_BASE_FILE=$llm_api_base_file" >> "$GITHUB_ENV"

- name: Prepare OpenRouter API base
if: steps.gate.outputs.provider_mode == 'openrouter'
run: |
Expand Down Expand Up @@ -929,6 +984,9 @@ jobs:
strix_llm_file="$RUNNER_TEMP/strix_llm.txt"
strix_model="$(printf '%s' "$STRIX_MODEL" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
case "$strix_model" in
contextual-orchestrator/orchestrator/free)
printf '%s' 'openai/orchestrator/free' > "$strix_llm_file"
;;
openai/gpt-5-mini* | openai/gpt-5-nano* | \
openai/openai/gpt-5-mini* | openai/openai/gpt-5-nano* | \
github_models/openai/gpt-5-mini* | github_models/openai/gpt-5-nano*)
Expand Down Expand Up @@ -959,7 +1017,7 @@ jobs:
printf '%s' "$strix_model" > "$strix_llm_file"
;;
*)
echo '::error::STRIX_LLM must select NVIDIA NIM Nemotron, GitHub Models openai/gpt-5 or newer, direct OpenAI GPT-5.4 or newer, OpenRouter openrouter/free, or an approved organization Vertex AI model.'
echo '::error::STRIX_LLM must select contextual-orchestrator/orchestrator/free, NVIDIA NIM Nemotron, GitHub Models openai/gpt-5 or newer, direct OpenAI GPT-5.4 or newer, OpenRouter openrouter/free, or an approved organization Vertex AI model.'
exit 1
;;
esac
Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ this file. The format follows Keep a Changelog, and versioned releases follow
Semantic Versioning where the repository publishes a release.

## [Unreleased]
- Required Strix security evidence now uses the existing vendored
`contextual-orchestrator` sidecar and its fail-closed `orchestrator/free`
ZDR-first zero-cost pool for normal scans. Direct NVIDIA NIM, OpenRouter,
GitHub Models, OpenAI, and Vertex paths remain available only when an
authorized `repository_dispatch` explicitly supplies `strix_llm` for
diagnosis. Gateway startup, loopback binding, bearer-token masking, and an
isolated `--target`, `--require-hashes`, binary-only dependency tree fail closed; Strix receives only the
loopback OpenAI-compatible token/base while provider credentials stay inside
the sidecar process. The gateway route owns provider/model failover, so the
scanner does not append a second direct-provider fallback chain. Explicit
gateway dispatches now provision the sidecar, unrelated diagnostic models do
not invoke NVIDIA discovery, and private-repository source is admitted only
to exact ZDR-attested routes. NVIDIA diagnostic fallback resolution excludes
the caller's actual requested model instead of resolving an unused surrogate
primary.
- Central review now routes through the vendored `contextual-orchestrator`
gateway sidecar: the write-capable PR autofix and the shared `opencode.jsonc`
default use the fail-closed zero-cost pool `orchestrator/free`, with
Expand Down
67 changes: 67 additions & 0 deletions docs/adr/0004-strix-contextual-orchestrator-authority.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# ADR-0004: contextual-orchestrator owns normal Strix provider routing

- Status: Proposed
- Date: 2026-08-28
- Owners: ContextualWisdomLab central CI maintainers
- Figma File ID: N/A (workflow/control-plane change; no customer UI)

## Context

Required Strix scans were serialized per repository, but each scan still owned a
hard-coded direct provider chain. A live DiskSage exact-head scan exhausted four
independent paths in one run: NVIDIA rate limiting, an unavailable NVIDIA model,
an OpenRouter upstream error, and exhausted direct OpenAI credit. No authoritative
vulnerability report existed, so the required check correctly failed closed, but
consumer product PRs could not repair the shared authority boundary.

The central repository already vendors a pinned contextual-orchestrator sidecar.
It registers the five organization provider credentials in a process-local KV,
performs live discovery, applies the reviewed zero-cost/ZDR policy, and exposes
`orchestrator/free` through an authenticated OpenAI-compatible loopback API.

## Decision

Normal Strix scans SHALL provision that sidecar and call
`openai/orchestrator/free` through exact IPv4 loopback. The sidecar owns
provider/model discovery and fallback. Strix SHALL NOT add a second direct
fallback chain for the gateway-backed route.

Private-repository source SHALL be routed only through exact ZDR-attested free
routes. If the live endpoint evidence is absent or no eligible route remains,
the sidecar fails closed before any source is sent. Public-repository scans may
use the documented non-ZDR free fallback tier after ZDR routes are exhausted.

A caller MAY use `repository_dispatch.strix_llm` to select an existing direct
provider model for bounded diagnosis. That override is explicit, auditable, and
does not change the normal default.

The sidecar dependency tree SHALL be installed from the exact vendored commit's
hash lock, with binary-only distributions, into an isolated `--target` directory
so it cannot rewrite the hash-locked Strix runtime. Its generated
bearer token SHALL be line-safe, masked before export, and passed to Strix only
through a mode-specific file. Missing credentials, unhealthy startup, non-loopback
base URLs, invalid ports, and missing tokens fail closed.

## Consequences

- Shared provider outages are handled by one routing authority instead of nested
retry/fallback loops.
- Provider credentials remain inside the gateway process; the scanner sees only
a short-lived loopback credential.
- A gateway outage remains non-passing security evidence.
- Existing direct-provider diagnostic contracts and their tests remain supported.
- After merge, consumer PRs require a fresh exact-head Strix run; predecessor
outage evidence is not transferred.

## Verification

- RED/GREEN static contract for the workflow, model namespace, loopback and token.
- Bounded required-workflow smoke contract.
- Bash syntax and YAML parse.
- Existing full organization Checks, independent review, and protected merge.

## Rollback

Revert this ADR and its workflow commit. Do not partially restore a direct default
while leaving gateway key/base files active. Re-run the complete required Strix
contract and affected consumer exact heads after rollback.
54 changes: 54 additions & 0 deletions docs/doctoring/strix-contextual-orchestrator-gateway.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Strix contextual-orchestrator gateway doctoring

## Failure evidence

The triggering consumer scan produced no vulnerability artifact. Its terminal
log recorded provider infrastructure failures across NVIDIA NIM, OpenRouter, and
direct OpenAI, followed by the existing fail-closed
`STRIX_PROVIDER_UNAVAILABLE` classification. Repository Test, Release, SAST, and
Security workflows were independently successful on the same consumer head.

## Causal boundary

The defect is not in the consumer product tree. It is the duplicated routing
authority in central Strix: the scanner selected and retried direct providers even
though the organization already had a pinned contextual-orchestrator gateway with
model discovery, ZDR policy, and provider-family diversity.

## Corrective control

```text
five provider credentials
→ process-local contextual-orchestrator KV
→ live discovery + ZDR-first zero-cost catalog
→ authenticated 127.0.0.1 OpenAI-compatible API
→ Strix openai/orchestrator/free
→ authoritative report or fail-closed required check
```

Direct providers are retained only for an explicit diagnostic override. The
normal gateway route has no scanner-owned fallback list.

## Security and operability

- The sidecar is pinned by commit SHA.
- Provider credentials never become Strix key files in gateway mode.
- The bearer token is generated per job, rejects line breaks, and is masked.
- The base URL must be exact IPv4 loopback with a valid port.
- Sidecar packages use the exact vendored commit's `requirements.lock` with
`--require-hashes`, binary-only distributions, and an isolated target directory
rather than the scanner's hash-locked environment.
- Private repositories admit only exact ZDR-attested gateway routes and fail
closed before source transmission when that evidence is absent.
- Health failure, empty discovery, missing credentials, and provider exhaustion
remain non-passing.
- Consumer PRs are rechecked on unchanged exact heads after the central fix.

## Traceability

- ADR: `docs/adr/0004-strix-contextual-orchestrator-authority.md`
- Workflow: `.github/workflows/strix.yml`
- Sidecar: `scripts/ci/contextual_orchestrator_review_sidecar.sh`
- Required smoke: `scripts/ci/strix_required_workflow_smoke.sh`
- Contract: `tests/test_strix_contextual_orchestrator_contract.py`
- Predecessor gateway ADR: `docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md`
Loading
Loading