Skip to content

osapi-io/gohai

Repository files navigation

release codecov go report card license build powered by conventional commits built with just gitHub commit activity go reference

gohai

gohai is an SDK-first Go library for collecting comprehensive system facts, inspired by Chef Ohai. Import it into your Go application for typed access to system facts — or use the standalone gohai CLI, a thin wrapper over the same SDK.

🐧 Linux-first. macOS is supported with a narrower field surface (see per-collector docs for platform coverage); Windows is not supported.

Each collector wraps a well-maintained backing source (gopsutil, ghw, procfs, cloud SDKs) and reshapes its output into typed Go structs. gohai's value is the unified API, typed structs, and pluggable collector model — not reimplementing /proc parsing from scratch.

Schema: OCSF + OpenTelemetry

gohai produces a validated OCSF inventory_info event (class_uid 5001) via --format ocsf. Standard OCSF attributes map directly; a gohai vendor extension (uid 1337) carries fields OCSF doesn't yet cover — validated against OCSF's own schema validator (12/12 tests passing).

Field names follow a three-tier naming ladder:

  1. OCSF (Open Cybersecurity Schema Framework) — primary authority. ~108 fields map to standard OCSF objects (device, device_hw_info, os, network_interface, cloud, package, process). Browse schema.ocsf.io.
  2. OpenTelemetry Resource Semantic Conventions — when OCSF is silent. ~74 fields cover CPU microarchitecture, memory states, filesystem attributes, hardware detail.
  3. gohai convention — for the ~768 remaining fields where no standard has an opinion. Starts from the backing library's field name in snake_case.

The complete per-field mapping lives in schemas/field-mapping.md. Gap candidates for upstream OCSF PRs are tracked in schemas/ocsf-gaps.md.

What we collect draws on Chef Ohai's plugin methodology. What we call each field draws on OCSF + OpenTelemetry.

Primary consumer

gohai is built to be embedded in OSAPI and other Go services that need typed system facts for routing, guards, discovery, inventory, and compliance. The CLI is a convenience — the SDK is the product.

📦 Install

curl -fsSL https://github.com/osapi-io/gohai/raw/main/install.sh | bash

Installs to ~/.local/bin (or /usr/local/bin as root) — SHA-256 checksums verified. Override with GOHAI_INSTALL_DIR=/some/path or pin a version with GOHAI_VERSION=1.0.0.

Other install methods

Go install

go install github.com/osapi-io/gohai@latest

As a library dependency

go get github.com/osapi-io/gohai

Build from source

git clone https://github.com/osapi-io/gohai.git
cd gohai
go build -o gohai .

✨ Features

Feature Description
🔌 Pluggable Collectors Enable/disable individual fact collectors
🏗️ Typed Structs Strongly-typed Go structs for all facts
📄 JSON Output Nested JSON output for CLI and programmatic use
🗺️ Flat Map Access Dot-separated key-value access
🐧 Cross-Platform Linux primary, macOS best-effort
🔗 Collector Dependencies Automatic dependency resolution between facts
⚡ Concurrent Collection Collectors run concurrently; dependency graph resolves order when any collector declares deps.
⏱️ Per-Collector Timings Opt-in --with-timings / WithTimings() embeds per-collector durations, status, and error messages under _timings in the JSON output
📊 OCSF + OpenTelemetry + Ohai Field names follow OCSF then OpenTelemetry; data sources mirror Chef Ohai's plugins
🔄 Native OCSF Output --format ocsf produces a standards-compliant OCSF inventory_info event (class_uid 5001) — feed directly into SIEMs and data lakes
🔌 SDK Integration Import as a Go package for OSAPI and others

🔌 Collectors

65 collectors across 9 categories. See the Collectors reference for the full catalog — implementation status, default membership, schema mappings, and per-collector docs.

Collectors are individually toggled using node_exporter-style flags — --collector.<name> to opt in, --no-collector.<name> to opt out. SDK consumers use gohai.WithEnabled(...) / gohai.WithDisabled(...) / gohai.WithCollectors(...).

Defaults are opt-in. gohai.New() returns an empty registry. Pass gohai.WithDefaults() for the recommended set (cheap + near-universal — identity, base hardware, network, load, virt detect). The CLI wires WithDefaults() automatically; pass --no-defaults to skip it and use only explicit --collector.X flags.

🎯 Usage

CLI

gohai collect --pretty                          # default collectors, pretty JSON
gohai collect --format ocsf --pretty            # OCSF inventory_info event
gohai collect --flat                            # flat key=value pairs
gohai collect --no-defaults --collector.cpu     # specific collectors only
gohai collect --pretty | gohai validate          # validate against schema
gohai version                                   # build info

SDK

Importers should read the full API reference on pkg.go.dev for every Option, Facts field, and Info struct — that's the authoritative API surface. The examples below show the two usage shapes.

Collecting facts (producer side):

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/osapi-io/gohai/pkg/gohai"
)

func main() {
    g, err := gohai.New(
        gohai.WithDefaults(),                      // the recommended set
        gohai.WithEnabled("process", "packages"),  // plus these two
    )
    if err != nil {
        log.Fatal(err)
    }

    facts, err := g.Collect(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    // Typed access — pkg.go.dev documents every Info struct's fields.
    fmt.Printf("OS:     %s %s\n", facts.Platform.Name, facts.Platform.Version)
    fmt.Printf("Cores:  %d\n", facts.CPU.Cores)
    fmt.Printf("Memory: %d bytes\n", facts.Memory.Total)

    // Serialize for transport / storage.
    b, _ := facts.PrettyJSON()
    fmt.Println(string(b))
}

Consuming stored facts (decoder side — e.g. a server that received a fact blob from an agent):

var facts gohai.Facts
if err := json.Unmarshal(payload, &facts); err != nil {
    log.Fatal(err)
}

// Typed access on the decoded value — no map[string]any guessing.
fmt.Println(facts.Platform.Name, facts.CPU.Cores)
fmt.Println(facts.Network.DefaultInterface)

Per-collector timings + error messages can be embedded in Facts by adding gohai.WithTimings() to gohai.New(...) — useful for debugging slow collectors or seeing why a collector failed without blocking the run. See the Timings field on pkg.go.dev.

Detecting which cloud you're on. Enable the cloud collectors (WithCategory("cloud")) and switch on Facts.Cloud() — returns a *Cloud with Name set to a provider identifier, or nil when no cloud was detected. Use the exported gohai.CloudAWS / CloudGCE / CloudAzure / etc. constants instead of raw strings:

g, _ := gohai.New(gohai.WithCategory("cloud"))
facts, _ := g.Collect(ctx)

cloud := facts.Cloud()
if cloud == nil { return } // not on a supported cloud

switch cloud.Name {
case gohai.CloudAWS:
    fmt.Println(facts.Ec2.Region, facts.Ec2.IAMInfo.InstanceProfileArn)
case gohai.CloudGCE:
    fmt.Println(facts.Gce.ProjectID, facts.Gce.Zone)
}

Rich per-provider data lives on the typed Facts.Ec2 / Facts.Gce / etc. field. See docs/collectors/cloud.md for the full pattern.

📖 Documentation

  • Package documentation on pkg.go.dev — generated API reference. Every Option, Facts field, and Info struct is documented there. This is the authoritative SDK reference.
  • Collectors reference — one doc per collector with fields, schema mappings (OCSF + OpenTelemetry), and Ohai source alignment.
  • Schemas — JSON Schema, field-naming strategy (OCSF > OTel > convention), OCSF gap analysis, and cloud canonical overlay.
  • Development — prerequisites, setup, testing, commit conventions.
  • Contributing — PR workflow.

🤝 Contributing

See the Development guide for prerequisites, setup, and conventions. See the Contributing guide before submitting a PR.

🔗 Related Works

gohai stands on the shoulders of the following projects — as methodology references, as backing libraries we wrap, or as peers solving adjacent problems:

Fact collectors (direct peers):

  • Chef Ohai — the canonical reference. Ruby-based plugin-driven fact collector; every gohai collector cross-references the corresponding Ohai plugin for data sources and per-distro edge cases.
  • Puppet Facter — Puppet's equivalent. Different JSON shape, overlapping fact surface.
  • osquery — Meta's SQL-based endpoint visibility. Different abstraction (SQL), same data space; common reference point when evaluating an inventory tool.
  • Ansible setup — Ansible's built-in fact gathering, exposed as ansible_facts in playbooks.
  • Salt Grains — SaltStack's static facts.

Backing libraries (we import these):

  • gopsutil — primary source for dynamic runtime state (memory, network I/O, process enumeration, virtualization detection).
  • ghw — canonical for physical hardware topology (CPU NUMA, DIMMs, block devices, DMI, GPU, PCI).
  • procfs — Linux /proc and /sys parsing when a library doesn't cover a field.
  • go-sysinfo — Elastic's alternative for host/platform/kernel facts.
  • avfs — virtual filesystem abstraction used in every collector that reads files, so tests can run against in-memory fixtures.

Other Go libraries in the space:

  • gosigar — Cloud Foundry's Go port of Hyperic Sigar. Historical reference for Go-based host metrics.
  • go-ps — narrow process-listing library. gopsutil supersedes it for our use.
  • goprocinfo — lightweight /proc parser. gopsutil + procfs cover the same ground for us.

Methodology references (we read, don't import):

  • node_exporter — gold standard for tricky Linux /proc and /sys parsing. Apache-2, but we rewrite in our style rather than import.
  • psutil — the Python library gopsutil is a port of; the original design reference for the dynamic-state facts.

📄 License

The MIT License.

About

A Go-based system fact collector inspired by Chef Ohai.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages