[GEMINI-HARDEN-01] Replace hard-coded fleet inventory with repo-native config
Some checks failed
Smoke Test / smoke (pull_request) Failing after 23s
Architecture Lint / Linter Tests (pull_request) Successful in 26s
Validate Config / YAML Lint (pull_request) Failing after 15s
Validate Config / JSON Validate (pull_request) Successful in 19s
Validate Config / Python Syntax & Import Check (pull_request) Failing after 1m1s
Validate Config / Python Test Suite (pull_request) Has been skipped
Validate Config / Shell Script Lint (pull_request) Failing after 1m4s
Validate Config / Cron Syntax Check (pull_request) Successful in 13s
Validate Config / Deploy Script Dry Run (pull_request) Successful in 13s
Validate Config / Playbook Schema Validation (pull_request) Successful in 25s
Architecture Lint / Lint Repository (pull_request) Failing after 22s
PR Checklist / pr-checklist (pull_request) Successful in 5m0s

Add fleet.inventory and fleet.path_contracts to config.yaml:
- Central source of truth for IPs, ports, roles, remote paths
- Introduce get_config_path(), load_fleet_inventory(), get_path_contract()
- Updated fleet_llama.py, self_healing.py, telemetry.py, agent_dispatch.py,
  skill_installer.py to read from config instead of hard-coded dicts/paths
- Documented inventory contract and override mechanism in scripts/README.md

Scripts retain forward-compatible fallback defaults for backwards compatibility.

Closes #433
This commit is contained in:
Alexander Payne
2026-04-26 22:47:59 -04:00
parent 34a1e68e67
commit ab9d1c0fa4
7 changed files with 267 additions and 29 deletions

View File

@@ -56,5 +56,61 @@ python3 scripts/fleet_llama.py status
python3 scripts/architecture_linter_v2.py
```
## Fleet Inventory Contract
The fleet inventory is defined in `timmy-config/config.yaml` under the `fleet:` key. All [OPS] scripts read this data at runtime, eliminating hard-coded IPs and paths.
### `fleet.inventory` — Per-Host Definition
```yaml
fleet:
inventory:
<hostname>:
ip: <string> # Public or private IP address
port: <int> # SSH target port (typically 22)
role: <string> # Logical role (hub, forge, agent-host, world-host)
remote_root: <path> # Remote root directory for Hermes operations
capabilities: [...] # Feature tags the host supports
```
Each host entry exposes: `ip`, `port`, `role`, `remote_root`, `capabilities`. The `capabilities` tag is freeform but standardized across the fleet (e.g., `gateway`, `orchestrator`, `forge`, `agent-host`, `llm-host`, `world-host`).
### `fleet.path_contracts` — Path Abstractions
```yaml
fleet:
path_contracts:
hermes_agent_local: ../hermes-agent # Path to local hermes-agent repo (relative to timmy-config)
hermes_remote: /opt/hermes # Remote Hermes root on fleet nodes
skills_remote: /opt/hermes/skills # Remote skills directory
```
All scripts reference paths via `get_path_contract(key, default)` or `get_remote_root()` helpers. This centralizes path management across local (mac) and remote wizards.
### Override Mechanism
Set the `TIMMY_CONFIG` environment variable to point at an alternate `config.yaml`:
```bash
export TIMMY_CONFIG=/path/to/alternate/config.yaml
python3 scripts/fleet_llama.py status
```
Without `TIMMY_CONFIG`, scripts auto-resolve `timmy-config/config.yaml` relative to their `scripts/` directory.
### Fallback Defaults
If `config.yaml` is missing or the `fleet:` section is absent, scripts fall back to the canonical production fleet:
| Hostname | IP | Role |
|----------|-------------------|---------------|
| mac | 10.1.10.77 | hub |
| ezra | 143.198.27.163 | forge |
| allegro | 167.99.126.228 | agent-host |
| bezalel | 159.203.146.185 | world-host |
Fleet eviction occurs through config changes, not code edits.
---
*Built by Gemini — The Builder, The Systematizer, The Force Multiplier.*

View File

@@ -15,12 +15,46 @@ if SCRIPT_DIR not in sys.path:
sys.path.insert(0, SCRIPT_DIR)
from ssh_trust import VerifiedSSHExecutor
import yaml
# --- CONFIGURATION ---
FLEET = {
"allegro": "167.99.126.228",
"bezalel": "159.203.146.185"
}
def get_config_path():
return os.environ.get('TIMMY_CONFIG') or os.path.join(
os.path.dirname(os.path.abspath(__file__)), '..', 'config.yaml'
)
def load_fleet_inventory():
"""Return {{host: ip}} map from config.yaml or fallback defaults."""
try:
with open(get_config_path(), 'r') as f:
cfg = yaml.safe_load(f)
inv = cfg.get('fleet', {}).get('inventory', {})
if inv:
return {k: v['ip'] for k, v in inv.items()}
except Exception:
pass
return {
"mac": "10.1.10.77",
"ezra": "143.198.27.163",
"allegro": "167.99.126.228",
"bezalel": "159.203.146.185",
}
FLEET = load_fleet_inventory()
def get_path_contract(key, default):
import yaml, os
config_path = get_config_path()
try:
with open(config_path, 'r') as f:
cfg = yaml.safe_load(f)
return cfg.get('fleet', {}).get('path_contracts', {}).get(key, default)
except Exception:
return default
REMOTE_ROOT = get_path_contract('hermes_remote', '/opt/hermes')
class Dispatcher:
def __init__(self, executor=None):
@@ -38,7 +72,7 @@ class Dispatcher:
res = self.executor.run(
ip,
['python3', 'run_agent.py', '--agent', agent_name, '--task', task],
cwd='/opt/hermes',
cwd=REMOTE_ROOT,
timeout=30,
)
if res.returncode == 0:

View File

@@ -19,14 +19,42 @@ if SCRIPT_DIR not in sys.path:
sys.path.insert(0, SCRIPT_DIR)
from ssh_trust import VerifiedSSHExecutor
import yaml
# --- FLEET DEFINITION ---
FLEET = {
"mac": {"ip": "10.1.10.77", "port": 8080, "role": "hub"},
"ezra": {"ip": "143.198.27.163", "port": 8080, "role": "forge"},
"allegro": {"ip": "167.99.126.228", "port": 8080, "role": "agent-host"},
"bezalel": {"ip": "159.203.146.185", "port": 8080, "role": "world-host"}
}
def get_config_path():
return os.environ.get('TIMMY_CONFIG') or os.path.join(
os.path.dirname(os.path.abspath(__file__)), '..', 'config.yaml'
)
def load_fleet_inventory():
"""Return dict {{host: {{ip, port, role, remote_root, capabilities}}}} from config.yaml,
or approved fallback defaults."""
try:
with open(get_config_path(), 'r') as f:
cfg = yaml.safe_load(f)
inv = cfg.get('fleet', {}).get('inventory', {})
if inv:
return inv
except Exception:
pass
return {
"mac": {"ip": "10.1.10.77", "port": 8080, "role": "hub"},
"ezra": {"ip": "143.198.27.163", "port": 8080, "role": "forge"},
"allegro": {"ip": "167.99.126.228", "port": 8080, "role": "agent-host"},
"bezalel": {"ip": "159.203.146.185", "port": 8080, "role": "world-host"},
}
def get_path_contract(key, default):
"""Return path contract from config.yaml."""
try:
with open(get_config_path(), 'r') as f:
cfg = yaml.safe_load(f)
return cfg.get('fleet', {}).get('path_contracts', {}).get(key, default)
except Exception:
return default
FLEET = load_fleet_inventory() # dict {host: {{"ip":..,"port":..,"role":..}}}
class FleetManager:
def __init__(self, executor=None):

View File

@@ -20,14 +20,42 @@ if SCRIPT_DIR not in sys.path:
sys.path.insert(0, SCRIPT_DIR)
from ssh_trust import VerifiedSSHExecutor
import yaml
# --- CONFIGURATION ---
FLEET = {
"mac": {"ip": "10.1.10.77", "port": 8080},
"ezra": {"ip": "143.198.27.163", "port": 8080},
"allegro": {"ip": "167.99.126.228", "port": 8080},
"bezalel": {"ip": "159.203.146.185", "port": 8080}
}
def get_config_path():
return os.environ.get('TIMMY_CONFIG') or os.path.join(
os.path.dirname(os.path.abspath(__file__)), '..', 'config.yaml'
)
def load_fleet_inventory():
"""Return dict {{host: {{ip, port, role, remote_root, capabilities}}}} from config.yaml,
or approved fallback defaults."""
try:
with open(get_config_path(), 'r') as f:
cfg = yaml.safe_load(f)
inv = cfg.get('fleet', {}).get('inventory', {})
if inv:
return inv
except Exception:
pass
return {
"mac": {"ip": "10.1.10.77", "port": 8080, "role": "hub"},
"ezra": {"ip": "143.198.27.163", "port": 8080, "role": "forge"},
"allegro": {"ip": "167.99.126.228", "port": 8080, "role": "agent-host"},
"bezalel": {"ip": "159.203.146.185", "port": 8080, "role": "world-host"},
}
def get_path_contract(key, default):
"""Return path contract from config.yaml."""
try:
with open(get_config_path(), 'r') as f:
cfg = yaml.safe_load(f)
return cfg.get('fleet', {}).get('path_contracts', {}).get(key, default)
except Exception:
return default
FLEET = load_fleet_inventory() # dict {host: {{"ip":..,"port":..,"role":..}}}
class SelfHealer:
def __init__(self, dry_run=True, confirm_kill=False, yes=False, executor=None):

View File

@@ -12,16 +12,59 @@ import argparse
import subprocess
from pathlib import Path
#!/usr/bin/env python3
"""
[OPS] Sovereign Skill Installer
Part of the Gemini Sovereign Infrastructure Suite.
Packages and installs Hermes skills onto remote wizard nodes.
"""
import os
import sys
import argparse
import subprocess
from pathlib import Path
import yaml
# --- CONFIGURATION ---
# Assumes hermes-agent is a sibling directory to timmy-config
HERMES_ROOT = "../hermes-agent"
# Load fleet inventory and path contracts from central timmy-config
def get_config_path():
return os.environ.get('TIMMY_CONFIG') or os.path.join(
os.path.dirname(os.path.abspath(__file__)), '..', 'config.yaml'
)
def load_fleet_inventory():
"""Return dict of {{host: {{ip, port, role, remote_root, capabilities}}}}"""
try:
with open(get_config_path(), 'r') as f:
cfg = yaml.safe_load(f)
inv = cfg.get('fleet', {}).get('inventory', {})
return inv if inv else {}
except Exception:
return {}
def get_path_contract(key, default):
"""Return path contract value from config."""
try:
with open(get_config_path(), 'r') as f:
cfg = yaml.safe_load(f)
return cfg.get('fleet', {}).get('path_contracts', {}).get(key, default)
except Exception:
return default
FLEET = load_fleet_inventory()
HERMES_ROOT = get_path_contract('hermes_agent_local', '../hermes-agent')
REMOTE_ROOT = get_path_contract('hermes_remote', '/opt/hermes')
SKILLS_DIR = "skills"
class SkillInstaller:
def __init__(self, host: str, ip: str):
self.host = host
self.ip = ip
self.hermes_path = Path(HERMES_ROOT).resolve()
self.hermes_path = Path(HERMES_ROOT).expanduser().resolve()
def log(self, message: str):
print(f"[*] {message}")
@@ -44,13 +87,13 @@ class SkillInstaller:
# 2. Upload to remote
self.log("Uploading to remote...")
remote_path = f"/opt/hermes/skills/{skill_name}"
subprocess.run(["ssh", f"root@{self.ip}", f"mkdir -p /opt/hermes/skills"])
remote_path = f"{REMOTE_ROOT}/skills/{skill_name}"
subprocess.run(["ssh", f"root@{self.ip}", f"mkdir -p {REMOTE_ROOT}/skills"])
subprocess.run(["scp", tar_file, f"root@{self.ip}:/tmp/"])
# 3. Extract and register
self.log("Extracting and registering...")
extract_cmd = f"tar -xzf /tmp/{tar_file} -C /opt/hermes/skills/ && rm /tmp/{tar_file}"
extract_cmd = f"tar -xzf /tmp/{tar_file} -C {REMOTE_ROOT}/skills/ && rm /tmp/{tar_file}"
subprocess.run(["ssh", f"root@{self.ip}", extract_cmd])
# Registration logic (simplified)

View File

@@ -17,14 +17,32 @@ if SCRIPT_DIR not in sys.path:
sys.path.insert(0, SCRIPT_DIR)
from ssh_trust import VerifiedSSHExecutor
import yaml
# --- CONFIGURATION ---
FLEET = {
"mac": "10.1.10.77",
"ezra": "143.198.27.163",
"allegro": "167.99.126.228",
"bezalel": "159.203.146.185"
}
def get_config_path():
return os.environ.get('TIMMY_CONFIG') or os.path.join(
os.path.dirname(os.path.abspath(__file__)), '..', 'config.yaml'
)
def load_fleet_inventory():
"""Return {{host: ip}} map from config.yaml or fallback defaults."""
try:
with open(get_config_path(), 'r') as f:
cfg = yaml.safe_load(f)
inv = cfg.get('fleet', {}).get('inventory', {})
if inv:
return {k: v['ip'] for k, v in inv.items()}
except Exception:
pass
return {
"mac": "10.1.10.77",
"ezra": "143.198.27.163",
"allegro": "167.99.126.228",
"bezalel": "159.203.146.185",
}
FLEET = load_fleet_inventory() # dict {host: ip} loaded from config or defaults
TELEMETRY_FILE = "logs/telemetry.json"
class Telemetry: