Repository navigation
Architecture
HomePilot is a single-binary Python service (FastAPI) backed by SQLite, with an MCP server frontend, an age-encrypted vault, and an artifact store backed by git.
The MCP (Model Context Protocol) server is the primary interface for AI clients. It supports two transports:
- stdio — for local use (AI client on the same machine)
- HTTP (StreamableHTTP) — for remote use (AI client connects over the network)
The MCP server exposes tools that AI clients can call to:
- Query inventory (hosts, services, networks)
- Search the knowledge base
- Propose, approve, and apply artifacts
- Read files or execute commands on managed hosts (read-only)
- Check artifact drift
| Service | Description |
|---|---|
ArtifactLifecycle |
Manages artifact state transitions (propose → approve → apply → revoke) |
ArtifactExecutor |
Dispatches artifact execution based on kind (Ansible, Proxmox API, HTTP, shell, composite) |
KBService |
Knowledge base with vector + keyword search, backed by sqlite-vec |
InventoryService |
Periodic reconciler that pulls host/service data from Proxmox and SSH guests |
VaultManager |
Manages age-encrypted secrets with passphrase-derived key |
EventBus |
Publishes SSE events for lifecycle transitions; delivers to registered webhooks |
~/.hp/
├── homepilot.db SQLite: hosts, services, artifacts index,
│ KB entries (+ sqlite-vec for embeddings),
│ audit log, webhook deliveries
├── artifacts/
│ └── YYYY/MM/YYYY-MM-DD-<slug>-<hash>.md one file per artifact
│ frontmatter (YAML) + body (Markdown)
└── vault/
├── master.protected age-encrypted master identity
└── secrets/ per-secret .age files
├── pve-token.age
└── <service>.age
Vault encryption uses pyrage (pure Python age implementation). No system age binary is required. The master identity is protected with AES-256-GCM derived from the vault passphrase.
- Pulls host/VM/LXC data from Proxmox REST API
- Optionally connects to guests via SSH jump server
- Stores results in SQLite (
hosts,servicestables) - Runs on configurable interval (default: 300 seconds)
AGENT YOU
──────────────────────────────────────────────────
1. READ query_inventory, search_kb
→ Agent builds context
2. PROPOSE propose_artifact(spec)
→ Artifact written with status: proposed
3. REVIEW hp artifacts show <id>
hp artifacts approve <id> → status: approved
hp artifacts reject <id> → status: rejected (terminal)
4. APPLY hp artifacts apply <id>
→ ArtifactExecutor._dispatch() runs the spec
→ Before mutating: Proxmox snapshot (if applicable)
→ Dispatch by kind:
ansible-playbook → playbook via SSH jump server
proxmox-api-seq → ordered Proxmox REST calls
http-sequence → ordered calls to adopted services
shell-script → exec on guest via SSH
composite → ordered list of sub-artifacts
kb-note → write to KB (auto-applied, no exec)
│
status: applied
or status: failed
│
Snapshot pruned
Audit entry written
5. REVOKE hp artifacts revoke <id> (applied → revoked)
→ If Proxmox snapshot exists: VM rolled back automatically
→ Artifact status: revoked, snapshot deleted
Every artifact transition and drift detection emits an SSE event via the EventBus. Connected clients stream these at GET /events.
Registered webhooks receive the same events as outbound HTTP POST deliveries with HMAC-SHA256 signatures.
All state transitions also write an append-only audit_log table in SQLite.
| Tool | Mutates? | Scope | Description |
|---|---|---|---|
query_inventory |
no | read | List hosts/services/networks from DB |
refresh_inventory |
no | read | Re-pull inventory from Proxmox + SSH |
get_environment_doc |
no | read | Fused doc for a target |
query_artifacts |
no | read | Find artifacts by status, kind, target |
get_artifact_status |
no | read | Detailed status of one artifact |
search_kb |
no | read | Vector + keyword search over KB |
proxmox_api_read |
no | read | GET-only Proxmox REST call |
http_call_read |
no | read | GET-only call to adopted services |
read_file_on_guest |
no | read | SFTP file read via jump server |
exec_on_guest_readonly |
no | read | Whitelisted read-only SSH exec |
check_artifact_drift |
no | read | Check if applied artifact has drifted |
record_fact |
KB only | write | Write to KB (auto-applied, no approval) |
propose_artifact |
triggers flow | write | Creates artifact with status: proposed |
approve_artifact |
yes | write | Approve a proposed artifact |
The jump server relays SSH connections from HomePilot to managed hosts without exposing private keys to the backend container. It uses a TCP protocol with length-prefixed JSON framing.
HomePilot backend → jumpserver:50051 → SSH → managed guest
The jump server generates its own key pair. You add the public key to authorized_keys on each managed host. For security, PVE_NODE_NAMES can block SSH to Proxmox nodes.
| Source | Protocol | Data Collected |
|---|---|---|
| Proxmox VE | REST API (HTTPS) | VMs, LXCs, nodes, storage, networks |
| SSH guests | Via jump server | Services, packages, configs, facts |
Every artifact write is committed to a local git repo inside the data directory. Optionally push to a private GitHub repository:
HP_ARTIFACTS_REMOTE=git@github.com:org/homepilot-artifacts.git
HP_ARTIFACTS_SSH_KEY=/home/homepilot/.hp/artifacts_deployPush failures are non-fatal — HomePilot continues normally if the remote is unreachable.
- Artifacts — Detailed artifact spec and lifecycle
- Configuration — Vault, secrets, and configuration options
- Security — Auth model and RBAC