# 02 — Architecture ## Runtime stack | Layer | Technology | |-------|------------| | Language | Go 1.22+ | | HTTP | `net/http`, `http.ServeMux` | | UI | Embedded via `//go:embed` in `web/embed.go` (templates + static assets) | | State | In-memory only — no database | | Build | `CGO_ENABLED=0`, single static binary | Default port: **8082** ## Directory structure ``` cmd/logpile/main.go # Binary entry point, CLI flag parsing internal/ collector/ # Live data collectors registry.go # Collector registration redfish/ # Redfish collector (real) ipmi/ # IPMI collector (mock scaffold) parser/ # Archive parsers bmc_parser.go # Top-level parser dispatcher vendors/ # Vendor-specific parser modules vendors.go # Import-side-effect registrations inspur/ supermicro/ nvidia/ nvidia_bug_report/ xigmanas/ generic/ pciids/ # PCI IDs lookup (embedded pci.ids) server/ # HTTP layer server.go # Server struct, route registration handlers.go # All HTTP handler functions exporter/ # Export formatters reanimator_models.go reanimator_converter.go csv.go / json.go models/ # Shared data contracts web/ embed.go # go:embed directive templates/ # HTML templates static/ # JS / CSS js/app.js # Frontend — API contract consumer ``` ## In-memory state The `Server` struct in `internal/server/server.go` holds: | Field | Type | Description | |-------|------|-------------| | `result` | `*models.AnalysisResult` | Current parsed/collected dataset | | `detectedVendor` | `string` | Vendor identifier from last parse | | Job manager | internal | Tracks live collect job status/logs | State is replaced atomically on successful upload or collect. On a failed/canceled collect, the previous `result` is preserved unchanged. ## Upload flow (`POST /api/upload`) ``` multipart form field: "archive" │ ├─ file looks like JSON? │ └─ parse as models.AnalysisResult snapshot → store in Server.result │ └─ otherwise └─ parser.NewBMCParser().ParseFromReader(...) │ ├─ try all registered vendor parsers (highest confidence wins) └─ result → store in Server.result ``` ## Live collect flow (`POST /api/collect`) ``` validate request (host / protocol / port / username / auth_type / tls_mode) │ └─ launch async job │ ├─ progress callback → job log (queryable via GET /api/collect/{id}) │ ├─ success: │ set source metadata (source_type=api, protocol, host, date) │ store result in Server.result │ └─ failure / cancel: previous Server.result unchanged ``` Job lifecycle states: `queued → running → success | failed | canceled` ## PCI IDs lookup Lookup order (first match wins, `LOGPILE_PCI_IDS_PATH` highest priority): 1. Embedded `internal/parser/vendors/pciids/pci.ids` (compiled into binary) 2. `./pci.ids` 3. `/usr/share/hwdata/pci.ids` 4. `/usr/share/misc/pci.ids` 5. `/opt/homebrew/share/pciids/pci.ids` 6. `LOGPILE_PCI_IDS_PATH` env var (colon-separated list; overrides all above) This means unknown GPU/NIC model strings can be updated by refreshing `pci.ids` without any code change.