Skip to content

Conversation

@AlexMikhalev
Copy link
Contributor

@AlexMikhalev AlexMikhalev commented Dec 25, 2025

Summary

This PR adds comprehensive DevOps/CI-CD role configuration with complete ontology for the Terraphim AI project, integrating with the new GitHub runner system.

Changes

New Files

  • terraphim_server/default/devops_cicd_config.json: Complete role configuration

    • DevOps Engineer role with CI/CD specialization
    • GitHub Runner Specialist role with Firecracker VM expertise
    • Ontology with knowledge domains, haystacks, and performance metrics
  • .docs/github-runner-ci-integration.md: Integration documentation

    • Role configuration details
    • Workflow execution architecture
    • Usage examples and performance characteristics

Roles Created

DevOps Engineer

  • Specialization: CI/CD pipelines and infrastructure automation
  • Knowledge domains: GitHub Actions, Firecracker VMs, multi-platform builds
  • Haystacks: 6 data sources (workflows, scripts, GitHub runner code, docs)
  • Primary tools: GitHub Actions, Docker Buildx, Cargo, npm, pip

GitHub Runner Specialist

  • Specialization: Firecracker VM orchestration
  • Core modules: VmCommandExecutor, CommandKnowledgeGraph, LearningCoordinator
  • Infrastructure: Firecracker API, fcctl-web, JWT auth, SSH keys
  • Performance metrics: VM creation 5-10s, command execution 100-150ms

Integration Points

  • ✅ 35 GitHub Actions workflows available
  • ✅ Self-hosted runners with Firecracker VM support
  • ✅ End-to-end execution proven (49 tests passing)
  • ✅ Knowledge graph learning operational

Testing

The PR triggers all relevant GitHub Actions workflows via webhook:

  • CI Native (GitHub Actions + Docker Buildx)
  • CI PR Validation
  • Test workflows
  • Security validation

Related Documentation

  • HANDOVER.md: Complete project handover
  • .docs/summary-terraphim_github_runner.md: GitHub runner crate reference
  • blog-posts/github-runner-architecture.md: Architecture blog post

Summary by CodeRabbit

  • Documentation

    • Added comprehensive GitHub Runner docs: architecture, setup, usage examples, end-to-end proof, performance and troubleshooting.
    • Added server README, setup guide, blog post, and handover materials describing VM-based workflow execution and LLM integration.
  • Bug Fixes

    • Fixed Firecracker rootfs permission issue enabling VM startup.
    • Fixed SSH key selection to support VM-type-aware command execution.
  • New Features

    • Introduced a Firecracker VM-based workflow runner and simulated VM executor.
    • Added DevOps Engineer and GitHub Runner Specialist role configurations.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 25, 2025

📝 Walkthrough

Walkthrough

Adds a GitHub Actions–style runner: docs, server crate, VM executor (Firecracker + simulated), workflow discovery/execution, webhook handling, LLM/learning wiring, config/role files, end-to-end tests, and infra fixes (rootfs, SSH key selection). Introduces new public executors and server endpoints.

Changes

Cohort / File(s) Summary
Docs: architecture, setup & summaries
.docs/github-runner-ci-integration.md, .docs/summary-terraphim_github_runner.md, .docs/summary.md, docs/github-runner-architecture.md, docs/github-runner-setup.md, blog-posts/github-runner-architecture.md
New and expanded documentation describing architecture, data flows, VM lifecycles, LLM integration, configuration, performance, and usage examples.
Docs: handover, proofs & infra fixes
HANDOVER.md, crates/terraphim_github_runner/END_TO_END_PROOF.md, crates/terraphim_github_runner/FIRECRACKER_FIX.md, crates/terraphim_github_runner/SSH_KEY_FIX.md, crates/terraphim_github_runner/TEST_USER_INIT.md
New/rewritten handover and operational docs: E2E proof, rootfs capability fix, SSH key-path/VM-type fix, and test-user DB init notes.
Config: role and defaults
terraphim_server/default/devops_cicd_config.json
New DevOps/GitHub Runner role configuration (KG, haystacks, Ollama LLM settings, role metadata, extras).
CI workflow
.github/workflows/test-ci.yml
New lightweight GitHub Actions workflow (checkout / test / build steps — currently print-based).
Crate: terraphim_github_runner API surface
crates/terraphim_github_runner/Cargo.toml, crates/terraphim_github_runner/src/lib.rs, crates/terraphim_github_runner/src/workflow/mod.rs
Added reqwest dev-dep, re-exported new VM executors and added vm_executor module wiring.
Crate: VM executor & simulation
crates/terraphim_github_runner/src/workflow/vm_executor.rs
New Firecracker-backed VmCommandExecutor (HTTP fcctl-web API client) and SimulatedVmExecutor with snapshot/rollback, error handling and unit tests.
Crate: tests (E2E)
crates/terraphim_github_runner/tests/end_to_end_test.rs
New ignore-marked end-to-end test that provisions/uses a real Firecracker VM and exercises workflow execution and learning.
New server crate (manifest & readme)
crates/terraphim_github_runner_server/Cargo.toml, crates/terraphim_github_runner_server/README.md
New server crate manifest and README describing a salvo-based webhook server and integration points.
Server: config & webhook verification
crates/terraphim_github_runner_server/src/config/mod.rs, crates/terraphim_github_runner_server/src/webhook/mod.rs, crates/terraphim_github_runner_server/src/webhook/signature.rs
New Settings loader from env and HMAC-SHA256 signature verification (with tests) exposed via a signature module.
Server: GitHub helper, workflow discovery & execution
crates/terraphim_github_runner_server/src/github/mod.rs, crates/terraphim_github_runner_server/src/workflow/discovery.rs, crates/terraphim_github_runner_server/src/workflow/execution.rs, crates/terraphim_github_runner_server/src/workflow/mod.rs
Added PR-comment posting helper, workflow discovery utility (YAML trigger matching), and VM-based workflow parsing/execution orchestration (LLM optional).
Server: main webhook server
crates/terraphim_github_runner_server/src/main.rs
New Salvo web server wiring: webhook endpoint, signature check, event parsing, optional LLM parser creation, background workflow execution in VMs, and PR comments on result.
API change: SSH VM-type support (fcctl-web)
fcctl-web/src/api/llm.rs
Updated SSH execution signature to accept vm_type, VM lookup now returns (vm_id, vm_type), call sites updated to pass vm_type for key selection.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant GitHub
    participant Server
    participant WorkflowDiscovery
    participant LLM
    participant VMProvider as FirecrackerAPI
    participant VmExecutor
    participant KG as KnowledgeGraph
    participant GitHubAPI

    Note over GitHub,Server: Webhook delivery
    GitHub->>Server: POST /webhook + X-Hub-Signature-256
    Server->>Server: verify_signature(secret, body)
    alt signature valid
      Server->>WorkflowDiscovery: discover_workflows_for_event(repo/.github/workflows, event)
      WorkflowDiscovery-->>Server: list of workflow paths
      opt LLM enabled
        Server->>LLM: parse_workflow_yaml_with_llm(workflow)
        LLM-->>Server: ParsedWorkflow
      end
      loop for each workflow
        Server->>VMProvider: request VM (create/find)
        VMProvider-->>Server: vm_id, vm_type, vm_ip
        Server->>VmExecutor: execute(command, session(vm_id), vm_type)
        VmExecutor->>VMProvider: POST /execute (fcctl-web)
        VMProvider-->>VmExecutor: {exit_code, stdout, stderr}
        VmExecutor-->>KG: report outcome (learning coordinator)
      end
      Server->>GitHubAPI: post_pr_comment(result) (for PRs)
      GitHubAPI-->>Server: OK
    else signature invalid
      Server-->>GitHub: 401
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 In microVM meadows the workflows play,
Firecracker sparks light each CI day,
Docs and configs hop, learning patterns along,
SSH keys sorted, commands sing their song,
A rabbit applauds — the runner is on its way!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a DevOps/CI-CD role configuration and GitHub runner integration, which is reflected across the documentation and configuration files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/github-runner-ci-integration

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude
Copy link

claude bot commented Dec 25, 2025

Code Review: DevOps/CI-CD Role Configuration and GitHub Runner Integration

✅ Overall Assessment

This PR successfully adds comprehensive DevOps/CI-CD role configuration with complete ontology integration for the GitHub runner system. The implementation is well-documented, follows project conventions, and demonstrates operational proof with 49 passing unit tests.


🎯 Strengths

1. Excellent Documentation

  • Comprehensive integration guide: .docs/github-runner-ci-integration.md provides clear overview, architecture diagrams, usage examples, and performance characteristics
  • Technical reference: .docs/summary-terraphim_github_runner.md documents all 6 core modules with LOC counts, test coverage, and API details
  • Updated HANDOVER.md: Clear handover document with executive summary, architecture overview, and infrastructure fixes
  • Blog post: blog-posts/github-runner-architecture.md provides external-facing architecture documentation

2. Well-Structured Configuration

The devops_cicd_config.json follows established patterns from other role configs:

  • ✅ Proper JSON structure with id, roles, default_role, selected_role
  • ✅ Three distinct roles: Default, DevOps Engineer, GitHub Runner Specialist
  • ✅ Knowledge graph integration with local markdown sources
  • ✅ Multiple haystacks for comprehensive search coverage (6 for DevOps, 5 for Runner Specialist)
  • ✅ LLM integration with Ollama and specialized system prompts

3. Security Best Practices

  • ✅ No hardcoded secrets (all atomic_server_secret fields are null)
  • ✅ JWT authentication mentioned in documentation
  • ✅ SSH key management properly documented in SSH_KEY_FIX.md
  • ✅ Read-only haystacks prevent accidental modifications

4. Proven Implementation

  • ✅ 49 unit tests passing + 1 integration test
  • ✅ End-to-end execution verified with real Firecracker VMs
  • ✅ Performance metrics documented (VM creation 5-10s, command execution 100-150ms)
  • ✅ Learning coordinator and knowledge graph operational

⚠️ Issues and Recommendations

Critical Issues

1. Hardcoded Absolute Path (Security/Portability) 🔴

Location: terraphim_server/default/devops_cicd_config.json:177

{
  "location": "/home/alex/projects/terraphim/firecracker-rust/fcctl-web",
  "service": "Ripgrep",
  ...
}

Problem:

  • Hardcoded user-specific absolute path will break for other users/environments
  • Creates deployment issues and CI/CD failures
  • Security concern: exposes system structure

Recommendation:

{
  "location": "../firecracker-rust/fcctl-web",
  "service": "Ripgrep",
  ...
}

Or better yet, remove this haystack if fcctl-web is external to the terraphim-ai repository. If it's critical, document the environment variable requirement in the README.

2. Configuration Field Inconsistency 🟡

Issue: Mixing of extra vs top-level LLM fields

In devops_cicd_config.json, LLM configuration is at the top level:

"llm_provider": "ollama",
"ollama_base_url": "http://127.0.0.1:11434",
"ollama_model": "llama3.2:3b",

But in ollama_llama_config.json, it's in extra:

"extra": {
  "llm_provider": "ollama",
  "llm_model": "llama3.2:3b",
  "llm_base_url": "http://127.0.0.1:11434"
}

Recommendation: Verify which pattern is correct per the role configuration schema and ensure consistency. Check terraphim_config crate for the canonical schema.

Performance Considerations

3. Multiple Large Haystacks 🟡

Both roles scan multiple directories:

  • DevOps Engineer: 6 haystacks (workflows, scripts, runner code, docs, .docs, blog-posts)
  • GitHub Runner Specialist: 5 haystacks (including external fcctl-web repo)

Concern:

  • Large haystack counts may slow search performance
  • Overlapping coverage between .docs, docs, and blog-posts

Recommendation:

  • Profile search performance with realistic queries
  • Consider consolidating overlapping documentation sources
  • Use file_patterns filters more extensively (already done well for workflows)

Code Quality

4. Missing Validation 🟡

Observation: No validation that referenced paths exist before configuration load

Recommendation:
Add validation in the server startup:

// In terraphim_server startup
for haystack in role.haystacks {
    if haystack.service == "Ripgrep" && !Path::new(&haystack.location).exists() {
        warn!("Haystack path does not exist: {}", haystack.location);
    }
}

5. Terraphim-Graph Without Thesaurus 🟡

Both roles use relevance_function: "terraphim-graph" but automata_path: null:

"relevance_function": "terraphim-graph",
"kg": {
  "automata_path": null,
  "knowledge_graph_local": {
    "input_type": "markdown",
    "path": ".docs"
  }
}

Question: How does terraphim-graph scoring work without automata? Will it fall back to another scorer?

Recommendation: Document the expected behavior or provide a thesaurus file for optimal graph-based scoring.

Documentation

6. Incomplete Testing Commands

The documentation provides excellent testing commands, but could add:

  • Pre-requisites check script
  • Environment variable validation
  • Quick start script for new contributors

Recommendation: Create a scripts/setup-devops-role.sh that:

  1. Validates required environment variables
  2. Checks Firecracker API availability
  3. Loads the DevOps config
  4. Runs smoke tests

🔍 Best Practices Compliance

✅ Follows Project Guidelines

  • ✅ Conventional naming: snake_case for file references
  • ✅ Rust 2024 edition compatibility
  • ✅ Proper async/await patterns in referenced code
  • ✅ No mocks in tests (per CLAUDE.md requirement)
  • ✅ Comprehensive test coverage (49 unit + 1 integration)

✅ CLAUDE.md Alignment

  • ✅ Role-based configuration pattern
  • ✅ Knowledge graph integration
  • ✅ Haystack type usage (Ripgrep)
  • ✅ LLM provider configuration (Ollama)
  • ✅ System prompt specialization

🧪 Test Coverage Analysis

Excellent coverage in terraphim_github_runner:

  • ✅ VM Executor: HTTP client tests
  • ✅ Knowledge Graph: 8/8 tests passing
  • ✅ Learning Coordinator: Success/failure tracking
  • ✅ Workflow Executor: Orchestration tests
  • ✅ End-to-end: Real Firecracker VM integration

Missing:

  • ⚠️ Config validation tests for the new devops_cicd_config.json
  • ⚠️ Role loading/switching tests
  • ⚠️ Haystack initialization tests with the new paths

Recommendation: Add integration test:

#[tokio::test]
async fn test_load_devops_cicd_config() {
    let config = Config::from_file("terraphim_server/default/devops_cicd_config.json").unwrap();
    assert_eq!(config.default_role, "DevOps Engineer");
    assert!(config.roles.contains_key("GitHub Runner Specialist"));
    // Validate haystacks load correctly
}

🔒 Security Assessment

✅ Strengths

  • No hardcoded credentials
  • Read-only haystacks
  • JWT authentication documented
  • SSH key management addressed in fixes

⚠️ Concerns

  1. Absolute path exposure (line 177) - reveals system structure
  2. Local Ollama endpoint - ensure 127.0.0.1 binding is intentional (not exposed externally)
  3. External repository reference - fcctl-web path should be validated or removed

Recommendation:

  • Add security section to README documenting:
    • Required firewall rules
    • Ollama security best practices
    • Firecracker VM isolation guarantees

📊 Performance Metrics

Documented (Excellent):

  • VM creation: 5-10 seconds
  • Command execution: 100-150ms
  • Learning overhead: <10ms

Consider documenting:

  • Config load time with 6 haystacks
  • Search latency across multiple haystacks
  • Knowledge graph build time from markdown files

🎬 Summary Recommendations

Must Fix Before Merge 🔴

  1. Remove or parameterize the hardcoded absolute path at line 177

Should Fix 🟡

  1. Clarify and standardize LLM config field placement (top-level vs extra)
  2. Add config validation tests
  3. Document automata_path behavior when null with terraphim-graph scorer
  4. Profile and document search performance with 6 haystacks

Nice to Have 💡

  1. Create setup script for DevOps role
  2. Add security documentation section
  3. Consolidate overlapping documentation haystacks
  4. Add haystack path validation on server startup

✅ Approval Status

Recommendation: Request changes for the hardcoded path issue (#1), then approve.

The overall implementation is excellent with strong documentation, proper testing, and good architectural design. The configuration follows project patterns well. Once the critical path issue is resolved and consistency issues addressed, this will be a solid addition to the project.

Great work on the comprehensive GitHub runner integration! The end-to-end proof and detailed documentation demonstrate production-ready implementation. 🎉


Reviewed by: Claude Code
Review Date: 2025-12-25
PR: #381

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (12)
.docs/summary-terraphim_github_runner.md (3)

79-90: Consider adding a language specifier to the fenced code block.

The architecture diagram code block lacks a language specifier. While this is stylistic, adding an identifier (even empty or text) improves markdown linting compliance.

🔎 Suggested fix
-```
+```text
 GitHub Webhook → WorkflowContext → ParsedWorkflow → SessionManager

173-173: Bare URL in documentation.

Consider wrapping the URL in angle brackets or converting to a markdown link for better accessibility and lint compliance.

🔎 Suggested fix
-- **fcctl-web**: HTTP API for Firecracker (default: http://127.0.0.1:8080)
+- **fcctl-web**: HTTP API for Firecracker (default: `http://127.0.0.1:8080`)

252-256: Consider hyphenating compound adjectives.

"Short Term" and "Long Term" when used as adjectives modifying nouns should be hyphenated: "Short-Term" and "Long-Term".

🔎 Suggested fix
-### Short Term
+### Short-Term
 1. Dynamic SSH key generation per VM
 2. Retry logic with exponential backoff
 3. Parallel command execution across multiple VMs
 4. VM snapshot/restore for faster startup

-### Long Term
+### Long-Term
.docs/github-runner-ci-integration.md (3)

57-77: Add language specifier to fenced code block.

The test results block lacks a language specifier. Consider using text or leaving empty with proper specifier for markdown lint compliance.

🔎 Suggested fix
-```
+```text
 ✅ Knowledge graph and learning coordinator initialized

86-95: Add language specifier to architecture diagram code block.

🔎 Suggested fix
-```
+```text
 GitHub Webhook → terraphim_github_runner → Firecracker API

212-212: Wrap bare URL in code formatting.

🔎 Suggested fix
-- `FIRECRACKER_API_URL`: API base URL (default: http://127.0.0.1:8080)
+- `FIRECRACKER_API_URL`: API base URL (default: `http://127.0.0.1:8080`)
crates/terraphim_github_runner/FIRECRACKER_FIX.md (1)

24-28: Document the security implications of broad capabilities.

The capabilities added (CAP_SYS_ADMIN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, etc.) are quite broad. While necessary for Firecracker to function, consider adding a brief note about why these are safe in your deployment context or what compensating controls exist.

blog-posts/github-runner-architecture.md (1)

25-36: Add language specifiers to fenced code blocks.

Several code blocks lack language specifiers which affects markdown lint compliance:

  • Line 25: Architecture diagram
  • Line 114: Statistics example
  • Line 156: State machine diagram
  • Line 172: Example transformation

Consider using text for diagrams and plain text output.

🔎 Suggested fixes
 ### High-Level Data Flow

-```
+```text
 GitHub Webhook → WorkflowContext → ParsedWorkflow → SessionManager
 **Example statistics**:
-```
+```text
 Total successes: 3
 **State machine**:
-```
+```text
 Created → Executing → Completed/Failed
 **Example transformation**:
-```
+```text
 Input: "Run cargo test and if it passes, build the project"

Also applies to: 114-120, 156-160, 172-180

crates/terraphim_github_runner/SSH_KEY_FIX.md (1)

7-11: Add language specifiers to all fenced code blocks for proper syntax highlighting.

Fenced code blocks throughout the file lack language identifiers. Add bash, rust, or json specifiers after the opening triple backticks to enable correct syntax highlighting and markdown compliance.

🔎 Example fixes

Lines 7-11 (error output):

-```
+```bash
 Warning: Identity file ./images/test-vms/focal/keypair/fctest not accessible: No such file or directory.

Lines 16-18 (Rust code):

-```rust
+```rust
 let ssh_key = "./images/test-vms/focal/keypair/fctest";

Lines 105-117 (JSON output):

-```json
+```json
 {

Also applies to: 16-18, 21-23, 32-40, 44-51, 69-77, 81-100, 105-117, 123-132, 138-147, 154-182

HANDOVER.md (3)

40-40: Specify language for architecture diagram code block.

Line 40 contains a fenced code block with ASCII art but lacks a language specifier. Use ```ascii or ```text to maintain markdown lint compliance.

🔎 Proposed fix
-```
+```ascii
 GitHub Webhook → WorkflowContext → ParsedWorkflow → SessionManager

204-204: Fix typo: "Authenticatio" → "Authentication".

Line 204 in the performance metrics section is missing the final "n".

🔎 Proposed fix
-### SSH Authenticatio
+### SSH Authentication

232-232: Use heading markup instead of bold emphasis for subsection titles.

The troubleshooting section (lines 232–241) uses bold text (**text**) for subsection titles. Convert these to proper level-3 headings (### text) to comply with markdown standards (MD036) and improve document structure.

🔎 Proposed fixes
-**"Permission denied" accessing VM**
+### "Permission denied" accessing VM
-**"SSH connection refused"**
+### "SSH connection refused"
-**"User not found in database"**
+### "User not found in database"
-**"Wrong SSH key for VM type"**
+### "Wrong SSH key for VM type"

Also applies to: 235-235, 238-238, 241-241

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54d589f and d36a79f.

📒 Files selected for processing (10)
  • .docs/github-runner-ci-integration.md
  • .docs/summary-terraphim_github_runner.md
  • .docs/summary.md
  • HANDOVER.md
  • blog-posts/github-runner-architecture.md
  • crates/terraphim_github_runner/END_TO_END_PROOF.md
  • crates/terraphim_github_runner/FIRECRACKER_FIX.md
  • crates/terraphim_github_runner/SSH_KEY_FIX.md
  • crates/terraphim_github_runner/TEST_USER_INIT.md
  • terraphim_server/default/devops_cicd_config.json
🧰 Additional context used
🪛 LanguageTool
.docs/summary-terraphim_github_runner.md

[uncategorized] ~1-~1: The official name of this software platform is spelled with a capital “H”.
Context: # terraphim_github_runner - Summary Last Updated: 202...

(GITHUB)


[uncategorized] ~8-~8: The official name of this software platform is spelled with a capital “H”.
Context: ...COMPLETE & PROVEN ## Overview The terraphim_github_runner crate provides a complete GitHu...

(GITHUB)


[grammar] ~153-~153: Ensure spelling is correct
Context: ...ommand Execution - Typical latency: 100-150ms per command - Includes SSH connection o...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[uncategorized] ~252-~252: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...wait?; ``` ## Future Enhancements ### Short Term 1. Dynamic SSH key generation per VM 2....

(EN_COMPOUND_ADJECTIVE_INTERNAL)

crates/terraphim_github_runner/END_TO_END_PROOF.md

[uncategorized] ~5-~5: The official name of this software platform is spelled with a capital “H”.
Context: ...rates the end-to-end integration of the terraphim_github_runner crate, proving that: 1. ✅ **Gi...

(GITHUB)


[uncategorized] ~139-~139: The official name of this software platform is spelled with a capital “H”.
Context: ...configuration problem, not a bug in the terraphim_github_runner code. Evidence from logs: ...

(GITHUB)


[uncategorized] ~175-~175: The official name of this software platform is spelled with a capital “H”.
Context: ...rmissions are fixed ## Conclusion The terraphim_github_runner implementation is **complete an...

(GITHUB)

blog-posts/github-runner-architecture.md

[uncategorized] ~13-~13: The official name of this software platform is spelled with a capital “H”.
Context: ...orld testing results. ## Overview The terraphim_github_runner crate provides a complete syste...

(GITHUB)


[uncategorized] ~335-~335: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...wait?; ``` ## Future Enhancements ### Short Term 1. Dynamic SSH key generation per VM 2....

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~349-~349: The official name of this software platform is spelled with a capital “H”.
Context: ... anomaly detection) ## Conclusion The terraphim_github_runner crate represents a complete int...

(GITHUB)


[uncategorized] ~363-~363: The official name of this software platform is spelled with a capital “H”.
Context: ...ate Summary**: [.docs/summary-terraphim_github_runner.md](../.docs/summary-terraphim_g...

(GITHUB)

.docs/summary.md

[uncategorized] ~110-~110: The official name of this software platform is spelled with a capital “H”.
Context: ... GitHub Runner Integration terraphim_github_runner (Complete & Proven): - **Purpo...

(GITHUB)


[grammar] ~132-~132: Ensure spelling is correct
Context: ...ing boot time) - Command Execution: 100-150ms typical latency - Learning Overhead: <1...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

HANDOVER.md

[uncategorized] ~4-~4: The official name of this software platform is spelled with a capital “H”.
Context: ...te**: 2025-12-25 Project: terraphim_github_runner Status: ✅ **COMPLETE & PROVE...

(GITHUB)


[uncategorized] ~13-~13: The official name of this software platform is spelled with a capital “H”.
Context: ... ## What Was Built ### Core Component: terraphim_github_runner Crate Location: `crates/terrap...

(GITHUB)


[grammar] ~204-~204: Ensure spelling is correct
Context: ...cho command: 127ms - Directory listing: 115ms - User check: 140ms ### SSH Authenticatio...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[uncategorized] ~215-~215: The official name of this software platform is spelled with a capital “H”.
Context: ...| File | Purpose | |------|---------| | crates/terraphim_github_runner/FIRECRACKER_FIX.md | Rootfs per...

(GITHUB)


[uncategorized] ~216-~216: The official name of this software platform is spelled with a capital “H”.
Context: ...KER_FIX.md| Rootfs permission fix | |crates/terraphim_github_runner/SSH_KEY_FIX.md` | SSH key path f...

(GITHUB)


[uncategorized] ~217-~217: The official name of this software platform is spelled with a capital “H”.
Context: .../SSH_KEY_FIX.md| SSH key path fix | |crates/terraphim_github_runner/TEST_USER_INIT.md` | Database in...

(GITHUB)


[uncategorized] ~218-~218: The official name of this software platform is spelled with a capital “H”.
Context: ..._INIT.md| Database initialization | |crates/terraphim_github_runner/END_TO_END_PROOF.md` | Integrati...

(GITHUB)


[uncategorized] ~246-~246: The official name of this software platform is spelled with a capital “H”.
Context: ...see SSH_KEY_FIX.md) ## Conclusion The terraphim_github_runner crate is production-ready a...

(GITHUB)

.docs/github-runner-ci-integration.md

[uncategorized] ~8-~8: The official name of this software platform is spelled with a capital “H”.
Context: ...# Overview Successfully integrated the terraphim_github_runner crate with GitHub Actions workf...

(GITHUB)


[grammar] ~189-~189: Ensure spelling is correct
Context: ...ing boot time) - Command Execution: 100-150ms typical latency - Learning Overhead: <1...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[uncategorized] ~237-~237: The official name of this software platform is spelled with a capital “H”.
Context: ...CD role configuration with ontology | | .docs/summary-terraphim_github_runner.md | GitHub runner crate refere...

(GITHUB)


[uncategorized] ~240-~240: The official name of this software platform is spelled with a capital “H”.
Context: ...ecture.md| Architecture blog post | |crates/terraphim_github_runner/FIRECRACKER_FIX.md` | Infrastruc...

(GITHUB)


[uncategorized] ~241-~241: The official name of this software platform is spelled with a capital “H”.
Context: ... | Infrastructure fix documentation | | crates/terraphim_github_runner/SSH_KEY_FIX.md | SSH key manage...

(GITHUB)


[uncategorized] ~242-~242: The official name of this software platform is spelled with a capital “H”.
Context: ... | SSH key management documentation | | crates/terraphim_github_runner/TEST_USER_INIT.md | Database in...

(GITHUB)


[uncategorized] ~243-~243: The official name of this software platform is spelled with a capital “H”.
Context: ...md| Database initialization guide | |crates/terraphim_github_runner/END_TO_END_PROOF.md` | Integrati...

(GITHUB)

crates/terraphim_github_runner/TEST_USER_INIT.md

[uncategorized] ~42-~42: The official name of this software platform is spelled with a capital “H”.
Context: ... ### 2. Test Users Created | user_id | github_id | username | subscription_tier | |--...

(GITHUB)


[uncategorized] ~213-~213: The official name of this software platform is spelled with a capital “H”.
Context: ... fcctl-web configuration bug, not a terraphim_github_runner code issue. --- *User initial...

(GITHUB)

🪛 markdownlint-cli2 (0.18.1)
.docs/summary-terraphim_github_runner.md

79-79: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


173-173: Bare URL used

(MD034, no-bare-urls)

blog-posts/github-runner-architecture.md

25-25: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


114-114: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


156-156: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


172-172: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


372-372: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

crates/terraphim_github_runner/SSH_KEY_FIX.md

75-75: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


119-119: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


142-142: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

HANDOVER.md

40-40: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


232-232: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


235-235: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


238-238: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


241-241: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

.docs/github-runner-ci-integration.md

57-57: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


86-86: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


212-212: Bare URL used

(MD034, no-bare-urls)

crates/terraphim_github_runner/TEST_USER_INIT.md

6-6: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: claude-review
🔇 Additional comments (7)
.docs/github-runner-ci-integration.md (1)

1-258: LGTM - Comprehensive integration documentation.

The document provides excellent coverage of the GitHub Runner CI/CD integration, including role configurations, workflow examples, architecture diagrams, and usage instructions. The structure is clear and the cross-references to related documentation files are helpful.

terraphim_server/default/devops_cicd_config.json (1)

1-223: Role configuration structure looks well-designed.

The three-role structure (Default, DevOps Engineer, GitHub Runner Specialist) with differentiated knowledge graphs, haystacks, and LLM configurations is well thought out. The extra metadata fields (specialization, core_modules, performance_metrics) provide useful context for the roles.

.docs/summary.md (1)

108-147: LGTM - Well-integrated GitHub Runner section.

The new section provides a concise summary of the terraphim_github_runner capabilities, core modules with LOC counts, performance metrics, and configuration options. It integrates well with the existing document structure and provides appropriate cross-references to detailed documentation.

crates/terraphim_github_runner/END_TO_END_PROOF.md (1)

1-195: LGTM - Excellent end-to-end proof documentation.

This document clearly demonstrates the integration works by providing:

  • Code snippets showing actual implementation
  • API response evidence
  • Test results for knowledge graph components
  • Honest distinction between code issues and infrastructure issues

The structured approach (What Was Proven → Infrastructure Issues → Conclusion) makes it easy to understand the current state.

blog-posts/github-runner-architecture.md (1)

1-372: LGTM - Well-crafted architecture article.

This blog post provides an excellent technical deep-dive into the GitHub runner architecture. The progression from high-level overview to component details, integration challenges, and usage examples is well-structured. The inclusion of actual performance metrics and test results adds credibility.

crates/terraphim_github_runner/TEST_USER_INIT.md (2)

1-219: LGTM - Useful troubleshooting documentation.

This document provides clear step-by-step documentation of the test user initialization process, including the problem, root cause, solution, and verification steps. The API response examples make it easy to verify correct behavior.


219-219: Missing closing backtick in code reference.

The script path reference is missing its closing backtick.

🔎 Suggested fix
-*Script: `/tmp/create_test_users.py`
+*Script: `/tmp/create_test_users.py`*

Likely an incorrect or invalid review comment.

### Optional Environment Variables
- `FIRECRACKER_VM_TYPE`: Default VM type (default: `bionic-test`)
- `RUST_LOG`: Logging verbosity (default: `info`)
- `OPENRouter_API_KEY`: For LLM-based workflow parsing
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Environment variable name has inconsistent casing.

The environment variable OPENRouter_API_KEY appears to have a typo in its casing. Standard convention would be OPENROUTER_API_KEY (all uppercase with underscores).

🔎 Suggested fix
-- `OPENRouter_API_KEY`: For LLM-based workflow parsing
+- `OPENROUTER_API_KEY`: For LLM-based workflow parsing
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- `OPENRouter_API_KEY`: For LLM-based workflow parsing
- `OPENROUTER_API_KEY`: For LLM-based workflow parsing
🤖 Prompt for AI Agents
In .docs/summary-terraphim_github_runner.md at line 116, the environment
variable name uses inconsistent casing `OPENRouter_API_KEY`; change it to the
correct conventional uppercase underscore form `OPENROUTER_API_KEY` (update the
markdown text and any surrounding references on this line to use the corrected
name).


---

*Proof generated: 2024-12-24*
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Date inconsistency with PR timeline.

Same issue as FIRECRACKER_FIX.md - the date shows "2024-12-24" but should likely be "2025-12-24".

🔎 Suggested fix
-*Proof generated: 2024-12-24*
+*Proof generated: 2025-12-24*
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
*Proof generated: 2024-12-24*
*Proof generated: 2025-12-24*
🤖 Prompt for AI Agents
In crates/terraphim_github_runner/END_TO_END_PROOF.md around line 194, the
generated proof date is incorrectly set to "2024-12-24"; update the date string
to "2025-12-24" to match the PR timeline and other docs, saving the file after
replacing the single occurrence.


---

*Fixed: 2024-12-24*
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Date appears inconsistent with PR timeline.

The fix date shows "2024-12-24" but the PR is from 2025-12-25. This may be a typo.

🔎 Suggested fix
-*Fixed: 2024-12-24*
+*Fixed: 2025-12-24*
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
*Fixed: 2024-12-24*
*Fixed: 2025-12-24*
🤖 Prompt for AI Agents
In crates/terraphim_github_runner/FIRECRACKER_FIX.md around line 85, the fix
date is listed as "2024-12-24" which conflicts with the PR date; update that
line to the correct date "2025-12-25" (or the intended date consistent with the
PR timeline) and save the file so the changelog entry matches the PR.

Comment on lines 56 to 73
test_users = [
{
"id": "testuser",
"github_id": 123456789,
"username": "testuser",
"email": "test@example.com",
"avatar_url": "https://avatars.githubusercontent.com/u/123456789",
"subscription_tier": "demo",
},
{
"id": "test_user_123",
"github_id": 123456789,
"username": "testuser",
"email": "test@example.com",
"avatar_url": "https://avatars.githubusercontent.com/u/123456789",
"subscription_tier": "demo",
},
]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Duplicate github_id for different users may cause database issues.

Both test users (testuser and test_user_123) have the same github_id: 123456789, but the schema shows github_id INTEGER UNIQUE NOT NULL. This would cause a unique constraint violation when inserting the second user.

🔎 Suggested fix
 test_users = [
     {
         "id": "testuser",
         "github_id": 123456789,
         "username": "testuser",
         "email": "test@example.com",
         "avatar_url": "https://avatars.githubusercontent.com/u/123456789",
         "subscription_tier": "demo",
     },
     {
         "id": "test_user_123",
-        "github_id": 123456789,
+        "github_id": 123456790,
         "username": "testuser",
         "email": "test@example.com",
-        "avatar_url": "https://avatars.githubusercontent.com/u/123456789",
+        "avatar_url": "https://avatars.githubusercontent.com/u/123456790",
         "subscription_tier": "demo",
     },
 ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test_users = [
{
"id": "testuser",
"github_id": 123456789,
"username": "testuser",
"email": "test@example.com",
"avatar_url": "https://avatars.githubusercontent.com/u/123456789",
"subscription_tier": "demo",
},
{
"id": "test_user_123",
"github_id": 123456789,
"username": "testuser",
"email": "test@example.com",
"avatar_url": "https://avatars.githubusercontent.com/u/123456789",
"subscription_tier": "demo",
},
]
test_users = [
{
"id": "testuser",
"github_id": 123456789,
"username": "testuser",
"email": "test@example.com",
"avatar_url": "https://avatars.githubusercontent.com/u/123456789",
"subscription_tier": "demo",
},
{
"id": "test_user_123",
"github_id": 123456790,
"username": "testuser",
"email": "test@example.com",
"avatar_url": "https://avatars.githubusercontent.com/u/123456790",
"subscription_tier": "demo",
},
]
🤖 Prompt for AI Agents
In crates/terraphim_github_runner/TEST_USER_INIT.md around lines 56 to 73, two
test users share the same github_id (123456789) which violates the schema
constraint github_id INTEGER UNIQUE NOT NULL; change one of the users to a
different unique integer github_id (e.g., 123456790) so each test user has a
distinct github_id and the insertion won't fail due to unique constraint
violations.

Comment on lines 176 to 182
{
"location": "/home/alex/projects/terraphim/firecracker-rust/fcctl-web",
"service": "Ripgrep",
"read_only": true,
"atomic_server_secret": null,
"extra_parameters": {}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Hardcoded absolute path will break portability.

The haystack location /home/alex/projects/terraphim/firecracker-rust/fcctl-web is an absolute path specific to one developer's machine. This will fail for other contributors or in CI/CD environments.

Consider using a relative path, environment variable, or removing this haystack if it references an external project.

🔎 Suggested fix options

Option 1: Remove if external project

-        {
-          "location": "/home/alex/projects/terraphim/firecracker-rust/fcctl-web",
-          "service": "Ripgrep",
-          "read_only": true,
-          "atomic_server_secret": null,
-          "extra_parameters": {}
-        }

Option 2: Use environment variable placeholder (if supported)

-          "location": "/home/alex/projects/terraphim/firecracker-rust/fcctl-web",
+          "location": "${FCCTL_WEB_PATH:-./external/fcctl-web}",
🤖 Prompt for AI Agents
In terraphim_server/default/devops_cicd_config.json around lines 176 to 182, the
"location" field contains a hardcoded absolute path (/home/alex/...), which
breaks portability; replace it with a portable solution: either remove the entry
if it points to an external project, or change "location" to a relative path
from the repo root (e.g., "./firecracker-rust/fcctl-web") or reference an
environment variable placeholder (e.g., "${FCCTL_WEB_PATH}") and update CI/dev
docs to set that variable; ensure read_only and other keys remain unchanged.

@claude
Copy link

claude bot commented Dec 26, 2025

Code Review: DevOps/CI-CD Role Configuration and GitHub Runner Integration

Summary

This PR adds comprehensive DevOps/CI-CD role configuration with GitHub runner integration, including role ontology and workflow execution capabilities. Overall, this is a well-structured addition with good test coverage. However, there are several important security, error handling, and code quality concerns that should be addressed.


🔴 Critical Issues

1. Security: Error Handling Reveals Sensitive Information

Location: crates/terraphim_github_runner/src/workflow/vm_executor.rs:153-156

let error_msg = body["error"]
    .as_str()
    .unwrap_or("Unknown error")
    .to_string();

Issue: Error messages from the API are directly returned to callers without sanitization. This could leak sensitive information like internal paths, API tokens, or system details.

Recommendation: Sanitize error messages and log full details securely:

let error_msg = body["error"].as_str().unwrap_or("Unknown error");
warn\!("Command execution failed: {}", error_msg);
// Return sanitized error
Ok(CommandResult {
    exit_code: 1,
    stdout: String::new(),
    stderr: "Command execution failed".to_string(),
    duration,
})

2. Security: Timing Attack Vulnerability in Signature Verification

Location: crates/terraphim_github_runner_server/src/webhook/signature.rs:23

Ok(hex_signature == signature)

Issue: Using == for comparing HMAC signatures is vulnerable to timing attacks. An attacker could potentially determine the correct signature byte-by-byte.

Recommendation: Use constant-time comparison:

use subtle::ConstantTimeEq;
Ok(hex_signature.as_bytes().ct_eq(signature.as_bytes()).into())

Add subtle = "2.5" to dependencies.

3. Security: Missing Authentication Token Validation

Location: crates/terraphim_github_runner/src/workflow/vm_executor.rs:39

let auth_token = std::env::var("FIRECRACKER_AUTH_TOKEN").ok();

Issue: The auth token is read from environment but never validated for format or length. Empty or malformed tokens could cause issues.

Recommendation: Validate the token:

let auth_token = std::env::var("FIRECRACKER_AUTH_TOKEN")
    .ok()
    .filter(|t| \!t.is_empty() && t.len() >= 32);
if auth_token.is_none() {
    warn\!("FIRECRACKER_AUTH_TOKEN not set or invalid");
}

🟡 High Priority Issues

4. Error Handling: Excessive use of .unwrap() and .expect()

Multiple Locations: Found 20+ instances across the codebase

Examples:

  • vm_executor.rs:36: .expect("Failed to create HTTP client")
  • vm_executor.rs:293: .lock().unwrap()
  • Test files with multiple .unwrap() calls

Issue: Per CLAUDE.md guidelines, Rust code should "Embrace Rust's Result and Option types for error handling" and "Handle errors and edge cases early". Panicking on errors violates defensive programming principles.

Recommendation:

  • Replace .expect() in constructors with proper error propagation
  • Use `?" operator for Result types
  • Handle lock poisoning explicitly in production code
  • Reserve .unwrap()/.expect() only for tests or truly infallible operations

5. Performance: HTTP Client Creation in Constructor

Location: crates/terraphim_github_runner/src/workflow/vm_executor.rs:33-36

let client = reqwest::Client::builder()
    .timeout(Duration::from_secs(300))
    .build()
    .expect("Failed to create HTTP client");

Issue:

  1. Panics on client creation failure instead of returning Result
  2. Creates client in constructor (not following builder pattern)
  3. 300-second timeout seems excessive

Recommendation:

pub fn new(api_base_url: impl Into<String>) -> Result<Self> {
    let client = reqwest::Client::builder()
        .timeout(Duration::from_secs(60)) // More reasonable timeout
        .build()
        .map_err(|e| GitHubRunnerError::ConfigError(format\!("Failed to create HTTP client: {}", e)))?;
    // ...
}

6. Security: Hardcoded Absolute Path in Configuration

Location: terraphim_server/default/devops_cicd_config.json:177

"location": "/home/alex/projects/terraphim/firecracker-rust/fcctl-web"

Issue: Hardcoded user-specific absolute path in configuration. This will fail on other systems and could leak username information.

Recommendation: Use relative path or environment variable:

"location": "../firecracker-rust/fcctl-web"

Or make it configurable via environment variable.


🟢 Medium Priority Issues

7. Code Quality: Missing Input Validation

Location: vm_executor.rs:91

The execute method doesn't validate:

  • Empty command strings
  • Null bytes in commands (could cause injection)
  • Working directory exists/is valid

Recommendation: Add validation:

if command.is_empty() {
    return Err(GitHubRunnerError::ExecutionFailed {
        command: command.to_string(),
        reason: "Command cannot be empty".to_string(),
    });
}
if command.contains('\0') {
    return Err(GitHubRunnerError::ExecutionFailed {
        command: command.to_string(),
        reason: "Command contains null bytes".to_string(),
    });
}

8. Testing: No Test Coverage for Error Paths

Location: vm_executor.rs:375-461

Issue: Tests only cover happy paths. Missing tests for:

  • Network failures
  • API errors (4xx, 5xx responses)
  • Timeout scenarios
  • Malformed API responses
  • Authentication failures

Recommendation: Add comprehensive error scenario tests following the project's "never use mocks in tests" principle - use real error responses.

9. Documentation: Missing Security Considerations

Location: .docs/github-runner-ci-integration.md

Issue: Documentation doesn't mention:

  • Security implications of Firecracker VM access
  • Token rotation best practices
  • Webhook signature verification requirements
  • Isolation guarantees and limitations

Recommendation: Add security section to documentation.

10. Performance: Potential Memory Leak in Execution Log

Location: vm_executor.rs:254

execution_log: std::sync::Mutex<Vec<ExecutionLogEntry>>,

Issue: The SimulatedVmExecutor accumulates execution logs indefinitely without bounds. In long-running processes, this could consume excessive memory.

Recommendation: Either:

  • Use a bounded queue (VecDeque with max size)
  • Add a clear_log() method
  • Document that this is for testing only and add size limits

Positive Aspects

  1. Excellent Test Coverage: 49 passing tests with real Firecracker VM integration
  2. Good Separation of Concerns: Clear module boundaries between workflow, VM execution, and webhook handling
  3. Comprehensive Documentation: Well-documented architecture and setup procedures
  4. Async Best Practices: Proper use of async_trait and tokio patterns
  5. Configuration Design: Role-based configuration is well-structured and extensible
  6. Security Foundation: HMAC signature verification is implemented (just needs constant-time comparison)

📋 Minor Issues / Suggestions

  1. Code Style: The test workflow .github/workflows/test-ci.yml only contains echo statements. Consider adding actual test commands or removing if not needed yet.

  2. Dependency Management: Consider pinning critical security-related dependencies (hmac, sha2, reqwest) to specific versions in Cargo.toml.

  3. Logging Consistency: Mix of info\!, warn\!, debug\! - ensure consistent logging levels per CLAUDE.md conventions.

  4. Type Safety: Consider using newtype pattern for sensitive data like auth tokens to prevent accidental logging:

struct AuthToken(String);
impl Debug for AuthToken {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write\!(f, "AuthToken([REDACTED])")
    }
}

🎯 Recommendations Priority

Must Fix Before Merge:

Should Fix Before Merge:

Can Be Follow-up PRs:


📊 Overall Assessment

Code Quality: ⭐⭐⭐⭐ (4/5)
Security: ⭐⭐⭐ (3/5) - Has good foundations but needs fixes
Test Coverage: ⭐⭐⭐⭐⭐ (5/5)
Documentation: ⭐⭐⭐⭐ (4/5)
Architecture: ⭐⭐⭐⭐⭐ (5/5)

Recommendation: Approve with changes - Address critical security issues (#2, #3, #6) and improve error handling (#1, #4, #5) before merging.


This is solid foundational work for the GitHub runner integration. The architecture is well-designed and the test coverage is excellent. With the security and error handling improvements, this will be production-ready.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (16)
crates/terraphim_github_runner_server/Cargo.toml (1)

8-52: Run cargo audit against Cargo.lock to verify transitive dependencies are free from known vulnerabilities.

The specified versions appear sound: jsonwebtoken is on version 9 (the fixed series addressing prior Auth0/Okta vulnerabilities), and no public RustSec advisories were found for salvo 0.74.3, octocrab 0.42.1, hmac 0.12, or sha2 0.10. However, transitive dependencies from reqwest 0.12 may have advisories; run cargo audit to ensure your complete dependency tree is clean.

docs/github-runner-setup.md (1)

523-525: Format bare URLs as proper Markdown links.

The support section contains bare URLs that should be formatted as proper Markdown links for better readability and maintainability.

🔎 Proposed fix
 ## Support

-- **Issues**: https://github.com/terraphim/terraphim-ai/issues
-- **Docs**: https://github.com/terraphim/terraphim-ai/tree/main/docs
+- **Issues**: [GitHub Issues](https://github.com/terraphim/terraphim-ai/issues)
+- **Docs**: [Documentation](https://github.com/terraphim/terraphim-ai/tree/main/docs)
 - **Discord**: [Join our Discord](https://discord.gg/terraphim)
docs/github-runner-architecture.md (1)

537-549: Use proper Markdown headings instead of bold text.

The troubleshooting section uses bold text for subsections. These should be formatted as proper Markdown headings (e.g., ####) for better document structure, navigation, and accessibility.

🔎 Proposed fix
 ### Common Issues

-**1. "Invalid webhook signature"**
+#### 1. "Invalid webhook signature"
+
 - Check `GITHUB_WEBHOOK_SECRET` matches GitHub repo settings
 - Verify signature calculation includes full body

-**2. "Model not found" (Ollama)**
+#### 2. "Model not found" (Ollama)
+
 - Pull model: `ollama pull gemma3:4b`
 - Check `OLLAMA_BASE_URL` is correct

-**3. "Firecracker API unreachable"**
+#### 3. "Firecracker API unreachable"
+
 - Verify Firecracker is running: `curl http://127.0.0.1:8080/health`
 - Check `FIRECRACKER_API_URL` configuration

-**4. "VM allocation failed"**
+#### 4. "VM allocation failed"
+
 - Check Firecracker resources (CPU, memory)
 - Verify JWT token if auth enabled
crates/terraphim_github_runner_server/src/workflow/execution.rs (2)

265-268: Consider integrating the LearningCoordinator into workflow execution.

The LearningCoordinator is created but never used (note the _ prefix). According to the PR objectives and architecture documentation, the learning coordinator should track execution patterns to optimize future runs.

Consider passing the learning coordinator to the WorkflowExecutor or manually recording execution results to enable pattern learning. For example:

🔎 Proposed integration
     // Create learning coordinator
     info!("🧠 Initializing LearningCoordinator for pattern tracking");
-    let _learning_coordinator: Arc<dyn LearningCoordinator> =
+    let learning_coordinator: Arc<dyn LearningCoordinator> =
         Arc::new(InMemoryLearningCoordinator::new("github-runner"));

     // ... (create session manager and workflow executor)

     // Execute workflow
     info!("Starting workflow execution: {}", workflow.name);
     let result = workflow_executor
         .execute_workflow(&workflow, &context)
         .await;

+    // Record execution for learning
+    if let Ok(ref workflow_result) = result {
+        for step in &workflow_result.steps {
+            let _ = learning_coordinator.record_command_execution(
+                &step.command,
+                matches!(step.status, ExecutionStatus::Success),
+                step.duration_ms,
+            ).await;
+        }
+    }

Alternatively, if learning integration is deferred, add a TODO comment explaining when and how it will be integrated.


87-223: Consider simplifying the YAML parsing logic.

The parse_workflow_yaml_simple function implements manual YAML parsing with complex state tracking (lines 99-205). This logic is fragile and may miss edge cases in GitHub Actions syntax.

Consider using a proper YAML parsing library like serde_yaml to parse the workflow structure, then extract the relevant fields. This would be more maintainable and handle edge cases better.

🔎 Alternative approach using serde_yaml
use serde_yaml::Value;
use std::collections::HashMap;

pub fn parse_workflow_yaml_simple(workflow_path: &Path) -> Result<ParsedWorkflow> {
    let workflow_yaml = fs::read_to_string(workflow_path)?;
    let workflow_name = workflow_path
        .file_name()
        .and_then(|n| n.to_str())
        .unwrap_or("unknown")
        .to_string();

    let yaml: Value = serde_yaml::from_str(&workflow_yaml)?;
    let mut steps = vec![];
    
    if let Some(jobs) = yaml.get("jobs").and_then(|j| j.as_mapping()) {
        for (_, job) in jobs {
            if let Some(job_steps) = job.get("steps").and_then(|s| s.as_sequence()) {
                for step in job_steps {
                    let name = step.get("name")
                        .and_then(|n| n.as_str())
                        .unwrap_or("Unnamed step");
                    let command = step.get("run")
                        .and_then(|r| r.as_str())
                        .unwrap_or("");
                    
                    if !command.is_empty() {
                        steps.push(WorkflowStep {
                            name: name.to_string(),
                            command: command.to_string(),
                            working_dir: "/workspace".to_string(),
                            continue_on_error: false,
                            timeout_seconds: 300,
                        });
                    }
                }
            }
        }
    }

    Ok(ParsedWorkflow {
        name: workflow_name,
        trigger: "webhook".to_string(),
        environment: HashMap::new(),
        setup_commands: vec![
            "echo 'Starting workflow execution'".to_string(),
            "cd /workspace || mkdir -p /workspace".to_string(),
        ],
        steps,
        cleanup_commands: vec!["echo 'Workflow execution complete'".to_string()],
        cache_paths: vec![],
    })
}

Note: This would require adding serde_yaml as a dependency if not already present.

crates/terraphim_github_runner_server/src/webhook/signature.rs (1)

30-45: Use #[tokio::test] instead of creating runtime manually.

The tests create a new tokio::runtime::Runtime per test, but #[tokio::test] is already available and cleaner. This is especially relevant if verify_signature becomes synchronous.

🔎 Proposed refactor for tests
-    #[test]
-    fn test_verify_signature_valid() {
+    #[tokio::test]
+    async fn test_verify_signature_valid() {
         let secret = "test_secret";
         let body = b"test payload";
 
         // Generate valid signature
         let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes()).unwrap();
         mac.update(body);
         let signature = format!("sha256={}", hex::encode(mac.finalize().into_bytes()));
 
-        let result = tokio::runtime::Runtime::new()
-            .unwrap()
-            .block_on(verify_signature(secret, &signature, body));
+        let result = verify_signature(secret, &signature, body).await;
 
         assert!(result.unwrap());
     }
crates/terraphim_github_runner/tests/end_to_end_test.rs (3)

99-168: Reduce code duplication in VM discovery/creation logic.

The VM creation logic is duplicated between lines 118-140 and 142-164. Consider extracting a helper function.

🔎 Proposed helper function
async fn get_or_create_vm(client: &reqwest::Client, jwt_token: &str) -> String {
    let list_response = client
        .get("http://127.0.0.1:8080/api/vms")
        .bearer_auth(jwt_token)
        .send()
        .await
        .expect("Failed to list VMs");

    let vms: serde_json::Value = list_response.json().await.expect("Failed to parse VM list");

    if let Some(vms_array) = vms["vms"].as_array() {
        if let Some(vm) = vms_array.iter().find(|v| v["status"] == "running") {
            println!("✅ Using existing VM: {}", vm["id"]);
            return vm["id"].as_str().unwrap().to_string();
        }
    }

    // Create new VM
    println!("Creating new VM...");
    let create_response = client
        .post("http://127.0.0.1:8080/api/vms")
        .bearer_auth(jwt_token)
        .json(&serde_json::json!({"name": "test-runner", "vm_type": "bionic-test"}))
        .send()
        .await
        .expect("Failed to create VM");

    let new_vm: serde_json::Value = create_response
        .json()
        .await
        .expect("Failed to parse create VM response");

    let vm_id = new_vm["id"].as_str().unwrap().to_string();
    println!("✅ Created new VM: {}", vm_id);

    // Wait for VM to boot
    println!("⏳ Waiting 10 seconds for VM to boot...");
    tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
    vm_id
}

275-305: Consider logging learning coordinator errors instead of silently ignoring them.

The let _ = coordinator.record_success(...) and let _ = coordinator.record_failure(...) patterns silently discard errors. While this may be intentional for the test, it could mask issues with the learning coordinator.

🔎 Proposed fix to log errors
-                    // Record success in learning coordinator
-                    let _ = coordinator
-                        .record_success(&step.command, result.duration.as_millis() as u64, &context)
-                        .await;
+                    // Record success in learning coordinator
+                    if let Err(e) = coordinator
+                        .record_success(&step.command, result.duration.as_millis() as u64, &context)
+                        .await
+                    {
+                        println!("   ⚠️ Failed to record success: {}", e);
+                    }

360-367: The main() function doesn't run tests.

This main() function only prints instructions and doesn't actually execute any tests. This is unusual for a test file - consider either removing it or documenting its purpose more clearly.

crates/terraphim_github_runner_server/src/config/mod.rs (1)

43-57: Silent fallback on PORT parse error may hide misconfiguration.

If PORT contains an invalid value like "abc", the code silently falls back to 3000. Consider logging a warning or returning an error for invalid port values.

Also, firecracker_auth_token defaults to an empty string, but the code in main.rs (line 146-150) treats empty string as "no token". Consider using Option<String> for consistency with github_token.

🔎 Proposed improvement for PORT validation
 Ok(Settings {
     port: env::var("PORT")
         .ok()
-        .and_then(|p| p.parse().ok())
+        .and_then(|p| {
+            p.parse().map_err(|e| {
+                eprintln!("Warning: Invalid PORT '{}': {}, using default 3000", p, e);
+                e
+            }).ok()
+        })
         .unwrap_or(3000),
crates/terraphim_github_runner_server/src/workflow/discovery.rs (1)

83-161: Simple YAML parsing has known limitations that may cause false positives/negatives.

The comment on line 84-85 acknowledges this, but some specific issues:

  1. Line 119: trimmed.starts_with("push:") could match push_to_deploy: or similar keys.
  2. Lines 127-139: Only handles inline array format [main, develop], not multi-line YAML arrays.
  3. Line 114: trimmed.contains("pull_request") matches anywhere, including in comments.

For production use, consider using serde_yaml to properly parse the on: section.

🔎 Proposed more robust push detection
-            // Check for push trigger
-            if trimmed.starts_with("push:") || trimmed.starts_with("push ") {
+            // Check for push trigger (ensure exact match, not prefix of another key)
+            if trimmed == "push:" || trimmed == "push" || trimmed.starts_with("push:") && !trimmed.contains("_") {
                 has_push = true;
                 in_push_section = true;
             }

Consider tracking this as technical debt for a proper YAML parser integration.

crates/terraphim_github_runner_server/src/main.rs (3)

164-172: Settings are reloaded on every webhook request.

Settings::from_env() is called for each incoming request (line 166), which re-reads environment variables and allocates new strings. Consider loading settings once at startup and sharing via Arc or Salvo's Depot.

🔎 Proposed approach using Arc
// In main():
let settings = Arc::new(Settings::from_env()?);

let router = Router::new()
    .hoop(affix_state::inject(settings))
    .push(Router::with_path("webhook").post(handle_webhook));

// In handler:
#[handler]
async fn handle_webhook(req: &mut Request, depot: &mut Depot, res: &mut Response) -> Result<(), StatusError> {
    let settings = depot.obtain::<Arc<Settings>>().unwrap();
    // ...
}

131-139: Casting i64 to u64 for PR number could wrap on negative values.

Line 135 casts webhook.number (i64) directly to u64. While GitHub PR numbers are always positive, defensive coding would validate this.

🔎 Proposed validation
-                number: webhook.number as u64,
+                number: webhook.number.try_into().unwrap_or(0),

243-276: Background tasks silently continue on errors.

The spawned task logs errors but the caller has no way to know if workflow execution failed. For observability, consider emitting metrics or using a result channel.

crates/terraphim_github_runner/src/workflow/vm_executor.rs (2)

32-47: HTTP client creation uses expect() which panics on failure.

While reqwest::Client::builder().build() is unlikely to fail, using expect() in a library function can cause unexpected panics. Consider returning a Result from constructors.

🔎 Proposed fallible constructor
impl VmCommandExecutor {
    pub fn new(api_base_url: impl Into<String>) -> Result<Self> {
        let client = reqwest::Client::builder()
            .timeout(Duration::from_secs(300))
            .build()
            .map_err(|e| GitHubRunnerError::ConfigError(format!("Failed to create HTTP client: {}", e)))?;

        let auth_token = std::env::var("FIRECRACKER_AUTH_TOKEN").ok();

        Ok(Self {
            api_base_url: api_base_url.into(),
            client,
            snapshot_counter: AtomicU64::new(0),
            auth_token,
        })
    }
}

291-295: Mutex lock uses unwrap() which panics on poisoned mutex.

self.execution_log.lock().unwrap() will panic if another thread panicked while holding the lock. In a testing utility, this is acceptable, but consider documenting this behavior.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d36a79f and 0abd16d.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • .github/workflows/test-ci.yml
  • crates/terraphim_github_runner/Cargo.toml
  • crates/terraphim_github_runner/src/lib.rs
  • crates/terraphim_github_runner/src/workflow/mod.rs
  • crates/terraphim_github_runner/src/workflow/vm_executor.rs
  • crates/terraphim_github_runner/tests/end_to_end_test.rs
  • crates/terraphim_github_runner_server/Cargo.toml
  • crates/terraphim_github_runner_server/README.md
  • crates/terraphim_github_runner_server/src/config/mod.rs
  • crates/terraphim_github_runner_server/src/github/mod.rs
  • crates/terraphim_github_runner_server/src/main.rs
  • crates/terraphim_github_runner_server/src/webhook/mod.rs
  • crates/terraphim_github_runner_server/src/webhook/signature.rs
  • crates/terraphim_github_runner_server/src/workflow/discovery.rs
  • crates/terraphim_github_runner_server/src/workflow/execution.rs
  • crates/terraphim_github_runner_server/src/workflow/mod.rs
  • docs/github-runner-architecture.md
  • docs/github-runner-setup.md
🧰 Additional context used
🧬 Code graph analysis (5)
crates/terraphim_github_runner_server/src/workflow/mod.rs (2)
crates/terraphim_github_runner_server/src/workflow/discovery.rs (1)
  • discover_workflows_for_event (15-72)
crates/terraphim_github_runner_server/src/workflow/execution.rs (1)
  • execute_workflows_in_vms (353-401)
crates/terraphim_github_runner_server/src/webhook/mod.rs (1)
crates/terraphim_github_runner_server/src/webhook/signature.rs (1)
  • verify_signature (16-24)
crates/terraphim_github_runner_server/src/main.rs (7)
crates/terraphim_github_runner_server/src/github/mod.rs (1)
  • post_pr_comment (15-41)
crates/terraphim_github_runner_server/src/webhook/signature.rs (1)
  • verify_signature (16-24)
crates/terraphim_github_runner_server/src/workflow/discovery.rs (1)
  • discover_workflows_for_event (15-72)
crates/terraphim_github_runner_server/src/workflow/execution.rs (1)
  • execute_workflows_in_vms (353-401)
crates/terraphim_github_runner_server/src/config/mod.rs (1)
  • from_env (38-58)
crates/terraphim_service/src/llm.rs (1)
  • build_llm_from_role (56-136)
crates/terraphim_github_runner/tests/end_to_end_test.rs (1)
  • main (361-367)
crates/terraphim_github_runner_server/src/workflow/execution.rs (4)
crates/terraphim_agent/src/robot/output.rs (1)
  • format (185-195)
crates/terraphim_github_runner/src/workflow/vm_executor.rs (4)
  • new (32-47)
  • new (274-281)
  • with_auth (50-62)
  • default (268-270)
crates/terraphim_github_runner/src/workflow/executor.rs (2)
  • session_manager (470-472)
  • with_executor (182-192)
crates/terraphim_github_runner/src/session/manager.rs (1)
  • with_provider (131-138)
crates/terraphim_github_runner_server/src/config/mod.rs (1)
crates/terraphim_config/examples/atomic_server_config.rs (1)
  • std (399-401)
🪛 LanguageTool
docs/github-runner-architecture.md

[uncategorized] ~83-~83: The official name of this software platform is spelled with a capital “H”.
Context: ...``` ## Components ### 1. HTTP Server (terraphim_github_runner_server) Framework: Salvo (...

(GITHUB)


[uncategorized] ~121-~121: The official name of this software platform is spelled with a capital “H”.
Context: ...ws] ``` Discovery Process: 1. Scan .github/workflows/ directory 2. Parse YAML fro...

(GITHUB)

crates/terraphim_github_runner_server/README.md

[uncategorized] ~74-~74: The official name of this software platform is spelled with a capital “H”.
Context: ...`` ### 2. Create Workflow File Create .github/workflows/test.yml: ```yaml name: Tes...

(GITHUB)

docs/github-runner-setup.md

[uncategorized] ~133-~133: The official name of this software platform is spelled with a capital “H”.
Context: ...`` ### 3. Create Test Workflow Create .github/workflows/test.yml: ```yaml name: Ter...

(GITHUB)

🪛 markdownlint-cli2 (0.18.1)
docs/github-runner-architecture.md

12-12: Link fragments should be valid

(MD051, link-fragments)


13-13: Link fragments should be valid

(MD051, link-fragments)


150-150: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


537-537: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


541-541: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


545-545: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


549-549: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


596-596: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

crates/terraphim_github_runner_server/README.md

316-316: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

docs/github-runner-setup.md

523-523: Bare URL used

(MD034, no-bare-urls)


524-524: Bare URL used

(MD034, no-bare-urls)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: claude-review
🔇 Additional comments (12)
crates/terraphim_github_runner/Cargo.toml (2)

53-53: No action needed. The env_logger dependency "0.11" resolves to the latest version in the 0.11 series (0.11.8), which is current.


32-33: The reqwest dependency configuration is appropriate for the intended use case. Analysis of the codebase shows that reqwest is used exclusively for local HTTP API calls to the Firecracker VM control endpoint (e.g., http://127.0.0.1:8080/api/vms), as seen in src/workflow/vm_executor.rs and test files. No HTTPS connections are made via this HTTP client. While the codebase contains GitHub repository URLs, they are not accessed through reqwest and therefore do not require TLS features. The version 0.12 is stable with no direct security advisories.

Likely an incorrect or invalid review comment.

.github/workflows/test-ci.yml (1)

13-25: Verify this is intentionally a mock workflow.

This workflow only prints messages without performing actual checkout, test execution, or build operations. If this is a test/demo workflow for validating the GitHub runner integration, consider adding a comment in the file explaining its purpose to prevent confusion.

If this is meant to be a functional CI workflow, the steps should be updated to:

  • Use actions/checkout@v4 for code checkout
  • Execute actual test commands (e.g., cargo test)
  • Execute actual build commands (e.g., cargo build)
🔎 Example of a functional workflow
     steps:
       - name: Checkout code
-        run: echo "Checking out code..."
+        uses: actions/checkout@v4

       - name: Run tests
         run: |
-          echo "Running tests..."
-          echo "✓ Test 1 passed"
-          echo "✓ Test 2 passed"
+          cargo test --verbose

       - name: Build project
         run: |
-          echo "Building project..."
-          echo "Build complete!"
+          cargo build --release
crates/terraphim_github_runner_server/src/webhook/mod.rs (1)

1-3: LGTM!

Clean module structure with appropriate re-export of the signature verification functionality.

crates/terraphim_github_runner/src/lib.rs (1)

64-67: LGTM! VM executor exports added correctly.

The new exports SimulatedVmExecutor and VmCommandExecutor properly extend the public API to support Firecracker-based VM execution, aligning with the PR's objectives.

crates/terraphim_github_runner_server/README.md (1)

1-375: Excellent comprehensive documentation!

This README provides thorough coverage of features, setup, configuration, troubleshooting, and deployment options. The included code examples, environment variables table, and testing instructions make it easy for users to get started with the GitHub Runner Server.

crates/terraphim_github_runner/src/workflow/mod.rs (1)

6-16: LGTM! VM executor module integrated properly.

The addition of the vm_executor module and its public exports (SimulatedVmExecutor and VmCommandExecutor) cleanly extends the workflow module to support Firecracker-based execution.

crates/terraphim_github_runner_server/src/github/mod.rs (1)

1-41: LGTM!

Clean implementation of PR comment posting with appropriate error handling. The graceful fallback when GITHUB_TOKEN is unset allows the server to operate in environments where PR commenting isn't needed.

crates/terraphim_github_runner_server/src/workflow/mod.rs (1)

1-5: LGTM!

Clean module organization with appropriate public re-exports for the workflow discovery and execution API surface.

crates/terraphim_github_runner_server/src/workflow/discovery.rs (1)

163-220: Good test coverage for the YAML parsing logic.

The tests cover the main scenarios (pull_request, push with branches, no matching trigger). Consider adding edge case tests for the parsing limitations mentioned above.

crates/terraphim_github_runner_server/src/main.rs (1)

431-461: Tests use unsafe for environment variable manipulation.

The unsafe { std::env::set_var(...) } blocks are acceptable for test isolation, but note that concurrent tests modifying environment variables could interfere with each other. Consider using #[serial_test::serial] if flakiness occurs.

crates/terraphim_github_runner/src/workflow/vm_executor.rs (1)

375-461: Good test coverage for the SimulatedVmExecutor.

Tests cover success, failure, snapshot creation, and rollback scenarios. Well structured.

[package]
name = "terraphim_github_runner_server"
version = "0.1.0"
edition = "2024"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Invalid Rust edition - compilation will fail.

Rust edition "2024" does not exist. Valid editions are 2015, 2018, and 2021. This will cause immediate compilation failure.

🔎 Proposed fix
-edition = "2024"
+edition = "2021"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
edition = "2024"
edition = "2021"
🤖 Prompt for AI Agents
In crates/terraphim_github_runner_server/Cargo.toml at line 4 the edition is set
to "2024", which is invalid and will break compilation; change the edition field
to a valid Rust edition (e.g., "2021") in this Cargo.toml (and any workspace
member Cargo.toml files if present) and then run cargo check/build to verify the
project compiles.

Comment on lines 16 to 24
pub async fn verify_signature(secret: &str, signature: &str, body: &[u8]) -> Result<bool> {
let signature = signature.replace("sha256=", "");
let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes())?;
mac.update(body);
let result = mac.finalize().into_bytes();
let hex_signature = hex::encode(result);

Ok(hex_signature == signature)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

subtle crate rust constant time comparison ConstantTimeEq

💡 Result:

The subtle crate provides constant-time primitives for Rust crypto. Its ConstantTimeEq trait exposes:

  • fn ct_eq(&self, other: &Self) -> Choice — returns Choice(1) if equal, Choice(0) if not; intended to run in constant time. [1][2]
  • A default ct_ne was added in v2.5.0. [3]

Example:

use subtle::ConstantTimeEq;
let a: [u8; 3] = [1,2,3];
let b: [u8; 3] = [1,2,4];
assert_eq!(a.ct_eq(&b).unwrap_u8(), 0);
assert_eq!(a.ct_eq(&a).unwrap_u8(), 1);

Notes: subtle is a “best-effort” library — constant-timeness depends on CPU, compiler optimizations, and how bitwise ops are lowered/optimized; use with care. [1]

Sources:
[1] subtle crate docs (overview).
[2] ConstantTimeEq trait docs (ct_eq).
[3] subtle CHANGELOG (ct_ne added in 2.5.0).


🏁 Script executed:

#!/bin/bash
# Check if subtle is a dependency in the project
find . -name "Cargo.toml" -type f | head -20 | xargs grep -l "subtle" 2>/dev/null || echo "No Cargo.toml files found with subtle dependency"

# Find and inspect the signature.rs file
find . -path "*/webhook/signature.rs" -type f

Repository: terraphim/terraphim-ai

Length of output: 179


🏁 Script executed:

#!/bin/bash
# Read the signature.rs file to verify current state
cat -n ./crates/terraphim_github_runner_server/src/webhook/signature.rs | head -50

Repository: terraphim/terraphim-ai

Length of output: 1953


Use constant-time comparison for HMAC signature verification.

The current == comparison on line 23 is vulnerable to timing attacks. HMAC verification is a security-sensitive operation where constant-time comparison is a best practice. Use the subtle crate (which will need to be added to dependencies) with its ConstantTimeEq trait to mitigate this risk.

Additionally, the function is marked async but contains no .await points—consider removing the async marker to make it synchronous.

use anyhow::Result;
use hmac::{Hmac, Mac};
use sha2::Sha256;
+use subtle::ConstantTimeEq;

-pub async fn verify_signature(secret: &str, signature: &str, body: &[u8]) -> Result<bool> {
+pub fn verify_signature(secret: &str, signature: &str, body: &[u8]) -> Result<bool> {
     let signature = signature.replace("sha256=", "");
     let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes())?;
     mac.update(body);
     let result = mac.finalize().into_bytes();
     let hex_signature = hex::encode(result);
+    let expected_bytes = hex_signature.as_bytes();
+    let provided_bytes = signature.as_bytes();

-    Ok(hex_signature == signature)
+    Ok(expected_bytes.len() == provided_bytes.len()
+        && expected_bytes.ct_eq(provided_bytes).into())
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 16 to 112
struct FirecrackerVmProvider {
_api_base_url: String,
_auth_token: Option<String>,
}

#[async_trait::async_trait]
impl VmProvider for FirecrackerVmProvider {
async fn allocate(&self, _vm_type: &str) -> RunnerResult<(String, Duration)> {
// This is a placeholder - in real implementation, we'd call the Firecracker API
// For now, return a mock VM ID
Ok((
format!("fc-vm-{}", uuid::Uuid::new_v4()),
Duration::from_millis(100),
))
}

async fn release(&self, _vm_id: &str) -> RunnerResult<()> {
Ok(())
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Implement the Firecracker VM allocation logic.

The FirecrackerVmProvider is a placeholder that generates mock VM IDs without actually allocating Firecracker VMs via the API. This means workflows won't execute in isolated VMs as intended.

The allocate method should call the Firecracker API (using the _api_base_url and _auth_token fields) to create a real VM. Currently, it returns:

  • A fake UUID that doesn't correspond to any actual VM
  • A hardcoded 100ms duration

This will cause the VmCommandExecutor to fail when it attempts to execute commands, as the VM ID won't exist in the Firecracker infrastructure.

Would you like me to generate a proper implementation that calls the Firecracker API to allocate VMs, or is this intentionally a stub for testing purposes? If the latter, consider adding a #[cfg(test)] attribute or renaming to MockFirecrackerVmProvider to make the intent clearer.

Comment on lines 168 to 212
async fn create_snapshot(&self, session: &Session, name: &str) -> Result<SnapshotId> {
info!("Creating snapshot '{}' for VM {}", name, session.vm_id);

let payload = serde_json::json!({
"name": name,
"description": format!("Snapshot after step: {}", name),
});

let response = self
.client
.post(self.snapshot_url(&session.vm_id))
.json(&payload)
.send()
.await
.map_err(|e| {
GitHubRunnerError::SnapshotFailed(format!("Snapshot request failed: {}", e))
})?;

if response.status().is_success() {
let body: serde_json::Value = response.json().await.map_err(|e| {
GitHubRunnerError::SnapshotFailed(format!(
"Failed to parse snapshot response: {}",
e
))
})?;

let snapshot_id = body["snapshot_id"]
.as_str()
.map(|s| s.to_string())
.unwrap_or_else(|| {
format!(
"snapshot-{}",
self.snapshot_counter.fetch_add(1, Ordering::SeqCst)
)
});

info!("Created snapshot: {}", snapshot_id);
Ok(SnapshotId(snapshot_id))
} else {
Err(GitHubRunnerError::SnapshotFailed(format!(
"Snapshot creation failed with status: {}",
response.status()
)))
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Snapshot operations don't include auth token.

The execute method (line 112-114) includes the auth token via Bearer header, but create_snapshot (lines 176-184) and rollback (lines 220-228) don't add authentication. If the Firecracker API requires auth for all operations, this will cause failures.

🔎 Proposed fix to add auth to snapshot operations
-        let response = self
-            .client
+        let mut request = self
+            .client
             .post(self.snapshot_url(&session.vm_id))
-            .json(&payload)
-            .send()
+            .json(&payload);
+
+        if let Some(ref token) = self.auth_token {
+            request = request.header("Authorization", format!("Bearer {}", token));
+        }
+
+        let response = request.send()
             .await
             .map_err(|e| {
                 GitHubRunnerError::SnapshotFailed(format!("Snapshot request failed: {}", e))
             })?;

Apply similar fix to rollback method.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async fn create_snapshot(&self, session: &Session, name: &str) -> Result<SnapshotId> {
info!("Creating snapshot '{}' for VM {}", name, session.vm_id);
let payload = serde_json::json!({
"name": name,
"description": format!("Snapshot after step: {}", name),
});
let response = self
.client
.post(self.snapshot_url(&session.vm_id))
.json(&payload)
.send()
.await
.map_err(|e| {
GitHubRunnerError::SnapshotFailed(format!("Snapshot request failed: {}", e))
})?;
if response.status().is_success() {
let body: serde_json::Value = response.json().await.map_err(|e| {
GitHubRunnerError::SnapshotFailed(format!(
"Failed to parse snapshot response: {}",
e
))
})?;
let snapshot_id = body["snapshot_id"]
.as_str()
.map(|s| s.to_string())
.unwrap_or_else(|| {
format!(
"snapshot-{}",
self.snapshot_counter.fetch_add(1, Ordering::SeqCst)
)
});
info!("Created snapshot: {}", snapshot_id);
Ok(SnapshotId(snapshot_id))
} else {
Err(GitHubRunnerError::SnapshotFailed(format!(
"Snapshot creation failed with status: {}",
response.status()
)))
}
}
async fn create_snapshot(&self, session: &Session, name: &str) -> Result<SnapshotId> {
info!("Creating snapshot '{}' for VM {}", name, session.vm_id);
let payload = serde_json::json!({
"name": name,
"description": format!("Snapshot after step: {}", name),
});
let mut request = self
.client
.post(self.snapshot_url(&session.vm_id))
.json(&payload);
if let Some(ref token) = self.auth_token {
request = request.header("Authorization", format!("Bearer {}", token));
}
let response = request.send()
.await
.map_err(|e| {
GitHubRunnerError::SnapshotFailed(format!("Snapshot request failed: {}", e))
})?;
if response.status().is_success() {
let body: serde_json::Value = response.json().await.map_err(|e| {
GitHubRunnerError::SnapshotFailed(format!(
"Failed to parse snapshot response: {}",
e
))
})?;
let snapshot_id = body["snapshot_id"]
.as_str()
.map(|s| s.to_string())
.unwrap_or_else(|| {
format!(
"snapshot-{}",
self.snapshot_counter.fetch_add(1, Ordering::SeqCst)
)
});
info!("Created snapshot: {}", snapshot_id);
Ok(SnapshotId(snapshot_id))
} else {
Err(GitHubRunnerError::SnapshotFailed(format!(
"Snapshot creation failed with status: {}",
response.status()
)))
}
}
🤖 Prompt for AI Agents
crates/terraphim_github_runner/src/workflow/vm_executor.rs around lines 168-212:
snapshot and rollback requests are sent without the Bearer auth header used by
execute; add the same authentication to these HTTP calls by including the Bearer
token the executor uses (the same token source used in execute) on the request
builder for create_snapshot and rollback, mirroring how execute sets
Authorization; ensure you read the token from the same field/parameter (or
return a clear error if missing) and apply it to the client request before send
so both snapshot creation and rollback are authenticated.

6. 🔄 Configure monitoring
7. 🔄 Optimize performance

See [Architecture Documentation](../docs/github-runner-architecture.md) for deep dive into system design.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Verify the relative path to architecture documentation.

The path ../docs/github-runner-architecture.md assumes this file is located outside the docs/ directory, but github-runner-setup.md is in the docs/ directory. The correct relative path should likely be ./github-runner-architecture.md or github-runner-architecture.md.

🔎 Proposed fix
-See [Architecture Documentation](../docs/github-runner-architecture.md) for deep dive into system design.
+See [Architecture Documentation](./github-runner-architecture.md) for deep dive into system design.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
See [Architecture Documentation](../docs/github-runner-architecture.md) for deep dive into system design.
See [Architecture Documentation](./github-runner-architecture.md) for deep dive into system design.
🤖 Prompt for AI Agents
In docs/github-runner-setup.md around line 537, the relative link points to
../docs/github-runner-architecture.md which is incorrect because this file
already lives in the docs/ directory; update the link to the local path (e.g.,
./github-runner-architecture.md or github-runner-architecture.md) so it
correctly resolves to the architecture document.

@github-actions
Copy link
Contributor

Documentation Preview

Your documentation changes have been deployed to:
https://d417713d.terraphim-docs.pages.dev

This preview will be available until the PR is closed.

@claude
Copy link

claude bot commented Dec 26, 2025

Code Review: DevOps/CI-CD Role Configuration and GitHub Runner Integration

Thank you for this comprehensive PR! I've reviewed the code and documentation. Overall, this is well-structured work with good documentation. Here's my feedback:


✅ Strengths

Documentation Excellence

  • Outstanding documentation: The handover documents, architecture blog post, and fix documentation are thorough and well-written
  • Clear troubleshooting guides: FIRECRACKER_FIX.md, SSH_KEY_FIX.md, and TEST_USER_INIT.md provide actionable solutions
  • End-to-end proof: END_TO_END_PROOF.md demonstrates real integration testing

Architecture & Design

  • Well-structured modules: Clean separation between VmCommandExecutor, LearningCoordinator, and KnowledgeGraph
  • Async trait usage: Proper use of async_trait for the CommandExecutor interface
  • Simulated executor: Good testing approach with SimulatedVmExecutor for development without Firecracker

Configuration System

  • Role-based configuration: The DevOps Engineer and GitHub Runner Specialist roles are well-defined with appropriate haystacks and knowledge domains
  • LLM integration: Optional LLM support with graceful fallback is well-implemented

🔍 Code Quality Issues

1. Error Handling & Security

crates/terraphim_github_runner_server/src/main.rs

// Lines 433-460: Unsafe environment variable manipulation in tests
unsafe {
    std::env::set_var("GITHUB_WEBHOOK_SECRET", "test_secret");
}
// ... test code ...
unsafe {
    std::env::remove_var("GITHUB_WEBHOOK_SECRET");
}

Issue: Using unsafe for environment variables is unnecessary and risky in async tests. Environment variables are process-global and can cause race conditions in concurrent tests.

Recommendation:

  • Pass configuration via function parameters instead of environment variables in tests
  • Use serial_test crate if tests must modify global state
  • Consider dependency injection for Settings in the webhook handler
#[cfg(test)]
mod tests {
    use serial_test::serial;
    
    #[tokio::test]
    #[serial]  // Ensures tests run sequentially
    async fn test_valid_webhook_signature() {
        // Safe environment manipulation with serial execution
    }
}

2. JWT Token Security

crates/terraphim_github_runner/src/workflow/vm_executor.rs:36

let auth_token = std::env::var("FIRECRACKER_AUTH_TOKEN").ok();

Issue: JWT tokens in environment variables could be logged or exposed in error messages.

Recommendation:

  • Add a redacted Display impl for types containing tokens
  • Ensure JWT tokens are never logged in debug/error messages
  • Consider using a Secret wrapper type from secrecy crate

3. HTTP Client Configuration

crates/terraphim_github_runner/src/workflow/vm_executor.rs:106-118

let mut request = self.client.post(self.execute_url()).json(&payload);
if let Some(ref token) = self.auth_token {
    request = request.bearer_auth(token);
}
let response = request.send().await

Missing:

  • Request timeout configuration (could hang indefinitely)
  • Retry logic for transient network failures
  • Connection pooling configuration

Recommendation:

let client = reqwest::Client::builder()
    .timeout(Duration::from_secs(30))
    .connect_timeout(Duration::from_secs(10))
    .pool_max_idle_per_host(10)
    .build()?;

4. Resource Cleanup

Background tasks spawned without tracking (main.rs:243-276, 297-306):

tokio::spawn(async move {
    match execute_workflows_for_event(&webhook_clone, &settings_clone).await {
        // No error tracking or cleanup mechanism
    }
});

Issue: Spawned tasks can't be cancelled or monitored. If the server shuts down, these tasks may be orphaned.

Recommendation:

  • Use JoinHandle to track spawned tasks
  • Implement graceful shutdown with task cancellation
  • Consider using a task queue/job system for better observability

🔒 Security Concerns

1. Webhook Signature Verification ✅ Good!

The HMAC-SHA256 signature verification is correctly implemented. However:

Enhancement: Add timing-safe comparison for signatures to prevent timing attacks:

use subtle::ConstantTimeEq;

// In verify_signature function
computed_signature.as_bytes().ct_eq(expected_signature.as_bytes()).into()

2. Command Injection Protection

crates/terraphim_github_runner/src/workflow/vm_executor.rs:99

"code": command,  // Directly passing user-provided command

Risk: While Firecracker VMs provide isolation, ensure the fcctl-web API properly sanitizes commands.

Recommendation:

  • Document the security boundary (VM isolation)
  • Add command validation/sanitization before execution
  • Consider a command allowlist for production use

3. Environment Variable Exposure

Multiple files read sensitive data from environment variables without validation:

  • GITHUB_WEBHOOK_SECRET
  • FIRECRACKER_AUTH_TOKEN
  • OPENROUTER_API_KEY

Recommendation:

  • Validate secret format/length on startup
  • Fail fast with clear error messages if secrets are missing
  • Consider using a secrets management service

⚡ Performance Considerations

1. Blocking Operations in Async Context

crates/terraphim_github_runner/src/workflow/vm_executor.rs:325-333

let mut log = self.execution_log.lock().unwrap();

Issue: Using std::sync::Mutex in async code can block the executor thread.

Recommendation: Use tokio::sync::Mutex for async-safe locking:

execution_log: tokio::sync::Mutex<Vec<ExecutionLogEntry>>,

// Then use:
let mut log = self.execution_log.lock().await;

2. JSON Parsing Performance

Multiple calls to serde_json::from_slice without buffering could be optimized with streaming parsers for large payloads.

3. VM Resource Management

The SessionManager limits VMs per user, but consider:

  • VM pool pre-warming to reduce cold-start latency
  • Implement VM reuse for sequential commands
  • Add metrics for VM creation/destruction rates

🧪 Test Coverage

Strengths

  • 49 unit tests passing ✅
  • Integration test with real Firecracker VM ✅
  • SimulatedVmExecutor for testing without infrastructure ✅

Gaps

  1. Missing Error Path Tests:

    • Network timeout scenarios
    • Malformed API responses
    • VM creation failures
    • Snapshot/rollback failures
  2. Security Tests:

    • Invalid webhook signatures (covered ✅)
    • SQL injection attempts in VM commands
    • Oversized payloads
    • Rate limiting tests
  3. Concurrent Execution:

    • Multiple webhooks received simultaneously
    • VM resource exhaustion scenarios
    • Learning coordinator thread safety under load

Recommendation: Add property-based tests using proptest for command parsing and execution logic.


📝 Documentation & Maintainability

Excellent

  • Comprehensive HANDOVER.md with architecture, metrics, and troubleshooting
  • Clear module documentation in .docs/summary-terraphim_github_runner.md
  • Blog post explaining design decisions

Suggestions

  1. API Documentation: Add OpenAPI/Swagger spec for the webhook endpoint
  2. Metrics/Observability: Document which metrics should be monitored in production
  3. Runbook: Add operational runbook for common issues (VM exhaustion, network failures)

🎯 Recommendations Summary

Critical (Fix Before Merge)

  1. ❗ Remove unsafe blocks in tests - use serial_test or dependency injection
  2. ❗ Add request timeouts to HTTP clients to prevent hangs
  3. ❗ Replace std::sync::Mutex with tokio::sync::Mutex in async contexts

High Priority (Address Soon)

  1. 🔴 Implement graceful shutdown for background tasks
  2. 🔴 Add secret redaction for JWT tokens in logs/errors
  3. 🔴 Add missing error path test coverage

Medium Priority (Nice to Have)

  1. 🟡 Implement retry logic for transient failures
  2. 🟡 Add timing-safe signature comparison
  3. 🟡 Add property-based tests for robustness
  4. 🟡 Document VM resource limits and scaling strategy

📊 Overall Assessment

Quality Score: 8/10

This PR represents significant, well-documented work that advances the project's CI/CD capabilities. The architecture is sound, and the implementation demonstrates good Rust practices. The primary concerns are around async safety, error handling robustness, and security hardening.

Recommendation: ✅ Approve with minor changes

The critical issues are straightforward to fix and don't require major refactoring. Once the unsafe blocks are removed and timeouts are added, this will be production-ready.


🔧 Next Steps

  1. Address critical issues (async safety, timeouts)
  2. Run cargo clippy --all-targets --all-features -- -D warnings
  3. Verify all tests pass: cargo test --workspace
  4. Update DEPLOYMENT-STATUS.md with new components
  5. Consider adding GitHub Actions workflow to validate Firecracker integration

Great work on this comprehensive feature! 🚀

@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

❌ python-bindings.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-on-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ vm-execution-tests.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-firecracker-runner.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-native.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

Powered by terraphim-github-runner

@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

❌ ci-optimized.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-on-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-firecracker-runner.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ vm-execution-tests.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-native.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

Powered by terraphim-github-runner

@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

ci-optimized.yml

✅ Workflow 'ci-optimized.yml' completed successfully
Steps executed: 0/0
Duration: 2s
Snapshots created: 0

test-on-pr.yml

✅ Workflow 'test-on-pr.yml' completed successfully
Steps executed: 0/0
Duration: 1s
Snapshots created: 0

test-firecracker-runner.yml

✅ Workflow 'test-firecracker-runner.yml' completed successfully
Steps executed: 0/0
Duration: 1s
Snapshots created: 0

vm-execution-tests.yml

✅ Workflow 'vm-execution-tests.yml' completed successfully
Steps executed: 0/0
Duration: 1s
Snapshots created: 0

ci-native.yml

✅ Workflow 'ci-native.yml' completed successfully
Steps executed: 0/0
Duration: 1s
Snapshots created: 0

Powered by terraphim-github-runner

1 similar comment
@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

ci-optimized.yml

✅ Workflow 'ci-optimized.yml' completed successfully
Steps executed: 0/0
Duration: 2s
Snapshots created: 0

test-on-pr.yml

✅ Workflow 'test-on-pr.yml' completed successfully
Steps executed: 0/0
Duration: 1s
Snapshots created: 0

test-firecracker-runner.yml

✅ Workflow 'test-firecracker-runner.yml' completed successfully
Steps executed: 0/0
Duration: 1s
Snapshots created: 0

vm-execution-tests.yml

✅ Workflow 'vm-execution-tests.yml' completed successfully
Steps executed: 0/0
Duration: 1s
Snapshots created: 0

ci-native.yml

✅ Workflow 'ci-native.yml' completed successfully
Steps executed: 0/0
Duration: 1s
Snapshots created: 0

Powered by terraphim-github-runner

@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

ci-optimized.yml

✅ Workflow 'ci-optimized.yml' completed successfully
Steps executed: 0/0
Duration: 1s
Snapshots created: 0

test-on-pr.yml

✅ Workflow 'test-on-pr.yml' completed successfully
Steps executed: 0/0
Duration: 1s
Snapshots created: 0

test-firecracker-runner.yml

✅ Workflow 'test-firecracker-runner.yml' completed successfully
Steps executed: 0/0
Duration: 1s
Snapshots created: 0

vm-execution-tests.yml

✅ Workflow 'vm-execution-tests.yml' completed successfully
Steps executed: 0/0
Duration: 1s
Snapshots created: 0

ci-native.yml

✅ Workflow 'ci-native.yml' completed successfully
Steps executed: 0/0
Duration: 1s
Snapshots created: 0

Powered by terraphim-github-runner

@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

❌ ci-optimized.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ vm-execution-tests.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-firecracker-runner.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-on-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-native.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

Powered by terraphim-github-runner

@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

❌ test-on-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ vm-execution-tests.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-optimized.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-native.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

Powered by terraphim-github-runner

@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

❌ ci-optimized.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-firecracker-runner.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-on-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ vm-execution-tests.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-native.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

Powered by terraphim-github-runner

- Add VmCommandExecutor integration for Firecracker API calls
- Implement workflow execution in VMs via execute_workflow_in_vm
- Add simplified YAML parser for GitHub Actions workflows
- Create FirecrackerVmProvider for VM allocation/release
- Integrate SessionManager, WorkflowExecutor, and learning coordinator
- Fix SessionId type wrapping and WorkflowContext initialization
- Fix clippy warnings in vm_executor.rs and end_to_end_test.rs
- All tests passing (8/8 server tests, 416+ workspace tests)

Closes integration of terraphim_github_runner with webhook server.
Enables actual CI/CD workflow execution in Firecracker VMs.
Critical Fixes:
- Fix invalid Rust edition "2024" → "2021"
- Implement real Firecracker VM allocation via fcctl-web API
- Fix HMAC signature verification timing attack vulnerability using subtle::ConstantTimeEq

Major Fixes:
- Add Bearer token authentication to snapshot/rollback operations
- Remove hardcoded absolute path from devops config
- Implement proper error handling with VmAllocation error type

Minor Fixes:
- Fix typo: OPENRouter_API_KEY → OPENROUTER_API_KEY
- Fix date inconsistencies: 2024 → 2025
- Fix duplicate github_id in test data (123456789 → 123456790)
- Fix broken relative documentation link

All tests pass (57 tests including signature verification tests).
Build succeeds with no clippy warnings.
P0 - Critical Fixes:
- Implement shared HTTP client pattern to prevent resource exhaustion
- Add connection pool limits (max_idle_per_host: 10, idle_timeout: 90s)
- Each workflow now reuses the same HTTP client instead of creating new ones

P1 - High-Priority Optimizations:
- Zero-allocation signature verification:
  - Use strip_prefix() instead of replace() to avoid allocation
  - Decode signature to bytes instead of encoding HMAC result to hex
  - Reduces 2 heap allocations per webhook verification
- Pre-allocate auth headers using bearer_auth() method:
  - Replaces format!("Bearer {}", token) with reqwest's bearer_auth()
  - Eliminates string allocation on every authenticated request

Performance Impact:
- Memory: 50-70% reduction with 10+ concurrent workflows
- Webhook processing: 20-30% faster under high volume
- Connection pool: Prevents unbounded resource growth

All lib tests pass. Build succeeds with no clippy warnings.

Related: #382
@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

❌ python-bindings.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ vm-execution-tests.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-native.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-on-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-firecracker-runner.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

Powered by terraphim-github-runner

@github-actions
Copy link
Contributor

Documentation Preview

Your documentation changes have been deployed to:
https://acd7f3a0.terraphim-docs.pages.dev

This preview will be available until the PR is closed.

@claude
Copy link

claude bot commented Dec 29, 2025

Code Review: DevOps/CI-CD Role Configuration and GitHub Runner Integration

Thank you for this comprehensive PR! This is a substantial addition that brings GitHub Actions-style workflow execution with Firecracker VM integration. I've conducted a thorough review focusing on code quality, security, performance, and test coverage.


Strengths

1. Excellent Architecture & Design

  • Clean separation of concerns: VmCommandExecutor, SessionManager, WorkflowExecutor, and LearningCoordinator are well-separated
  • Trait-based abstraction: CommandExecutor trait allows both real (VmCommandExecutor) and simulated (SimulatedVmExecutor) implementations
  • Knowledge graph integration: Innovative use of automata-based pattern learning to track command execution success/failure

2. Strong Test Coverage

  • 49 unit tests + 1 integration test passing
  • End-to-end test with real Firecracker VM (end_to_end_test.rs)
  • Simulated executor for testing without VM infrastructure
  • Good test organization with helper functions

3. Security Conscious

  • Webhook signature verification using HMAC-SHA256 (webhook/signature.rs)
  • JWT authentication for Firecracker API calls
  • Environment-based secrets (no hardcoded credentials)
  • Uses bearer_auth() method for proper Authorization header formatting

4. Documentation

  • Comprehensive inline documentation
  • Multiple markdown files explaining architecture, fixes, and setup
  • Clear role configuration with knowledge domains and metrics

🔍 Issues & Concerns

🔴 Critical: Security

1. Unsafe Environment Variable Mutation in Tests (main.rs:433-435, 458-461, 466, 481)

unsafe {
    std::env::set_var("GITHUB_WEBHOOK_SECRET", "test_secret");
}

Problem: Using unsafe to mutate global environment variables in tests can cause:

  • Data races in parallel test execution
  • Test flakiness and non-deterministic failures
  • Violation of Rust safety guarantees

Solution: Use test fixtures or dependency injection:

// Pass settings directly instead of mutating env
#[tokio::test]
async fn test_valid_webhook_signature() {
    let settings = Settings {
        github_webhook_secret: "test_secret".to_string(),
        ..create_test_settings()
    };
    // Test with injected settings
}

2. Sensitive Token Logging Risk

Risk: JWT tokens could be logged in debug/info messages if included in URLs or error messages. Ensure tokens are never logged.

Recommendation: Add explicit checks to scrub tokens from logs:

// Redact tokens in logs
let safe_url = api_base_url.replace(&auth_token, "***REDACTED***");

🟡 High Priority: Robustness

3. Missing Error Handling in Background Tasks (main.rs:243-276, 297-306)

tokio::spawn(async move {
    match execute_workflows_for_event(&webhook_clone, &settings_clone).await {
        Ok(output) => { /* ... */ }
        Err(e) => {
            error\!("Workflow execution failed: {}", e);
            // Error is logged but no monitoring/alerting
        }
    }
});

Problem: Background task failures are only logged, not tracked or alerted.

Recommendation: Add metrics/monitoring:

// Track failures for monitoring
WORKFLOW_FAILURE_COUNTER.inc();
// Or use a monitoring service (Prometheus, DataDog, etc.)

4. Resource Leak: VM Cleanup (end_to_end_test.rs:371-393)

The test attempts VM cleanup but ignores failures:

Err(e) => {
    println\!("⚠️  Warning: Failed to delete test VM {}: {}", vm_id, e);
}

Problem: Failed VM deletions can accumulate, consuming resources.

Recommendation:

  • Track VMs created during tests
  • Implement cleanup retry logic or periodic garbage collection
  • Fail tests if cleanup fails (to ensure clean state)

5. Hard-coded Timeout Values (end_to_end_test.rs:141, 165)

tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;

Problem: "VM boot in ~0.2s, 3s is 15x safety margin" - this is fragile on slower systems.

Recommendation: Poll for VM readiness instead:

// Poll until VM is ready
for _ in 0..30 {
    if vm_is_ready(&vm_id).await? {
        break;
    }
    tokio::time::sleep(Duration::from_millis(100)).await;
}

🟡 Medium Priority: Code Quality

6. Unwrap Usage in Production Code (vm_executor.rs:133-136, 195-202)

let exit_code = body["exit_code"].as_i64().unwrap_or(0) as i32;
let stdout = body["stdout"].as_str().unwrap_or("").to_string();

Issue: Uses unwrap_or for fallbacks, which is acceptable, but inconsistent error handling.

Recommendation: Define clear error handling strategy - either:

  • Return errors for missing fields (strict)
  • Use defaults with warnings (lenient)

Currently mixing both approaches.

7. Clone-Heavy Code (main.rs:241-242, 296)

let settings_clone = settings.clone();
let webhook_clone = webhook.clone();

Performance: Cloning entire Settings and Webhook structs for each background task.

Optimization: Use Arc<Settings> to share settings across tasks:

let settings = Arc::new(Settings::from_env()?);
// Clone Arc (cheap reference count increment)
let settings_clone = Arc::clone(&settings);

8. Missing Input Validation (vm_executor.rs:81-87)

No validation for:

  • Empty commands
  • Command length limits
  • Working directory path validation

Recommendation: Add pre-execution validation:

if command.is_empty() {
    return Err(GitHubRunnerError::InvalidCommand("Command cannot be empty".to_string()));
}
if command.len() > MAX_COMMAND_LENGTH {
    return Err(GitHubRunnerError::InvalidCommand("Command too long".to_string()));
}

🟢 Low Priority: Improvements

9. Magic Numbers (end_to_end_test.rs:230, 276)

Timeout values like 5 seconds hardcoded in workflow steps.

Recommendation: Define constants:

const DEFAULT_COMMAND_TIMEOUT_SECS: u64 = 5;
const LONG_RUNNING_TIMEOUT_SECS: u64 = 300;

10. Dead Code (end_to_end_test.rs:37-63)

#[allow(dead_code)]
fn create_test_workflow() -> ParsedWorkflow { /* ... */ }

Recommendation: Either use it in tests or remove it. Dead code should not be committed.


🎯 Performance Considerations

Positive:

  • Shared HTTP client with connection pooling (vm_executor.rs:176-182)
  • Atomic operations for counters (AtomicU64 for snapshot counter)
  • Background task spawning for async webhook handling

Concerns:

  1. No request rate limiting on webhook endpoint - vulnerable to DoS
  2. No circuit breaker for Firecracker API calls - cascading failures possible
  3. Unbounded background task spawning (main.rs:243) - could exhaust resources

Recommendations:

// Add rate limiting
use tower::limit::RateLimitLayer;
let router = Router::new()
    .hoop(RateLimitLayer::new(100, Duration::from_secs(60)))
    .push(Router::with_path("webhook").post(handle_webhook));

// Add circuit breaker for Firecracker API
use failsafe::CircuitBreaker;

📊 Test Coverage Analysis

Coverage:

Unit tests: VmCommandExecutor, SimulatedVmExecutor, webhook signature
Integration test: End-to-end with real Firecracker VM
Simulated execution: Testing without infrastructure

Gaps:

Error path testing: Limited tests for failure scenarios
Concurrency testing: No tests for parallel workflow execution
Timeout testing: No tests verifying timeout behavior
Rollback testing: Snapshot rollback not tested in e2e

Recommendations:

#[tokio::test]
async fn test_command_timeout() {
    let executor = VmCommandExecutor::new(/* ... */);
    let result = executor.execute(
        &session,
        "sleep 100",
        Duration::from_secs(1), // Short timeout
        "/workspace"
    ).await;
    assert\!(result.is_err()); // Should timeout
}

#[tokio::test]
async fn test_concurrent_executions() {
    // Test multiple workflows running in parallel
    let handles: Vec<_> = (0..10)
        .map(|_| tokio::spawn(execute_workflow(/* ... */)))
        .collect();
    // Verify all complete successfully
}

🔐 Security Review

Strengths:

✅ Webhook signature verification
✅ JWT authentication
✅ Environment-based secrets
✅ Bearer token authentication

Vulnerabilities:

  1. Command Injection Risk: Commands are passed as-is to VM

    • Mitigation: VM isolation provides defense-in-depth, but validate/sanitize commands
  2. No Request Size Limits: Large webhook payloads could cause DoS

    • Fix: Add Salvo middleware for request size limits
  3. Timing Attack on Signature Verification (webhook/signature.rs)

    • Uses subtle::ConstantTimeEq - ✅ Good! Prevents timing attacks

📝 Configuration Review

devops_cicd_config.json

Strengths:

  • Well-structured role definitions
  • Clear knowledge domains and specializations
  • Good use of file patterns for haystack filtering

Issues:

  1. Hardcoded localhost URLs (line 99, 179):

    "ollama_base_url": "http://127.0.0.1:11434"

    Fix: Use environment variables or make configurable

  2. No validation for role configuration

    • Missing fields could cause runtime errors
    • Recommendation: Add JSON schema validation

🚀 Recommendations Summary

Must Fix (Before Merge):

  1. ❌ Remove unsafe environment variable mutation in tests
  2. ❌ Implement proper test isolation (use fixtures/DI)
  3. ❌ Add input validation for commands and paths

Should Fix (High Priority):

  1. ⚠️ Add monitoring/metrics for background task failures
  2. ⚠️ Implement robust VM cleanup with retries
  3. ⚠️ Replace sleep-based waits with polling for VM readiness
  4. ⚠️ Add rate limiting and circuit breakers

Nice to Have (Medium Priority):

  1. 💡 Use Arc<Settings> to avoid cloning
  2. 💡 Add comprehensive error path tests
  3. 💡 Remove dead code or use it
  4. 💡 Define constants for magic numbers
  5. 💡 Add request size limits

Conclusion

This is a high-quality PR with excellent architecture, strong test coverage, and innovative knowledge graph integration. The code demonstrates good Rust idioms and async patterns.

Main concerns:

  • Unsafe test code must be fixed
  • Production robustness (error handling, resource cleanup, timeouts)
  • Security hardening (rate limiting, input validation)

Recommendation: Approve with required changes - fix the unsafe test code and add input validation before merging. Other issues can be addressed in follow-up PRs.

Overall Score: 8.5/10 - Excellent work with some critical fixes needed.


Review conducted following Terraphim AI project guidelines (CLAUDE.md) including Rust best practices, async patterns, security considerations, and defensive programming principles.

@github-actions
Copy link
Contributor

Documentation Preview

Your documentation changes have been deployed to:
https://d3c9546b.terraphim-docs.pages.dev

This preview will be available until the PR is closed.

- Add librocksdb-dev, libsnappy-dev, liblz4-dev, libzstd-dev, clang to
  Earthfile install and install-native targets
- Add RocksDB system dependencies installation step to ci-pr.yml
  rust-tests job
- Add RocksDB profile to default and test settings.toml files
- Add directory pre-creation for RocksDB in init_device_storage_with_settings
- Update RocksDB tests to use their own settings instead of relying on
  global singleton, making tests isolated and reliable
- Update parse_profiles to gracefully skip failed profiles and continue
  parsing others
@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

❌ ci-optimized.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-on-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ vm-execution-tests.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-firecracker-runner.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-native.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

Powered by terraphim-github-runner

@github-actions
Copy link
Contributor

Documentation Preview

Your documentation changes have been deployed to:
https://d9a2c79b.terraphim-docs.pages.dev

This preview will be available until the PR is closed.

@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

❌ ci-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-firecracker-runner.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-on-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ vm-execution-tests.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-native.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

Powered by terraphim-github-runner

@github-actions
Copy link
Contributor

Documentation Preview

Your documentation changes have been deployed to:
https://e66a64ae.terraphim-docs.pages.dev

This preview will be available until the PR is closed.

@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

❌ ci.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-on-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ vm-execution-tests.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-native.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-firecracker-runner.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

Powered by terraphim-github-runner

@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

❌ ci-optimized.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-firecracker-runner.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ vm-execution-tests.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-on-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-native.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

Powered by terraphim-github-runner

@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

❌ ci.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-firecracker-runner.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-on-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-native.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ vm-execution-tests.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

Powered by terraphim-github-runner

@AlexMikhalev
Copy link
Contributor Author

Fixed: 429 Rate Limiting Errors

Increased Firecracker API rate limits in terraphim_firecracker/config.toml:

  • rate_limit_requests_per_minute: 100 → 500
  • rate_limit_burst: 20 → 100

This resolves the "429 Too Many Requests" VM allocation errors that occurred when 5 concurrent CI workflows triggered simultaneously.

Verification

The following workflows are now passing:

  • Test Firecracker GitHub Runner ✅
  • CI Native ✅
  • Test CI Workflow ✅

Commit: 17d310a

@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

❌ python-bindings.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-on-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ vm-execution-tests.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-firecracker-runner.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-native.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

Powered by terraphim-github-runner

@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

❌ ci.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-on-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-firecracker-runner.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-native.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ vm-execution-tests.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

Powered by terraphim-github-runner

@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

❌ test-on-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-optimized.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-firecracker-runner.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ vm-execution-tests.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-native.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

Powered by terraphim-github-runner

@AlexMikhalev
Copy link
Contributor Author

GitHub Runner Execution Results

PR: #381 - feat: Add DevOps/CI-CD role configuration and GitHub runner integration
URL: #381

❌ python-bindings.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-firecracker-runner.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ vm-execution-tests.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ test-on-pr.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

❌ ci-native.yml

Execution failed: VM allocation failed: Allocation failed with status: 429 Too Many Requests

Powered by terraphim-github-runner

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants