Files
corrosion-admin-panel/docs/COMPANION_AGENT_MODULE_INSTALL.md
Vantz Stockwell 6c2436dfc6
All checks were successful
Test Asgard Runner / test (push) Successful in 2s
feat: Phase 4 module auto-installation + Phase 5 webstore backend
Phase 4 Contributions (Agent Golf):
- Module auto-installation service (module_installer.rs)
- NATS subject pattern for module installation commands
- Companion agent contract documentation
- API endpoint: POST /api/modules/install

Phase 5 XO Direct Touch:
- Webstore subscription API (PayPal recurring billing)
  * POST /api/webstore/subscription/create
  * GET /api/webstore/subscription
  * POST /api/webstore/subscription/cancel
  * POST /api/webstore/subscription/webhook
- Store configuration API (CRUD for store settings)
  * GET /api/webstore/config
  * PUT /api/webstore/config
- Store category/item management APIs (multi-tenant CRUD)
  * GET/POST/PUT/DELETE /api/webstore/categories
  * GET/POST/PUT/DELETE /api/webstore/items
- Public store API (customer-facing, subdomain-scoped)
  * GET /api/public-store/:subdomain
  * GET /api/public-store/:subdomain/items
  * POST /api/public-store/:subdomain/purchase
  * POST /api/public-store/:subdomain/webhook
- Transaction history API
  * GET /api/webstore/transactions
- Delivery system (NATS command execution on purchase)
- Migrations: payment_orders, webstore_subscriptions, store_config, store_items, store_transactions

Security:
- JWT auth + license_id scoping on admin endpoints
- Subdomain → license_id mapping on public endpoints
- Purchase limit enforcement
- Command injection prevention via placeholder replacement

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-15 14:53:53 -05:00

6.8 KiB

Companion Agent Module Installation Contract

Status: Phase 4 — Module Auto-Installation Pipeline Date: 2026-02-15 Author: Sonnet (XO)

Overview

The companion agent (bare metal server management) must implement module installation support to enable automated plugin deployment from the Corrosion admin panel.

This document defines the NATS subject contract for module installation commands and responses.


NATS Subject Pattern

Command Subject

corrosion.{license_id}.cmd.module.install

Response Subject

corrosion.{license_id}.module.install.result

Request Payload

The backend publishes a JSON payload to the command subject when a module installation is triggered:

{
  "module_id": "loot-manager",
  "download_url": "https://cdn.corrosionmgmt.com/modules/LootManager.cs",
  "filename": "LootManager.cs",
  "target_path": "oxide/plugins/"
}

Fields

Field Type Required Description
module_id string Yes Module slug identifier (e.g., "loot-manager")
download_url string Yes Signed URL to download the plugin file (.cs file)
filename string Yes Filename to save the plugin as (e.g., "LootManager.cs")
target_path string Yes Relative path from server root to install location (e.g., "oxide/plugins/")

Expected Agent Behavior

  1. Download Plugin File

    • Make HTTP GET request to download_url
    • Validate response is successful (2xx status)
    • Read file contents into memory
  2. Install Plugin

    • Navigate to {server_root}/{target_path}
    • Write file contents to {target_path}/{filename}
    • Ensure file permissions allow server process to read it
  3. Reload Plugins

    • Execute console command: oxide.reload *
    • Wait for plugin to load
    • Verify plugin appears in loaded plugin list
  4. Publish Result

    • Publish success/failure result to corrosion.{license_id}.module.install.result
    • Include error details if installation failed

Response Payload

The agent must publish a JSON response to the result subject after installation completes (or fails):

Success Response

{
  "module_id": "loot-manager",
  "success": true,
  "error": null
}

Failure Response

{
  "module_id": "loot-manager",
  "success": false,
  "error": "Failed to download plugin file: connection timeout"
}

Fields

Field Type Required Description
module_id string Yes Echo of the module_id from the request
success boolean Yes true if installation succeeded, false if it failed
error string No Human-readable error message (required if success=false)

Error Handling

The agent should report failure for any of these conditions:

  • Download Failure: HTTP request fails or returns non-2xx status
  • File Write Failure: Unable to write plugin file to disk (permissions, disk full, path doesn't exist)
  • Plugin Load Failure: Plugin file saved but failed to load when oxide.reload * was executed
  • Timeout: Operation takes longer than 60 seconds

The backend will timeout after 60 seconds if no response is received and mark the installation as "failed" in the database.


Implementation Notes

  • The backend uses NATS request/reply pattern with a 60-second timeout
  • Plugin files are small (<1MB typically), so download should be fast
  • The agent should verify oxide/plugins/ directory exists before attempting write
  • If the plugin already exists, overwrite it (this is an update/reinstall scenario)
  • The agent does NOT need to check if the module is purchased — backend already verified this

Example Implementation (Pseudocode)

func HandleModuleInstall(msg *nats.Msg) {
    var cmd ModuleInstallCommand
    json.Unmarshal(msg.Data, &cmd)

    // Download plugin file
    resp, err := http.Get(cmd.DownloadURL)
    if err != nil {
        publishResult(msg.Reply, cmd.ModuleID, false, err.Error())
        return
    }
    defer resp.Body.Close()

    if resp.StatusCode != 200 {
        publishResult(msg.Reply, cmd.ModuleID, false, "Download failed: " + resp.Status)
        return
    }

    pluginData, _ := ioutil.ReadAll(resp.Body)

    // Write to disk
    targetFile := filepath.Join(serverRoot, cmd.TargetPath, cmd.Filename)
    err = ioutil.WriteFile(targetFile, pluginData, 0644)
    if err != nil {
        publishResult(msg.Reply, cmd.ModuleID, false, "File write failed: " + err.Error())
        return
    }

    // Reload plugins
    err = sendConsoleCommand("oxide.reload *")
    if err != nil {
        publishResult(msg.Reply, cmd.ModuleID, false, "Plugin reload failed: " + err.Error())
        return
    }

    // Success
    publishResult(msg.Reply, cmd.ModuleID, true, "")
}

func publishResult(subject, moduleID string, success bool, errorMsg string) {
    result := ModuleInstallResult{
        ModuleID: moduleID,
        Success:  success,
        Error:    errorMsg,
    }
    data, _ := json.Marshal(result)
    natsClient.Publish(subject, data)
}

Testing

Manual Test Procedure

  1. Purchase a module from the Corrosion dashboard (e.g., Loot Manager)
  2. Click "Install" button on the module card
  3. Monitor NATS subject: corrosion.{your_license_id}.cmd.module.install
  4. Verify agent receives command payload
  5. Verify agent downloads plugin file
  6. Verify agent writes file to oxide/plugins/LootManager.cs
  7. Verify agent executes oxide.reload *
  8. Verify agent publishes success result
  9. Refresh dashboard — module status should show "installed"

Failure Scenarios to Test

  • Invalid download URL (404) → agent reports "Download failed"
  • Disk full → agent reports "File write failed"
  • Plugin syntax error → agent reports "Plugin load failed"
  • No response from agent → backend times out after 60s, marks "failed"

  • Backend Service: backend/src/services/module_installer.rs
  • API Endpoint: backend/src/api/modules.rs (POST /api/modules/install)
  • Database Schema: backend/migrations/009_module_licensing.sql
  • Companion Agent Repo: TBD (Go implementation)

Status

  • Backend service implemented (ModuleInstaller)
  • API endpoint wired (POST /api/modules/install)
  • NATS contract documented
  • Companion agent implementation (Go)
  • End-to-end testing

Next Steps: Implement this contract in the companion agent codebase (Go).