fix(evo-controller): read CHIT_PASSPHRASE via services.common.env helpers - #2124
POWERFULMOVES wants to merge 6 commits into
Conversation
|
Warning Review limit reached
Next review available in: 56 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (45)
📝 WalkthroughWalkthroughThe PR establishes a canonical PMOVES MCP inventory, generates client-specific configurations, bootstraps multiple agent stacks, adds KiloCode operational tooling and documentation, updates node configurations, and changes CHIT secret handling and TensorZero KiloCode routing. ChangesMCP and KiloCode platform
Runtime signing and model routing
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4c473b4490
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| val = env.get(var) or os.environ.get(var) | ||
| if val: | ||
| result.append(val) |
There was a problem hiding this comment.
Stop expanding secrets into tracked MCP configs
When mcp-config-bootstrap/opencode-bootstrap runs in a shell that has loaded the PMOVES secrets, this fallback reads values such as CIPHER_API_TOKEN and SUPABASE_SERVICE_KEY from os.environ and writes them into tracked outputs like .kimi/mcp.json, kilo.json, and pmoves/configs/claws/*.json instead of preserving ${...} placeholders. I reproduced this with CIPHER_API_TOKEN=SECRET123 SUPABASE_SERVICE_KEY=SUPAKEY python3 -m pmoves.tools.mcp_config_generator --client kimi --output /tmp/mcp.json, which wrote Bearer SECRET123 and SUPAKEY into JSON. For tracked config generation, only explicit --set values should be expanded, or secret placeholders should remain literal.
Useful? React with 👍 / 👎.
| # ── Crush ──────────────────────────────────────────────────────────────────── | ||
|
|
||
| info "Updating Crush config: ${CRUSH_CONFIG}" | ||
| python3 -m pmoves.tools.mcp_config_generator --client crush --output "${CRUSH_CONFIG}" || warn "Crush config update failed" |
There was a problem hiding this comment.
Run the Crush generator with the repo on PYTHONPATH
When this target is invoked through make -C pmoves hermes-crush-bootstrap, the current directory is pmoves/, so python3 -m pmoves.tools.mcp_config_generator cannot resolve the top-level pmoves package; I checked from that directory and it fails with ModuleNotFoundError: No module named 'pmoves.tools'. Because the failure is converted to a warning and the script still exits 0, the Crush config is silently not updated in the advertised bootstrap flow; set PYTHONPATH="$REPO_ROOT" or run the module from the repo root before invoking it.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 15
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.kimi/mcp.json:
- Around line 31-43: Update the --apiKey value in the pmoves-supabase
configuration at .kimi/mcp.json lines 31-43 and kilo.json lines 146-158 to use
${SUPABASE_SERVICE_ROLE_KEY:-${SUPABASE_SERVICE_KEY}}, matching .claude/mcp.json
so either secret name is supported.
In `@kilo.json`:
- Around line 160-169: Change the supabase-db permission configuration from
allow to ask so database operations require explicit approval before execution.
Update the permission.supabase-db_* setting associated with the supabase-db MCP
server, while leaving its command and environment configuration unchanged.
In `@pmoves/config/profiles/hermes/kvm4-1.yaml`:
- Around line 104-109: Align the Hermes gateway configuration with
render_hermes(), which currently emits only enabled, type, url, and optional
headers. Remove the unsupported auth_token and tools fields from
docker_mcp_gateway in pmoves/config/profiles/hermes/kvm4-1.yaml (104-109),
pmoves/config/profiles/hermes/spark.yaml (140-145), and
pmoves/config/profiles/hermes/z890.yaml (121-126), unless the renderer/bootstrap
path is explicitly updated to preserve them.
In `@pmoves/configs/claws/opencode-4090.json`:
- Around line 32-55: Apply least-privilege Supabase configuration to the
pmoves-supabase and supabase-db entries in
pmoves/configs/claws/opencode-4090.json lines 32-55,
pmoves/configs/claws/opencode-5090.json lines 72-95,
pmoves/configs/claws/opencode-kvm2.json lines 32-55, and
pmoves/configs/claws/opencode-kvm4-1.json lines 32-55: replace broad shared
credentials with scoped Supabase credentials and change postgres-mcp from
--access-mode=unrestricted to the appropriate restricted access mode, preserving
each entry’s existing connectivity settings.
In `@pmoves/configs/claws/opencode-nemoclaw.json`:
- Around line 32-44: Remove the Supabase service key from the CLI args for the
pmoves-supabase MCP configuration and use the project’s secret-injection
mechanism instead, while preserving the existing endpoint and schema settings.
Apply the same change in pmoves/configs/claws/opencode-nemoclaw.json#L32-L44,
pmoves/configs/claws/scopes/kvm4-2.json#L73-L85,
pmoves/configs/claws/scopes/nemotron-claw.json#L88-L100,
pmoves/configs/claws/scopes/spark.json#L73-L85, and
pmoves/configs/claws/scopes/z890.json#L114-L126, then regenerate the mirrored
claw configurations.
In `@pmoves/configs/claws/scopes/kvm4-2.json`:
- Around line 109-119: Remove the “tailscale” MCP configuration from the
restricted scope, or replace it with a read-only credential and tool set that
cannot perform network administration. Ensure this tier retains only status-only
Tailscale access and no longer wires the administrative TAILSCALE_API_KEY
through tailscale-mcp.
In `@pmoves/scripts/bootstrap-hermes-crush.sh`:
- Around line 34-35: Update the bootstrap error paths around the Crush config
generation and the referenced missing-PyYAML and client-configuration branches
to return a nonzero status instead of success. Ensure failures from the python3
mcp_config_generator command and unavailable required dependencies propagate out
of the script, preventing the “bootstrap complete” success path from running.
In `@pmoves/tests/test_mcp_config_generator.py`:
- Around line 325-332: Update the test setup around scopes.main([]) to use
pytest’s monkeypatch fixture for both scopes.SCOPES_DIR and
scopes.INVENTORY_PATH, replacing the manual try/finally restoration. Apply the
same change to the corresponding test blocks at the other affected locations so
both module globals are automatically restored after each test.
In `@pmoves/tools/bootstrap_openclaw_scopes.py`:
- Around line 138-150: Update the scope validation logic around SCOPE_CONFIG,
TIER_KEYS, and PMOVES_MCP_KEYS to compare each expected PMOVES MCP entry’s
complete canonical definition, including URLs, headers, commands, and arguments,
rather than only comparing key presence. Render or derive the expected tier
configuration, compare it against data["mcp_servers"], and report mismatched
definitions alongside missing and unexpected entries while preserving the
existing tier-specific validation.
In `@pmoves/tools/crush_configurator.py`:
- Around line 196-318: Replace the duplicate MCP definitions in
pmoves/tools/crush_configurator.py (lines 196-318) with rendering through
mcp_config_generator; in pmoves/tools/bootstrap_openclaw_scopes.py (lines 23-35)
and pmoves/tools/bootstrap_opencode.py (lines 18-30), derive owned PMOVES keys
from the canonical inventory; in pmoves/scripts/bootstrap-hermes-crush.sh (line
76), remove inventory-owned entries before merging the newly rendered Hermes
block.
In `@pmoves/tools/kilo_parity_mcp_check.py`:
- Around line 15-31: The parity check currently compares only MCP keys; update
its main validation flow to generate the canonical KiloCode fragment from the
inventory and structurally compare the owned mcp definitions and permission
entries, including URLs, headers, commands, and permissions. Detect and report
stale values and unexpected obsolete entries, while preserving the existing
client filtering and nonzero failure behavior.
In `@pmoves/tools/mcp_config_generator.py`:
- Around line 452-458: Update the client-output selection logic in the loop over
clients to reject the combination of args.client == "all" and a single
args.output path before generating files. Require args.output_dir for all-client
generation, or otherwise assign a distinct output filename per client; preserve
the existing single-client output behavior.
- Around line 366-375: Update the hermes branch of the MCP configuration
generation flow to avoid writing the rendered fragment over the real Hermes
config.yaml. Use the existing merge behavior to parse and preserve unrelated
YAML settings while updating only the top-level mcp_servers key, or require and
write to a distinct fragment output path; do not ignore merge in this branch.
- Around line 107-114: Update the variable expansion logic around _expand so
process-environment values are not used to resolve placeholders by default,
preventing secrets from being written to generated configuration files. Preserve
unresolved references such as CIPHER_API_TOKEN and Supabase service keys unless
the caller explicitly enables secret materialization, while retaining explicit
env mappings and recursive default expansion.
- Around line 227-229: Update render_kilocode() so it does not automatically
assign wildcard permissions to every rendered MCP server. Require explicit
per-server opt-in or use narrower permissions for sensitive servers such as
tailscale and supabase-db, while preserving rendering of the MCP entries and
existing permissions for non-sensitive servers.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 8953f37e-c6bf-4b8b-90ca-2f37b3c5b6f0
📒 Files selected for processing (51)
.claude/mcp.json.gitignore.kilocode/skills/kilocode-agent-trails/SKILL.md.kilocode/skills/kilocode-bringup-audit/SKILL.md.kimi/AGENTS.md.kimi/mcp.jsonkilo.jsonpmoves/Makefilepmoves/config/agent_registry.yamlpmoves/config/mcp_inventory.jsonpmoves/config/profiles/hermes/4090.yamlpmoves/config/profiles/hermes/5090.yamlpmoves/config/profiles/hermes/b850.yamlpmoves/config/profiles/hermes/elder-melchor.yamlpmoves/config/profiles/hermes/kvm4-1.yamlpmoves/config/profiles/hermes/spark.yamlpmoves/config/profiles/hermes/z890.yamlpmoves/configs/claws/opencode-4090.jsonpmoves/configs/claws/opencode-5090.jsonpmoves/configs/claws/opencode-kvm2.jsonpmoves/configs/claws/opencode-kvm4-1.jsonpmoves/configs/claws/opencode-kvm4-2.jsonpmoves/configs/claws/opencode-nemoclaw.jsonpmoves/configs/claws/opencode-nemotron-claw.jsonpmoves/configs/claws/opencode-spark.jsonpmoves/configs/claws/scopes/4090.jsonpmoves/configs/claws/scopes/5090.jsonpmoves/configs/claws/scopes/kvm2.jsonpmoves/configs/claws/scopes/kvm4-1.jsonpmoves/configs/claws/scopes/kvm4-2.jsonpmoves/configs/claws/scopes/nemoclaw.jsonpmoves/configs/claws/scopes/nemotron-claw.jsonpmoves/configs/claws/scopes/spark.jsonpmoves/configs/claws/scopes/z890.jsonpmoves/docs/AGENTS/KILOCODE_PERSONA_STYLE_PLAYBOOK.mdpmoves/docs/operations/MCP_TOOLKIT.mdpmoves/mk/kilo.mkpmoves/mk/mcp-toolkit.mkpmoves/scripts/bootstrap-hermes-crush.shpmoves/scripts/bootstrap-node.shpmoves/scripts/mcp-toolkit-bootstrap.shpmoves/services/evo-controller/app.pypmoves/services/evo-controller/tests/test_chit_signing.pypmoves/tensorzero/config/tensorzero.tomlpmoves/tests/test_mcp_config_generator.pypmoves/tools/bootstrap_openclaw_scopes.pypmoves/tools/bootstrap_opencode.pypmoves/tools/crush_configurator.pypmoves/tools/kilo_parity_mcp_check.pypmoves/tools/mcp_config_generator.pyskills/README.md
| "pmoves-supabase": { | ||
| "description": "Supabase PostgREST API", | ||
| "command": "npx", | ||
| "args": [ | ||
| "-y", | ||
| "@supabase/mcp-server-postgrest@0.1.1", | ||
| "--apiUrl", | ||
| "http://localhost:8000/rest/v1", | ||
| "--apiKey", | ||
| "${SUPABASE_SERVICE_KEY}", | ||
| "--schema", | ||
| "public" | ||
| ] | ||
| }, | ||
| "supabase-db": { | ||
| "description": "Supabase Postgres database", | ||
| "command": "uvx", | ||
| "args": [ | ||
| "postgres-mcp@0.3.0", | ||
| "--access-mode=unrestricted" | ||
| ], | ||
| "env": { | ||
| "DATABASE_URI": "${SUPABASE_DB_URI}" | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' "== file list =="
git ls-files 'pmoves/configs/claws/opencode-4090.json' \
'pmoves/configs/claws/opencode-5090.json' \
'pmoves/configs/claws/opencode-kvm2.json' \
'pmoves/configs/claws/opencode-kvm4-1.json'
printf '\n%s\n' "== targeted excerpts =="
for f in \
pmoves/configs/claws/opencode-4090.json \
pmoves/configs/claws/opencode-5090.json \
pmoves/configs/claws/opencode-kvm2.json \
pmoves/configs/claws/opencode-kvm4-1.json
do
echo "--- $f ---"
nl -ba "$f" | sed -n '1,140p'
done
printf '\n%s\n' "== search for related Supabase / DB config patterns =="
rg -n '"pmoves-supabase"|"supabase-db"|"SUPABASE_SERVICE_KEY"|"SUPABASE_DB_URI"|"access-mode=unrestricted"|mcp-server-postgrest|postgres-mcp' pmoves/configs -SRepository: POWERFULMOVES/PMOVES.AI
Length of output: 453
🏁 Script executed:
#!/bin/bash
set -euo pipefail
for f in \
pmoves/configs/claws/opencode-4090.json \
pmoves/configs/claws/opencode-5090.json \
pmoves/configs/claws/opencode-kvm2.json \
pmoves/configs/claws/opencode-kvm4-1.json
do
echo "--- $f ---"
awk 'NR>=1 && NR<=140 { printf "%4d %s\n", NR, $0 }' "$f"
echo
done
echo '== related patterns in pmoves/configs =='
python3 - <<'PY'
from pathlib import Path
import re
patterns = [
r'"pmoves-supabase"',
r'"supabase-db"',
r'SUPABASE_SERVICE_KEY',
r'SUPABASE_DB_URI',
r'access-mode=unrestricted',
r'mcp-server-postgrest',
r'postgres-mcp',
]
for path in sorted(Path('pmoves/configs').rglob('*')):
if not path.is_file():
continue
try:
text = path.read_text()
except Exception:
continue
hits = [p for p in patterns if re.search(p, text)]
if hits:
print(path)
for p in hits:
print(' ', p)
PYRepository: POWERFULMOVES/PMOVES.AI
Length of output: 17859
Use scoped Supabase credentials and restrict DB access
The shared pmoves-supabase/supabase-db entries pass ${SUPABASE_SERVICE_KEY} on the command line and start postgres-mcp with --access-mode=unrestricted, giving these nodes broad Supabase authority.
Apply the least-privilege change to the matching claw and scope configs as well.
📍 Affects 4 files
pmoves/configs/claws/opencode-4090.json#L32-L55(this comment)pmoves/configs/claws/opencode-5090.json#L72-L95pmoves/configs/claws/opencode-kvm2.json#L32-L55pmoves/configs/claws/opencode-kvm4-1.json#L32-L55
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pmoves/configs/claws/opencode-4090.json` around lines 32 - 55, Apply
least-privilege Supabase configuration to the pmoves-supabase and supabase-db
entries in pmoves/configs/claws/opencode-4090.json lines 32-55,
pmoves/configs/claws/opencode-5090.json lines 72-95,
pmoves/configs/claws/opencode-kvm2.json lines 32-55, and
pmoves/configs/claws/opencode-kvm4-1.json lines 32-55: replace broad shared
credentials with scoped Supabase credentials and change postgres-mcp from
--access-mode=unrestricted to the appropriate restricted access mode, preserving
each entry’s existing connectivity settings.
| "pmoves-supabase": { | ||
| "description": "Supabase PostgREST API", | ||
| "command": "npx", | ||
| "args": [ | ||
| "-y", | ||
| "@supabase/mcp-server-postgrest@0.1.1", | ||
| "--apiUrl", | ||
| "http://localhost:8000/rest/v1", | ||
| "--apiKey", | ||
| "${SUPABASE_SERVICE_KEY}", | ||
| "--schema", | ||
| "public" | ||
| ] |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## file list\n'
git ls-files 'pmoves/configs/claws/**/*.json' 'pmoves/configs/claws/*.json' | sed -n '1,200p'
printf '\n## search for SUPABASE_SERVICE_KEY and mcp-server-postgrest\n'
rg -n 'SUPABASE_SERVICE_KEY|mcp-server-postgrest|apiKey|apiUrl|schema' pmoves/configs -S
printf '\n## inspect one representative config and any shared templates/docs\n'
for f in \
pmoves/configs/claws/opencode-nemoclaw.json \
pmoves/configs/claws/scopes/kvm4-2.json \
pmoves/configs/claws/scopes/nemotron-claw.json \
pmoves/configs/claws/scopes/spark.json \
pmoves/configs/claws/scopes/z890.json
do
echo "\n### $f"
sed -n '1,180p' "$f"
done
printf '\n## search repo for Supabase auth guidance\n'
rg -n 'Supabase|service key|service_key|environment.*secret|process listings|command.*args|PostgREST' -S .Repository: POWERFULMOVES/PMOVES.AI
Length of output: 50382
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,180p' pmoves/configs/claws/opencode-nemoclaw.jsonRepository: POWERFULMOVES/PMOVES.AI
Length of output: 2941
🌐 Web query:
@supabase/mcp-server-postgrest apiKey environment variable command line args secret path
💡 Result:
To configure the @supabase/mcp-server-postgrest, you typically pass the apiKey as a command-line argument when setting up the MCP server in your configuration file (such as the Claude Desktop configuration file) [1][2][3]. The standard way to provide this configuration is within the args array of your mcpServers entry [1][3]. The following structure is used: { "mcpServers": { "your-server-name": { "command": "npx", "args": [ "-y", "@supabase/mcp-server-postgrest@latest", "--apiUrl", "https://your-project-ref.supabase.co/rest/v1", "--apiKey", "your-anon-key", "--schema", "public" ] } } } Key details regarding the configuration: - The --apiKey argument is optional and is used for authentication [1][4]. - The --apiUrl argument is required and specifies the base URL of your PostgREST endpoint [1][4]. - The --schema argument is required and defines the Postgres schema to serve (e.g., public) [1][4]. - If you are using the server programmatically, the createPostgrestMcpServer function accepts these parameters as options in an object (apiUrl, apiKey, and schema) [1][4]. There is no native or explicit mention in the documentation of using a specific secret path or environment variable mapping within the command-line args for this package; the standard practice is to pass the value directly via the --apiKey flag as shown above [1][2]. If you need to manage sensitive keys, it is common practice in MCP environments to handle them via your local shell's environment variables if supported by your MCP client, or by using a secure configuration management approach outside of the MCP server's command-line definition [1][3].
Citations:
- 1: https://registry.npmjs.org/@supabase/mcp-server-postgrest
- 2: https://glama.ai/mcp/servers/supabase-community/supabase-mcp
- 3: https://mcpcursor.com/server/supabase
- 4: https://deepwiki.com/supabase-community/supabase-mcp/4.3-mcp-server-postgrest
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '## env-style secret handling in pmoves configs\n'
rg -n '"env"\s*:|SUPABASE_SERVICE_KEY|SERVICE_KEY|SECRET' pmoves/configs -S
printf '\n## any other MCP server configs that keep secrets out of args\n'
rg -n 'mcp-server|command|args|env' pmoves/configs -g '*.json' -S | sed -n '1,220p'Repository: POWERFULMOVES/PMOVES.AI
Length of output: 23645
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## files containing the Supabase service key in MCP args"
rg -l '"--apiKey",\s*"\$\{SUPABASE_SERVICE_KEY\}"' pmoves/configs/claws -g '*.json' | sort
echo
echo "## exact occurrences count"
rg -n '"--apiKey",\s*"\$\{SUPABASE_SERVICE_KEY\}"' pmoves/configs/claws -g '*.json' | wc -l
echo
echo "## any generator/template references"
rg -n 'opencode-nemoclaw|nemotron-claw|kvm4-2|spark|z890|SUPABASE_SERVICE_KEY' pmoves -g '*.json' -g '*.md' -g '*.yml' -g '*.yaml' | sed -n '1,200p'Repository: POWERFULMOVES/PMOVES.AI
Length of output: 218
Keep the Supabase service key out of args.
--apiKey ${SUPABASE_SERVICE_KEY} expands into the spawned argv, exposing the credential via process listings and telemetry. Move this MCP launch to a secret-injection path that keeps the key out of CLI args, then regenerate the mirrored claw configs.
pmoves/configs/claws/opencode-nemoclaw.json#L32-L44pmoves/configs/claws/scopes/kvm4-2.json#L73-L85pmoves/configs/claws/scopes/nemotron-claw.json#L88-L100pmoves/configs/claws/scopes/spark.json#L73-L85pmoves/configs/claws/scopes/z890.json#L114-L126
📍 Affects 5 files
pmoves/configs/claws/opencode-nemoclaw.json#L32-L44(this comment)pmoves/configs/claws/scopes/kvm4-2.json#L73-L85pmoves/configs/claws/scopes/nemotron-claw.json#L88-L100pmoves/configs/claws/scopes/spark.json#L73-L85pmoves/configs/claws/scopes/z890.json#L114-L126
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pmoves/configs/claws/opencode-nemoclaw.json` around lines 32 - 44, Remove the
Supabase service key from the CLI args for the pmoves-supabase MCP configuration
and use the project’s secret-injection mechanism instead, while preserving the
existing endpoint and schema settings. Apply the same change in
pmoves/configs/claws/opencode-nemoclaw.json#L32-L44,
pmoves/configs/claws/scopes/kvm4-2.json#L73-L85,
pmoves/configs/claws/scopes/nemotron-claw.json#L88-L100,
pmoves/configs/claws/scopes/spark.json#L73-L85, and
pmoves/configs/claws/scopes/z890.json#L114-L126, then regenerate the mirrored
claw configurations.
| inventory = json.loads(inventory_path.read_text(encoding="utf-8")) | ||
| kilo = json.loads(kilo_path.read_text(encoding="utf-8")) | ||
| kilo_keys = set(kilo.get("mcp", {}).keys()) | ||
|
|
||
| expected: set[str] = set() | ||
| for group in inventory.get("groups", {}).values(): | ||
| for srv in group.get("servers", []): | ||
| clients = srv.get("clients") | ||
| if clients is None or "kilocode" in clients: | ||
| expected.add(srv["key"]) | ||
|
|
||
| missing = sorted(expected - kilo_keys) | ||
| for key in missing: | ||
| print(f" ❌ kilo.json missing MCP: {key}", file=sys.stderr) | ||
| if not missing: | ||
| print(" ✅ kilo.json contains all canonical MCP servers", file=sys.stderr) | ||
| return len(missing) |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Compare rendered MCP definitions, not only missing keys.
The check passes stale URLs, headers, commands, permissions, and unexpected obsolete MCP entries. Generate the canonical KiloCode fragment and compare the owned mcp and permission entries structurally.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pmoves/tools/kilo_parity_mcp_check.py` around lines 15 - 31, The parity check
currently compares only MCP keys; update its main validation flow to generate
the canonical KiloCode fragment from the inventory and structurally compare the
owned mcp definitions and permission entries, including URLs, headers, commands,
and permissions. Detect and report stale values and unexpected obsolete entries,
while preserving the existing client filtering and nonzero failure behavior.
| mcp[spec.key] = entry | ||
| permissions[f"{spec.key}_*"] = "allow" | ||
| return {"mcp": mcp, "permission": permissions} |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## File outline\n'
ast-grep outline pmoves/tools/mcp_config_generator.py --view expanded || true
printf '\n## Relevant slices\n'
sed -n '1,320p' pmoves/tools/mcp_config_generator.py
printf '\n## Search for generated permission usage and privileged specs\n'
rg -n "permission|permissions|mcp_config_generator|spec.key|Tailscale|Supabase|MCP" pmoves -SRepository: POWERFULMOVES/PMOVES.AI
Length of output: 50381
🏁 Script executed:
#!/bin/bash
set -euo pipefail
nl -ba pmoves/tools/mcp_config_generator.py | sed -n '1,320p'Repository: POWERFULMOVES/PMOVES.AI
Length of output: 201
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "tailscale|supabase|mcp" pmoves -S --glob '!**/.git/**' | sed -n '1,200p'Repository: POWERFULMOVES/PMOVES.AI
Length of output: 19023
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '## Outline\n'
ast-grep outline pmoves/tools/mcp_config_generator.py --view expanded || true
printf '\n## File excerpt with line numbers\n'
sed -n '1,280p' pmoves/tools/mcp_config_generator.py | cat -n
printf '\n## Permission and spec references\n'
rg -n "permissions|permission|MCP_SPECS|tailscale|supabase-db|required_env|required_commands|required_envs" pmoves/tools/mcp_config_generator.py -n -SRepository: POWERFULMOVES/PMOVES.AI
Length of output: 14918
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '## Inventory outline\n'
ast-grep outline pmoves/config/mcp_inventory.json --view expanded || true
printf '\n## Inventory entries mentioning tailscale/supabase/privileged servers\n'
python3 - <<'PY'
import json
from pathlib import Path
path = Path("pmoves/config/mcp_inventory.json")
data = json.loads(path.read_text())
for group_name, group in data.get("groups", {}).items():
for server in group.get("servers", []):
key = server.get("key", "")
text = json.dumps(server, sort_keys=True)
if any(name in text.lower() for name in ("tailscale", "supabase", "hostinger", "docker", "nats")):
print(f"[{group_name}] {key}")
print(json.dumps(server, indent=2, sort_keys=True))
print()
PY
printf '\n## Client restrictions in inventory\n'
python3 - <<'PY'
import json
from pathlib import Path
data = json.loads(Path("pmoves/config/mcp_inventory.json").read_text())
clients = {}
for group in data.get("groups", {}).values():
for server in group.get("servers", []):
if "clients" in server:
clients[server["key"]] = server["clients"]
for k, v in sorted(clients.items()):
print(k, "->", v)
PYRepository: POWERFULMOVES/PMOVES.AI
Length of output: 2565
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
import json
from pathlib import Path
data = json.loads(Path("pmoves/config/mcp_inventory.json").read_text())
for group_name, group in data.get("groups", {}).items():
for server in group.get("servers", []):
key = server.get("key", "")
blob = json.dumps(server, sort_keys=True).lower()
if any(name in blob for name in ("tailscale", "supabase", "hostinger", "docker", "nats")):
print(f"[{group_name}] {key}")
print(json.dumps(server, indent=2, sort_keys=True))
print()
PY
python3 - <<'PY'
import json
from pathlib import Path
data = json.loads(Path("pmoves/config/mcp_inventory.json").read_text())
for group in data.get("groups", {}).values():
for server in group.get("servers", []):
if "clients" in server:
print(server["key"], "->", server["clients"])
PYRepository: POWERFULMOVES/PMOVES.AI
Length of output: 2389
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '320,390p' pmoves/tools/mcp_config_generator.py | cat -n
printf '\n## Renderer registration and client generation references\n'
rg -n "render_kilocode|generate_for_client|_collect_servers|clients" pmoves/tools/mcp_config_generator.py -n -SRepository: POWERFULMOVES/PMOVES.AI
Length of output: 4130
Avoid wildcard-allowing every rendered MCP server. render_kilocode() assigns *_ permissions to every included server, so privileged MCPs like tailscale and supabase-db end up allowed by default. Require explicit per-server opt-in or narrower permissions for sensitive tools.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pmoves/tools/mcp_config_generator.py` around lines 227 - 229, Update
render_kilocode() so it does not automatically assign wildcard permissions to
every rendered MCP server. Require explicit per-server opt-in or use narrower
permissions for sensitive servers such as tailscale and supabase-db, while
preserving rendering of the MCP entries and existing permissions for
non-sensitive servers.
| if client == "hermes": | ||
| # Hermes is YAML; return the rendered snippet and let the bootstrap | ||
| # script merge it surgically. We do not overwrite the whole YAML file. | ||
| output_path.parent.mkdir(parents=True, exist_ok=True) | ||
| output_path.write_text( | ||
| f"# PMOVES MCP servers (auto-generated by mcp_config_generator.py)\n" | ||
| f"# Merge this into your Hermes config.yaml under the top-level `mcp_servers:` key.\n" | ||
| + json.dumps(rendered, indent=2), | ||
| encoding="utf-8", | ||
| ) |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🔴 Critical | 🏗️ Heavy lift
Do not overwrite the complete Hermes profile with an MCP fragment.
--client hermes targets the real config.yaml, but this branch ignores merge and replaces every unrelated Hermes setting. Merge mcp_servers through a YAML parser or require a separate fragment output path.
🧰 Tools
🪛 ast-grep (0.44.1)
[info] 372-372: use jsonify instead of json.dumps for JSON output
Context: json.dumps(rendered, indent=2)
Note: [CWE-116] Improper Encoding or Escaping of Output.
(use-jsonify)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pmoves/tools/mcp_config_generator.py` around lines 366 - 375, Update the
hermes branch of the MCP configuration generation flow to avoid writing the
rendered fragment over the real Hermes config.yaml. Use the existing merge
behavior to parse and preserve unrelated YAML settings while updating only the
top-level mcp_servers key, or require and write to a distinct fragment output
path; do not ignore merge in this branch.
| for client in clients: | ||
| output = args.output | ||
| if output is None: | ||
| if args.client == "all" and args.output_dir: | ||
| output = args.output_dir / f"mcp.{client}.json" | ||
| else: | ||
| output = DEFAULT_OUTPUTS[client] |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Reject --client all with a single --output path.
All clients currently write to the same file sequentially, leaving only the final client’s incompatible configuration. Require --output-dir for all, or derive distinct filenames.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pmoves/tools/mcp_config_generator.py` around lines 452 - 458, Update the
client-output selection logic in the loop over clients to reject the combination
of args.client == "all" and a single args.output path before generating files.
Require args.output_dir for all-client generation, or otherwise assign a
distinct output filename per client; preserve the existing single-client output
behavior.
…tstrap - Add pmoves/config/mcp_inventory.json as single source of truth - Add pmoves/tools/mcp_config_generator.py to render client-native configs - Wire MCP bootstrap into pmoves/scripts/bootstrap-node.sh and Makefile - Add hermes-crush bootstrap script and make targets - Update Claude, Kimi, KiloCode, Hermes, Crush, and OpenCode configs - Add pytest suite for the generator - Add kilo parity check tooling - Docs updated in MCP_TOOLKIT.md
- Extend mcp_inventory.json with endpoint_prefix so fleet/local URLs resolve cleanly - Fix mcp_config_generator.py endpoint lookup to honor caller endpoint preference - Add pmoves/tools/bootstrap_openclaw_scopes.py with full/edge tier policy - Update all 8 pmoves/configs/claws/scopes/*.json configs - full nodes (4090, 5090, z890, kvm4-1, kvm4-2, nemotron-claw): full PMOVES MCP set - edge nodes (nemoclaw, kvm2): cipher + agent-zero + tailscale - preserve scope-specific MCPs (gpu-mesh, docker, zai-*, etc.) - Add openclaw-scope-bootstrap and openclaw-scope-check make targets - Extend test_mcp_config_generator.py with endpoint + scope tests (17 passing)
KVMs act as Tailscale exit nodes and connect to GPU-bearing nodes, so every KVM gets the full PMOVES MCP set per operator clarification. - Move kvm2 from edge to full tier in bootstrap_openclaw_scopes.py - Regenerate pmoves/configs/claws/scopes/kvm2.json with full PMOVES MCP set
…ensorZero TOML duplicates
…pers Replaces direct os.getenv calls with get_secret so Docker *_FILE secrets are supported. Adds test for CHIT_PASSPHRASE_FILE.
1. mcp_config_generator.py: remove os.environ fallback in _expand() (CR P1) — secrets from shell env were leaking into tracked config files 2. bootstrap-hermes-crush.sh: add export PYTHONPATH (CR P2) — python3 -m pmoves.tools.* failed without repo root on path 3. .kimi/mcp.json + kilo.json: SUPABASE_SERVICE_KEY -> canonical SUPABASE_SERVICE_ROLE_KEY with fallback (Codex P2) 4. kilo.json: supabase-db access-mode unrestricted -> schema + permission allow -> ask (CR Major — auto-approving all DDL/DML) 5. mcp_config_generator.py: add extra field to ServerSpec + pass-through in render_hermes so auth_token/tools fields survive regeneration (CR Major) 6. All 16 opencode + scope configs: access-mode=unrestricted -> schema across fleet (CR Major — restricted DB access everywhere)
🔏 CHIT-aware change — control-body routingThis PR touches a CHIT-aware service (ports 8086/8087/8103/8106/8113/9224 surface). Before a Control-Body ACK:
Advisory routing only — the blocking contract check is CHIT Contract. |
909fcdd to
4a7c842
Compare
… fallback 1. DEFAULT_OUTPUTS: add opencode entry (was missing — KeyError on --client all) 2. bootstrap-hermes-crush.sh: add export PYTHONPATH for python3 -m pmoves.tools.* 3. .kimi/mcp.json: SUPABASE_SERVICE_KEY -> canonical with fallback (same fixes as PR #2124, applied to this branch)
|
Closing in favor of #2126 — this PR is a strict subset (43 of 46 files are identical, plus #2126 adds 6 more). The evo-controller CHIT_PASSPHRASE fix is present in both branches identically. All 12 review threads apply equally to #2126 and will be addressed there. The test_chit_signing.py file will be cherry-picked if missing. |
Pull request was closed
… fallback 1. DEFAULT_OUTPUTS: add opencode entry (was missing — KeyError on --client all) 2. bootstrap-hermes-crush.sh: add export PYTHONPATH for python3 -m pmoves.tools.* 3. .kimi/mcp.json: SUPABASE_SERVICE_KEY -> canonical with fallback (same fixes as PR #2124, applied to this branch)
…node matrix (#2126) * feat(mcp): canonical MCP inventory + generator + full agent-stack bootstrap - Add pmoves/config/mcp_inventory.json as single source of truth - Add pmoves/tools/mcp_config_generator.py to render client-native configs - Wire MCP bootstrap into pmoves/scripts/bootstrap-node.sh and Makefile - Add hermes-crush bootstrap script and make targets - Update Claude, Kimi, KiloCode, Hermes, Crush, and OpenCode configs - Add pytest suite for the generator - Add kilo parity check tooling - Docs updated in MCP_TOOLKIT.md * feat(mcp): bootstrap canonical PMOVES MCPs into OpenClaw scopes - Extend mcp_inventory.json with endpoint_prefix so fleet/local URLs resolve cleanly - Fix mcp_config_generator.py endpoint lookup to honor caller endpoint preference - Add pmoves/tools/bootstrap_openclaw_scopes.py with full/edge tier policy - Update all 8 pmoves/configs/claws/scopes/*.json configs - full nodes (4090, 5090, z890, kvm4-1, kvm4-2, nemotron-claw): full PMOVES MCP set - edge nodes (nemoclaw, kvm2): cipher + agent-zero + tailscale - preserve scope-specific MCPs (gpu-mesh, docker, zai-*, etc.) - Add openclaw-scope-bootstrap and openclaw-scope-check make targets - Extend test_mcp_config_generator.py with endpoint + scope tests (17 passing) * fix(mcp): promote kvm2 OpenClaw scope to full tier KVMs act as Tailscale exit nodes and connect to GPU-bearing nodes, so every KVM gets the full PMOVES MCP set per operator clarification. - Move kvm2 from edge to full tier in bootstrap_openclaw_scopes.py - Regenerate pmoves/configs/claws/scopes/kvm2.json with full PMOVES MCP set * feat(spark): add SPARK node to OpenClaw/OpenCode MCP matrix and fix TensorZero TOML duplicates * fix(mcp): add opencode to DEFAULT_OUTPUTS + PYTHONPATH + Supabase key fallback 1. DEFAULT_OUTPUTS: add opencode entry (was missing — KeyError on --client all) 2. bootstrap-hermes-crush.sh: add export PYTHONPATH for python3 -m pmoves.tools.* 3. .kimi/mcp.json: SUPABASE_SERVICE_KEY -> canonical with fallback (same fixes as PR #2124, applied to this branch) * fix(mcp): preserve secrets in tracked configs + fix Hermes overwrite Addresses Codex P1, Codex P2, CodeRabbit Critical + Major findings: P1 (security): mcp_config_generator._expand() now accepts allow_os_environ flag. Tracked configs (repo-relative claude/kimi/ kilocode/opencode) generate with allow_os_environ=False, preserving ${VAR} placeholders instead of expanding real secret values from os.environ. Only --set values and local configs (crush/hermes in ~/.config or ~/.hermes) expand from os.environ. Critical (hermes): --client hermes now writes to a .mcp_snippet.json sidecar instead of overwriting the real config.yaml. The bootstrap script merges the snippet into config.yaml surgically via PyYAML. Major (--client all): --client all with --output is now rejected (exit 2) with a message requiring --output-dir. P2 (PYTHONPATH): bootstrap-hermes-crush.sh already exports PYTHONPATH at line 33 (was added in a prior commit). Major (bootstrap fail): script now exits 1 on Crush generator failure or missing PyYAML, instead of silently succeeding. 💘 Generated with Crush Assisted-by: Crush:glm-5.2 --------- Co-authored-by: Agent Zero <agent.zero@pmoves.ai> Co-authored-by: Shaela Bello <slbello@uncg.edu>
Summary
Replaces direct
os.getenv("CHIT_PASSPHRASE")calls inpmoves/services/evo-controller/app.pywithservices.common.env.get_secret()so Docker*_FILEsecrets are supported consistently with the rest of the stack.Changes
_chit_signing_key()now usesget_secret("CHIT_SIGNING_KEY")/get_secret("CHIT_PASSPHRASE", "").test_payload_signed_when_passphrase_file_presentto verifyCHIT_PASSPHRASE_FILEsupport.Verification
python3 -m pytest pmoves/services/evo-controller/tests/ -v # 13 passedRelated
This addresses the evo-controller secrets-audit blocker. A follow-up issue will track the remaining services still using raw
os.environ.get/os.getenvforCHIT_PASSPHRASE.Summary by CodeRabbit
New Features
Bug Fixes
Documentation