Documentation
¶
Overview ¶
Package onboard is the deterministic core of `halo doctor` and `halo onboard`: detect the AI CLIs on this machine, check the environment and print exact fixes, plan and apply a local install of a ring's managed config, generate a local halo-proxy config, run a headless round trip, and generate a company rollout bundle. Agents (the halos-onboard skill, the MCP tools) call these; nothing here asks questions or decides policy.
Secrets: credential checks only report whether an environment variable is set, never its value, and verify output is redacted before it is returned.
Index ¶
- Constants
- Variables
- func Company(o CompanyOptions) (map[string][]byte, error)
- func DefaultRing(org *policy.Org) string
- func IsLoopback(u string) bool
- func Issues(is []policy.Issue) (out []string, ok bool)
- func LocalProxy(org *policy.Org, absDir, listen string) (map[string][]byte, string, error)
- func Reachable(ctx context.Context, c *http.Client, base string) (int, error)
- func SuggestProvider(e Env) string
- func SuggestTools(hs []Harness) map[string]string
- type Check
- type CompanyOptions
- type CompanyResult
- type Credential
- type Env
- type FilePlan
- type Harness
- type InstallPlan
- type LocalOptions
- type LocalResult
- type PolicyResult
- type ProxyResult
- type Report
- type Runner
- type VerifyResult
Constants ¶
const ( StatusOK = "ok" StatusWarn = "warn" StatusFail = "fail" )
Check statuses.
const ( ActionCreate = "create" ActionUpdate = "update" ActionUnchanged = "unchanged" // ActionForeign is a differing file Halos did not write (e.g. one an MDM // pushed). Apply refuses to replace it unless asked to. ActionForeign = "foreign" )
File actions in an install plan.
const ( VerifyPass = "pass" VerifyFail = "fail" VerifySkipped = "skipped" VerifyDryRun = "dry-run" )
Verify results.
const ( PolicyCreate = "create" PolicyUnchanged = "unchanged" PolicyKept = "kept" // exists and differs from the answers: never overwritten )
Policy actions.
const DefaultLocalProxy = "127.0.0.1:8088"
DefaultLocalProxy is the loopback listener a local halo-proxy uses.
const LocalProxyDir = ".halos/local"
LocalProxyDir is where `halo onboard proxy` writes, inside the policy repo.
const (
VerifyPrompt = "Reply with exactly the word HALOS_OK and nothing else."
)
VerifyPrompt is what verify asks; a pass is the token in the reply.
Variables ¶
var ( Gateways = []string{"halo-proxy", "kong", policy.EngineExternal} Deliveries = []string{"devcontainer", "mdm", "halod"} )
Company gateways and delivery channels.
var ErrForeign = errors.New("refusing to replace files Halos did not write")
ErrForeign is returned (wrapped) by Install when apply would replace files Halos did not write and replace was not set.
var ErrNotFound = errors.New("no halos.yaml")
ErrNotFound marks a missing policy repo.
var ErrPermission = errors.New("permission denied")
ErrPermission is returned (wrapped) when a managed path is not writable.
var FallbackTools = map[string]string{"claude-code": "2.1.280"}
FallbackTools is what a machine with no AI CLI on PATH starts with.
Functions ¶
func Company ¶
func Company(o CompanyOptions) (map[string][]byte, error)
Company generates a policy repo for an organisation: halos.yaml, an optional Gateway overlay for the token helper, Helm values, the IdP client registration, enrollment config per delivery channel, a CI workflow, a smoke eval suite and a README of next steps. Keys are paths relative to the repo. Nothing here is secret: every credential is a reference to a Secret or env var. Everything but halos.yaml and gateway.yaml lives under .halos/: the policy loader reads every other YAML file in the repo as a policy document.
func DefaultRing ¶
DefaultRing is the ring a single machine follows: the last (GA) ring.
func IsLoopback ¶
IsLoopback reports whether u's host is localhost or a loopback IP.
func LocalProxy ¶
LocalProxy returns the files for a single-developer halo-proxy on loopback (identity off: it listens on 127.0.0.1 only) keyed by path relative to the policy repo, plus the command that starts it. absDir is the policy repo's absolute path (halo-proxy resolves the snapshot path from it).
func Reachable ¶
Reachable reports the HTTP status base answers with; any status counts (a gateway answers 401/404 without a token).
func SuggestProvider ¶
SuggestProvider picks the provider whose credential is already exported; anthropic when none is (ordering is the order checked).
func SuggestTools ¶
SuggestTools pins every installed CLI at the version found on PATH, so the first release changes no CLI binary. Empty when none is installed.
Types ¶
type Check ¶
type Check struct {
ID string `json:"id"`
Status string `json:"status"`
Detail string `json:"detail"`
Fix string `json:"fix,omitempty"`
}
Check is one doctor finding. Fix is the exact command or step that resolves a warn or fail; it never contains a secret value.
type CompanyOptions ¶
type CompanyOptions struct {
Init intent.InitOptions // org, tools, provider, models, gateway, issuer, client id, admins, presets
GatewayKind string // halo-proxy | kong | external (the company's own API gateway; Halos runs no data plane)
AuthHelper string // command printing a short-lived gateway token on each laptop (optional)
Delivery []string // devcontainer | mdm | halod
Registry string // OCI repository releases are published to
PolicyRepo string // git URL of this policy repo (Helm git-sync)
PortalURL string // halo-server base URL
HaloVersion string // halo the CI workflow installs ("" or "dev" = latest)
}
CompanyOptions are an admin's interview answers.
type CompanyResult ¶
type CompanyResult struct {
DryRun bool `json:"dryRun"`
Dir string `json:"dir"`
Files map[string]string `json:"files"` // repo-relative path -> create | unchanged
Valid bool `json:"valid"`
Issues []string `json:"issues"`
Next []string `json:"next"`
}
CompanyResult is what `halo onboard company` decided per file.
func WriteCompany ¶
func WriteCompany(dir string, o CompanyOptions, apply bool) (*CompanyResult, error)
WriteCompany generates the company repo into dir, validates it, and writes it when apply. It never overwrites a file that differs (exit with a list).
type Credential ¶
type Credential struct {
Env string `json:"env"`
For string `json:"for"`
Present bool `json:"present"`
}
Credential reports whether a credential variable is set. Never its value.
func Credentials ¶
func Credentials(e Env) []Credential
Credentials lists every known credential variable and whether it is set.
type Env ¶
type Env struct {
Run Runner
LookPath func(string) (string, error)
Getenv func(string) string
GOOS string
HTTP *http.Client // nil: skip network checks
// ReadFile and Home read harness settings for the sandbox/egress posture
// (ADR-0032); a nil ReadFile skips it, an empty Home skips per-user files.
ReadFile func(string) ([]byte, error)
Home string
}
Env is everything onboarding reads from the machine, injectable for tests.
type FilePlan ¶
type FilePlan struct {
Harness string `json:"harness"`
Path string `json:"path"` // the path the CLI reads
Dest string `json:"dest"` // where it is written (Path, or under --root)
Mode uint32 `json:"mode"`
Action string `json:"action"`
Backup string `json:"backup,omitempty"` // an existing differing file is kept here first
Content string `json:"content"`
// contains filtered or unexported fields
}
FilePlan is one managed config file: where it lands and what happens to it.
type Harness ¶
type Harness struct {
Name string `json:"name"`
Binary string `json:"binary"`
Installed bool `json:"installed"`
Path string `json:"path,omitempty"`
Version string `json:"version,omitempty"`
Error string `json:"error,omitempty"`
}
Harness is one AI CLI as found on PATH.
type InstallPlan ¶
type InstallPlan struct {
Ring string `json:"ring"`
OS string `json:"os"`
Root string `json:"root,omitempty"`
Gateway string `json:"gateway,omitempty"`
Versions map[string]string `json:"versions"` // harness -> pinned CLI version ("" = unpinned)
Files []FilePlan `json:"files"`
Warnings []string `json:"warnings"`
}
InstallPlan is what installing a ring's release on this machine does.
func Install ¶
func Install(dir, ring, goos, root string, apply, replace bool) (*InstallPlan, []string, error)
Install plans the managed-config install for the policy in dir and, when apply, writes it. A file Halos did not write (ActionForeign) blocks the whole apply unless replace is set. Idempotent: a second apply writes nothing.
func PlanInstall ¶
func PlanInstall(org *policy.Org, ring, goos, root string) (*InstallPlan, error)
PlanInstall renders ring's release for goos and compares each file with what is on disk under root ("" = the real paths). It writes nothing.
func (*InstallPlan) Apply ¶
func (p *InstallPlan) Apply() ([]string, error)
Apply writes every create/update/foreign file (callers gate foreign ones). A replaced foreign file is kept at <dest>.halos-backup (timestamped if that exists), and each write records its hash at <dest>.halos-sha256. Idempotent: a second Apply finds every file unchanged and writes nothing.
type LocalOptions ¶
type LocalOptions struct {
Dir string
Init intent.InitOptions // Gateway "" = the local halo-proxy on loopback
Ring string // "" = the GA (last) ring
GOOS string
Root string // stage managed config under this directory instead of the real paths
Apply bool // write halos.yaml (never the managed config: see Install)
}
LocalOptions drive the "my machine" path.
type LocalResult ¶
type LocalResult struct {
DryRun bool `json:"dryRun"`
Policy PolicyResult `json:"policy"`
Valid bool `json:"valid"`
Issues []string `json:"issues"`
Install *InstallPlan `json:"install,omitempty"`
Next []string `json:"next"`
}
LocalResult is one pass of init -> validate -> install plan.
func Local ¶
func Local(o LocalOptions) (*LocalResult, error)
Local creates (or keeps) the policy in o.Dir, validates it as it would be written, and plans the managed-config install. It writes halos.yaml only when o.Apply and the policy validates; it never writes managed config. Idempotent: a second run reports the policy unchanged.
type PolicyResult ¶
type PolicyResult struct {
Path string `json:"path"`
Action string `json:"action"`
Content string `json:"content"`
Notes []string `json:"notes"`
}
PolicyResult is what InitPolicy decided for halos.yaml.
func InitPolicy ¶
func InitPolicy(dir string, o intent.InitOptions) (*intent.Change, PolicyResult, error)
InitPolicy is the idempotent front half of `halo init`: the halos.yaml o describes, as a change on dir. An existing halos.yaml is never replaced: the change is then empty and the result says whether it already matches. The caller validates (Change.Validate) and applies.
type ProxyResult ¶
type ProxyResult struct {
DryRun bool `json:"dryRun"`
Files map[string]string `json:"files"` // absolute path -> create | update | unchanged
Command string `json:"command"`
Notes []string `json:"notes"`
}
ProxyResult is what `halo onboard proxy` decided.
type Report ¶
type Report struct {
OK bool `json:"ok"`
PolicyDir string `json:"policyDir"`
Checks []Check `json:"checks"`
Harnesses []Harness `json:"harnesses"`
Credentials []Credential `json:"credentials"`
// Posture is each installed harness's effective sandbox and egress
// posture on this machine (ADR-0032).
Posture []harness.Posture `json:"posture,omitempty"`
}
Report is the doctor result. OK is false when any check failed.
type VerifyResult ¶
type VerifyResult struct {
Harness string `json:"harness"`
Command []string `json:"command"`
Status string `json:"status"`
Detail string `json:"detail"`
Output string `json:"output,omitempty"`
}
VerifyResult is one headless round trip. Output is redacted and truncated.
func Verify ¶
func Verify(ctx context.Context, e Env, h string, assumeAuth, dry bool, timeout time.Duration) VerifyResult
Verify runs h headless with VerifyPrompt (unless dry) and checks the reply. It is skipped when the CLI is missing or no credential variable is set and assumeAuth is false (a logged-in CLI needs --assume-auth: halo cannot see it).