feat(hub): add Strix Hub — multi-tenant task orchestration & dual-channel model routing console - #1170
feat(hub): add Strix Hub — multi-tenant task orchestration & dual-channel model routing console#1170Genesiu wants to merge 9 commits into
Conversation
…el model routing console
Greptile SummaryStrix Hub adds a network-accessible multi-tenant console, SQLite-backed task and user state, process lifecycle controls, and per-task dual-channel model routing. The implementation currently contains several blocking launch, command-execution, credential-handling, task-attribution, and lifecycle defects.
Confidence Score: 0/5This PR is not safe to merge until the command injection, known administrator credential, broken CLI and presets paths, credential logging, and task-state correctness failures are fixed. Authenticated task input reaches a shell interpreter, fresh network deployments expose a universal administrator login, provider keys enter logs, the advertised CLI cannot parse its invocation, and concurrent or stopped tasks can persist incorrect results and status. Files Needing Attention: strix/interface/main.py, strix_hub/db.py, strix_hub/server.py, strix_hub/task_manager.py, strix_hub/model_router.py
|
| Filename | Overview |
|---|---|
| strix/interface/main.py | Adds Hub dispatch, but leaves the consumed subcommand in sys.argv, preventing the documented CLI invocation from starting. |
| strix_hub/db.py | Adds persistence and RBAC data operations but automatically provisions a known administrator password. |
| strix_hub/server.py | Adds the REST API and SPA, but the presets endpoint raises on undefined configuration names. |
| strix_hub/task_manager.py | Adds process orchestration but introduces shell-command injection, cross-task artifact attribution, and incorrect stopped-task reconciliation. |
| strix_hub/model_router.py | Adds dual-channel proxying and tool-call recovery but logs raw model-provider credentials during hot reload. |
| strix_hub/main.py | Defines standalone Hub argument parsing and defaults to a network-wide binding, amplifying the impact of seeded credentials. |
Prompt To Fix All With AI
### Issue 1
strix/interface/main.py:434-438
**Hub subcommand remains unconsumed**
When `strix hub --port 8888` is invoked, `run_hub()` parses the unchanged `sys.argv`, so argparse treats `hub` as an unknown argument and exits instead of starting the server.
### Issue 2
strix_hub/task_manager.py:103-106
**Task fields reach shell**
When an authenticated user supplies shell syntax in `target`, `scan_mode`, or `instruction`, these values are interpolated into a string executed with `shell=True`, causing arbitrary commands to run with the Hub process's privileges.
**How this was verified:** The request fields flow from `POST /api/tasks` through persistence into the shell-interpreted command without shell-safe argument separation.
### Issue 3
strix_hub/db.py:115-126
**Known administrator credential seeded**
On a fresh installation, the service binds to all interfaces and provisions the publicly documented `admin` / `admin123` account, allowing a network peer to authenticate as administrator and control every tenant's tasks, logs, users, and model configuration.
**How this was verified:** Database initialization creates the literal credential while the default server configuration exposes the login service on `0.0.0.0`.
### Issue 4
strix_hub/server.py:184-186
**Preset variables are undefined**
When the SPA requests `/api/models/presets`, the handler reads three undefined `LOCAL_QWEN38_*` names, raising `NameError` and preventing presets and local model defaults from loading.
```suggestion
"model": LOCAL_LLM_MODEL,
"url": LOCAL_LLM_URL,
"key": LOCAL_LLM_KEY,
```
### Issue 5
strix_hub/model_router.py:60
**Router logs raw credentials**
When model routing configuration is hot-reloaded, the info-level log serializes the complete configuration dictionary, exposing raw root and subagent API keys to application logs and configured log collectors.
**How this was verified:** The logged dictionary directly contains the `root_api_key` and `subagent_api_key` values.
```suggestion
logger.info("ModelRouter config hot-updated")
```
### Issue 6
strix_hub/task_manager.py:303-329
**Artifacts cross task boundaries**
If multiple tasks run concurrently, each monitor selects the globally newest Strix run directory without associating it with its own task, causing one tenant's task to display another scan's run directory and vulnerability count.
### Issue 7
strix_hub/task_manager.py:283-286
**Stopped tasks become failed**
When an operator terminates a task, `stop_task` records `stopped`, but the monitor subsequently maps the signal exit code to `failed` and overwrites that state, causing intentional terminations to appear as failures.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "feat(hub): add Strix Hub multi-tenant ta..." | Re-trigger Greptile
| # `strix hub …` launches the multi-tenant task orchestration and dual-channel router console. | ||
| if len(sys.argv) > 1 and sys.argv[1] == "hub": | ||
| from strix_hub.main import main as run_hub | ||
|
|
||
| run_hub() |
There was a problem hiding this comment.
Hub subcommand remains unconsumed
When strix hub --port 8888 is invoked, run_hub() parses the unchanged sys.argv, so argparse treats hub as an unknown argument and exits instead of starting the server.
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/interface/main.py
Line: 434-438
Comment:
**Hub subcommand remains unconsumed**
When `strix hub --port 8888` is invoked, `run_hub()` parses the unchanged `sys.argv`, so argparse treats `hub` as an unknown argument and exits instead of starting the server.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| cmd = f"{strix_bin} -n --target {target} --scan-mode {scan_mode}" | ||
| if instruction: | ||
| clean_inst = instruction.replace('"', '\\"') | ||
| cmd += f' --instruction "{clean_inst}"' |
There was a problem hiding this comment.
When an authenticated user supplies shell syntax in target, scan_mode, or instruction, these values are interpolated into a string executed with shell=True, causing arbitrary commands to run with the Hub process's privileges.
How this was verified: The request fields flow from POST /api/tasks through persistence into the shell-interpreted command without shell-safe argument separation.
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix_hub/task_manager.py
Line: 103-106
Comment:
**Task fields reach shell**
When an authenticated user supplies shell syntax in `target`, `scan_mode`, or `instruction`, these values are interpolated into a string executed with `shell=True`, causing arbitrary commands to run with the Hub process's privileges.
**How this was verified:** The request fields flow from `POST /api/tasks` through persistence into the shell-interpreted command without shell-safe argument separation.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| def ensure_admin_user() -> None: | ||
| with get_connection() as conn: | ||
| cursor = conn.cursor() | ||
| cursor.execute("SELECT id FROM users WHERE role = 'admin' LIMIT 1") | ||
| if cursor.fetchone() is None: | ||
| admin_id = f"user_{secrets.token_hex(6)}" | ||
| p_hash, salt = hash_password("admin123") | ||
| cursor.execute( | ||
| "INSERT INTO users (id, username, password_hash, salt, role, created_at) VALUES (?, ?, ?, ?, ?, ?)", | ||
| (admin_id, "admin", p_hash, salt, "admin", int(time.time())), | ||
| ) | ||
| conn.commit() |
There was a problem hiding this comment.
Known administrator credential seeded
On a fresh installation, the service binds to all interfaces and provisions the publicly documented admin / admin123 account, allowing a network peer to authenticate as administrator and control every tenant's tasks, logs, users, and model configuration.
How this was verified: Database initialization creates the literal credential while the default server configuration exposes the login service on 0.0.0.0.
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix_hub/db.py
Line: 115-126
Comment:
**Known administrator credential seeded**
On a fresh installation, the service binds to all interfaces and provisions the publicly documented `admin` / `admin123` account, allowing a network peer to authenticate as administrator and control every tenant's tasks, logs, users, and model configuration.
**How this was verified:** Database initialization creates the literal credential while the default server configuration exposes the login service on `0.0.0.0`.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| "model": LOCAL_QWEN38_MODEL, | ||
| "url": LOCAL_QWEN38_URL, | ||
| "key": LOCAL_QWEN38_KEY, |
There was a problem hiding this comment.
Preset variables are undefined
When the SPA requests /api/models/presets, the handler reads three undefined LOCAL_QWEN38_* names, raising NameError and preventing presets and local model defaults from loading.
| "model": LOCAL_QWEN38_MODEL, | |
| "url": LOCAL_QWEN38_URL, | |
| "key": LOCAL_QWEN38_KEY, | |
| "model": LOCAL_LLM_MODEL, | |
| "url": LOCAL_LLM_URL, | |
| "key": LOCAL_LLM_KEY, |
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix_hub/server.py
Line: 184-186
Comment:
**Preset variables are undefined**
When the SPA requests `/api/models/presets`, the handler reads three undefined `LOCAL_QWEN38_*` names, raising `NameError` and preventing presets and local model defaults from loading.
```suggestion
"model": LOCAL_LLM_MODEL,
"url": LOCAL_LLM_URL,
"key": LOCAL_LLM_KEY,
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| self.config[k] = v.rstrip("/") | ||
| else: | ||
| self.config[k] = v | ||
| logger.info("ModelRouter config hot-updated: %s", self.config) |
There was a problem hiding this comment.
When model routing configuration is hot-reloaded, the info-level log serializes the complete configuration dictionary, exposing raw root and subagent API keys to application logs and configured log collectors.
How this was verified: The logged dictionary directly contains the root_api_key and subagent_api_key values.
| logger.info("ModelRouter config hot-updated: %s", self.config) | |
| logger.info("ModelRouter config hot-updated") |
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix_hub/model_router.py
Line: 60
Comment:
**Router logs raw credentials**
When model routing configuration is hot-reloaded, the info-level log serializes the complete configuration dictionary, exposing raw root and subagent API keys to application logs and configured log collectors.
**How this was verified:** The logged dictionary directly contains the `root_api_key` and `subagent_api_key` values.
```suggestion
logger.info("ModelRouter config hot-updated")
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| def _inspect_strix_runs_dir(task_id: str) -> tuple[str | None, int]: | ||
| """Inspect newest run artifacts to find linked run_dir and vulnerability counts.""" | ||
| if not STRIX_RUNS_DIR.is_dir(): | ||
| return None, 0 | ||
|
|
||
| runs = sorted( | ||
| [d for d in STRIX_RUNS_DIR.iterdir() if d.is_dir() and not d.name.startswith(".")], | ||
| key=lambda p: p.stat().st_mtime, | ||
| reverse=True, | ||
| ) | ||
|
|
||
| if not runs: | ||
| return None, 0 | ||
|
|
||
| latest = runs[0] | ||
| vuln_count = 0 | ||
| vulns_file = latest / "vulnerabilities.json" | ||
| if vulns_file.is_file(): | ||
| try: | ||
| with open(vulns_file, "r", encoding="utf-8") as f: | ||
| vulns = json.load(f) | ||
| if isinstance(vulns, list): | ||
| vuln_count = len(vulns) | ||
| except Exception: | ||
| pass | ||
|
|
||
| return latest.name, vuln_count |
There was a problem hiding this comment.
Artifacts cross task boundaries
If multiple tasks run concurrently, each monitor selects the globally newest Strix run directory without associating it with its own task, causing one tenant's task to display another scan's run directory and vulnerability count.
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix_hub/task_manager.py
Line: 303-329
Comment:
**Artifacts cross task boundaries**
If multiple tasks run concurrently, each monitor selects the globally newest Strix run directory without associating it with its own task, causing one tenant's task to display another scan's run directory and vulnerability count.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| run_dir_name, vulns_cnt = _inspect_strix_runs_dir(task_id) | ||
|
|
||
| final_status = "completed" if exit_code in [0, 2] else "failed" | ||
| db.update_task_status( |
There was a problem hiding this comment.
When an operator terminates a task, stop_task records stopped, but the monitor subsequently maps the signal exit code to failed and overwrites that state, causing intentional terminations to appear as failures.
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix_hub/task_manager.py
Line: 283-286
Comment:
**Stopped tasks become failed**
When an operator terminates a task, `stop_task` records `stopped`, but the monitor subsequently maps the signal exit code to `failed` and overwrites that state, causing intentional terminations to appear as failures.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.…h professional PDF printing and HTML export
Summary
This PR introduces Strix Hub (
strix hub), a zero-intrusion, multi-tenant task orchestration and dual-channel model routing console for Strix.Motivation
While Strix has an excellent headless CLI and read-only viewer (
strix view), security teams often require:SIGSTOPwith 0 CPU & 0 token cost), resume (SIGCONT), and termination without needing direct SSH/terminal access.tool_callsfor maximum compatibility with open-weights LLMs.Key Architectural Highlights
http.server,sqlite3,subprocess,threading) and a single-file modern Dark Mode React SPA.strix hub [--port 8888]or as a standalone modulepython -m strix_hub.main.Usage
# Launch Strix Hub Web Management on port 8888 strix hub --port 8888Open
http://localhost:8888in browser.admin/admin123