installation

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2026 License: MPL-2.0 Imports: 6 Imported by: 0

README

Installation planning

github.com/AChWorks/achrix/installation is independently versioned, standard-library-only operational tooling. It can run before an Application, database or administrator exists. It performs no host inspection, command execution, provisioning, migration, activation, network call or persistence.

The product supplies actual requirements, trusted observations and explicitly selected adapters. This package supplies one deterministic assessment/planning boundary and conservative re-entry decisions. The real disposable-resource example demonstrates private-file inspection, a product-owned effect, retained attempt metadata, a lost acknowledgement and verification without replay. It does not establish a production installer, Rixa/panel support, generic updater or Recovery implementation.

Inputs and binding

Public Go declarations own exact signatures.

Contract Responsibility
Binding{ProfileID, OperationID, CandidateSHA256, PreconditionsSHA256} Product-selected exact operation/candidate/approved precondition identity. Digests are 64 lower-case hexadecimal characters encoding SHA-256; IDs are non-secret ASCII references of 1–128 bytes.
Profile{Binding, Requirements} The product declares 1–1024 unique outcomes. Requirement order is the intended action order. Required outcomes block installation; context entries never block or create actions.
Observation{RequirementID, State} Exactly one fresh observation per requirement. The product verifies the actual endpoint, resource, compatibility and intended state; existence of a tool, root or panel is not that proof.
Support{RequirementID, AdapterID, Authorized} One explicitly selected supported adapter per required outcome where applicable. Products determine actual authority; this assertion neither grants permission nor exposes an executable callback.
Progress{PlanID, Attempts} Product-owned retained attempt/verification evidence. Persist Attempted before effects and Verified only after actual postflight. An empty Progress is for a genuinely new operation only.

Identifiers and digests must identify safe opaque references and non-secret artifact/configuration/target metadata. Never insert DSNs, paths, passwords, tokens, secret values or raw provider errors. A digest does not establish publisher authenticity, compatibility or permission. Products verify candidate authenticity and supported component/schema combinations independently and change their binding when the intended artifact, target or approved configuration/preconditions changes.

Assess(profile, observations, supports, progress) rejects malformed, missing, duplicate and unknown entries with fixed ErrInput; a progress plan mismatch returns ErrStale. It returns no partially valid plan on those errors. No CPU, operating-system, package-manager, panel or product names are built into evaluation.

Observation and plan meaning

Observation Meaning
Satisfied The exact desired requirement was actually verified.
Missing Currently absent or unsatisfied; this alone never proves a previous effect cannot arrive later.
Unknown Unchecked, unavailable or inconclusive. Never counts as available or provisionable.
Unsupported The product's selected supported profile cannot satisfy this outcome.
NotApplied Stronger than absence: a trusted adapter fenced prior work and definitively proved that its effect did not occur and cannot arrive later.

Outcomes retain the four installation categories Available, Provisionable, OperatorRequired and Incompatible, with stable reasons distinguishing missing adapter, authority, unknown evidence, reconciliation or lost verification. A provisionable outcome needs both an explicit adapter and its current authority.

Plan statuses distinguish:

  • Ready: every required outcome is presently verified; this is the assessed requirement set, not a claim of whole-product deployment.
  • ChangesRequired: a bounded action proposal exists after all other required preflight passes. The product must authorize and execute it separately.
  • OperatorActionRequired: a requirement needs operator/provider input.
  • ReconciliationRequired: an earlier attempt or drift requires investigation before mutation.
  • UnsupportedPlan: a mandatory outcome is incompatible with the selected profile.

Blocked, unsupported or uncertain plans expose zero Actions even if another outcome could be provisioned. Context such as unavailable root, package management or a panel may be reported without blocking an otherwise satisfied provider-managed installation.

The immutable Plan snapshots input slices. ID() binds the exact profile, operation, candidate, preconditions, ordered requirement set and normalized adapter/authority declarations. Observations and progress deliberately do not change that ID because they advance during the same operation. The hash is a consistency precondition, not a permission or authentication token. Output JSON contains stable metadata only; human messages and localization belong to the product.

Safe execution and re-entry

Before using a proposal, the product must:

  1. authorize the exact candidate and target through its deployment boundary;
  2. serialize conflicting operations with a real product/platform lock or fencing mechanism;
  3. inspect fresh actual state and authority;
  4. call plan.Recheck(currentProfile, freshObservations, currentSupports, retainedProgress) and use the returned plan, rejecting ErrStale instead of using cached actions;
  5. durably record each attempt before its effect, execute only the selected supported operation, and verify afterward;
  6. retain unknown outcomes and actionable failure state across interruption; never infer rollback from a timeout.

Recheck rejects changed binding, requirement/order or selected adapter/authority declarations. It reclassifies fresh observations and retained progress. It does not know whether a product's inspection is truthful/current, authenticate a candidate, grant authority or provide a concurrency lock.

An Attempted effect plus Missing or Unknown observation produces reconciliation-required with no replay action. Satisfied can verify that the intended effect exists. Only authoritative fenced NotApplied can permit a later action proposal when a previous attempt may have happened. A previously Verified resource that stops satisfying the requirement requires explicit recovery; it is not automatically recreated.

These semantics cover the observed difference between safely repeated migration and one-time administrator bootstrap without pretending that all commands are idempotent. Database commit acknowledgement, account identity, listener ownership, private paths, service persistence, TLS and panel operations remain adapter/product-specific. The package provides no automatic retry, arbitrary shell, privileged runner, journal/lease/workflow engine, backup format, down migration or production update mechanism.

Version and validation

Version() reports this module's actual resolved tag/pseudo-version from Go build information. Source builds report development; replaced dependencies report development-replacement. The product retains exact artifact/source identities in addition to these labels. No Core runtime dependency is needed.

Run focused checks from this directory:

GOWORK=off go test -race -count=1 ./...
GOWORK=off go vet ./...

The repository's module-aware full validation and separately pinned Notes consumer own normal dependency-resolution/build identity and integrated proof. Published support remains limited to profiles that have their own executable evidence.

Documentation

Overview

Package installation evaluates product-declared installation requirements. It performs no discovery or mutation. Products own trusted observations, adapter authority, durable progress, serialization, effects and verification.

SPDX-License-Identifier: MPL-2.0

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrInput = errors.New("invalid installation input")
	ErrStale = errors.New("installation binding or plan changed")
)

Functions

func Version

func Version() string

Version reports this installation module's source-backed dependency version. Local or replaced builds identify themselves explicitly as development.

Types

type Action

type Action struct {
	RequirementID string
	AdapterID     string
}

type Attempt

type Attempt struct {
	RequirementID string
	State         AttemptState
}

type AttemptState

type AttemptState string
const (
	Attempted AttemptState = "attempted"
	Verified  AttemptState = "verified"
)

type Binding

type Binding struct {
	ProfileID           string
	OperationID         string
	CandidateSHA256     string
	PreconditionsSHA256 string
}

Binding identifies the exact operation, candidate and approved preconditions. Digests identify trusted non-secret artifact/configuration/target metadata. They do not establish publisher authenticity or grant execution authority.

type Observation

type Observation struct {
	RequirementID string
	State         ObservedState
}

type ObservedState

type ObservedState string
const (
	Satisfied   ObservedState = "satisfied"
	Missing     ObservedState = "missing"
	Unknown     ObservedState = "unknown"
	Unsupported ObservedState = "unsupported"
	// NotApplied is stronger than absence: the adapter has fenced prior work and
	// definitively established that its effect did not occur and cannot arrive.
	NotApplied ObservedState = "not-applied"
)

type Outcome

type Outcome struct {
	RequirementID string
	Required      bool
	State         OutcomeState
	Reason        Reason
	AdapterID     string
}

type OutcomeState

type OutcomeState string
const (
	Available        OutcomeState = "available"
	Provisionable    OutcomeState = "provisionable"
	OperatorRequired OutcomeState = "operator-required"
	Incompatible     OutcomeState = "unsupported"
)

type Plan

type Plan struct {
	// contains filtered or unexported fields
}

Plan is an immutable snapshot. Its ID binds the profile, candidate, operation, preconditions, requirement order and selected adapter/authority declarations. Observations/progress do not change its ID: those must advance during the same operation. The ID is a consistency precondition, never an authorization token.

func Assess

func Assess(profile Profile, observations []Observation, supports []Support, progress Progress) (*Plan, error)

Assess validates complete finite input, classifies required outcomes and makes a deterministic plan. Every declared requirement needs exactly one observation. Missing, duplicate, unknown and malformed entries fail closed. No host tools, root/panel/CPU detection, resource acquisition or effects occur here.

func (*Plan) Actions

func (p *Plan) Actions() []Action

Actions returns only proposals allowed by an otherwise satisfied preflight. A blocked/unsupported/uncertain plan offers no partial executable action set.

func (*Plan) Binding

func (p *Plan) Binding() Binding

func (*Plan) ID

func (p *Plan) ID() string

func (*Plan) MarshalJSON

func (p *Plan) MarshalJSON() ([]byte, error)

MarshalJSON exports only stable safe metadata, never profile secret values or raw inspection/provider errors. Caller-supplied identifiers must be non-secret.

func (*Plan) Outcomes

func (p *Plan) Outcomes() []Outcome

func (*Plan) Recheck

func (p *Plan) Recheck(current Profile, observations []Observation, supports []Support, progress Progress) (*Plan, error)

Recheck must receive current product binding and newly inspected evidence, adapter authority and durable progress before effects. It rejects a stale candidate/configuration/operation or changed adapter declarations. Callers use the returned fresh plan, never a cached earlier action list.

Example

This example is a single serialized product operation against a disposable private marker. A real adapter must supply its own cross-process lock/fence, durable atomic progress storage, artifact trust and target-specific checks.

package main

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path/filepath"

	"github.com/AChWorks/achrix/installation"
)

func main() {
	root, err := os.MkdirTemp("", "achrix-installation-example-")
	if err != nil {
		panic(err)
	}
	defer func() { _ = os.RemoveAll(root) }()
	resource := filepath.Join(root, "private-marker")
	journal := filepath.Join(root, "progress.json")
	snapshot := func(s string) string { d := sha256.Sum256([]byte(s)); return hex.EncodeToString(d[:]) }
	profile := installation.Profile{
		Binding: installation.Binding{
			ProfileID: "private-marker", OperationID: "install-42",
			CandidateSHA256:     snapshot("reviewed-example-artifact"),
			PreconditionsSHA256: snapshot("empty-private-marker-target:" + root),
		},
		Requirements: []installation.Requirement{{ID: "private.marker", Required: true}},
	}
	support := []installation.Support{{RequirementID: "private.marker", AdapterID: "product-marker", Authorized: true}}
	inspect := func() installation.Observation {
		body, readErr := os.ReadFile(resource)
		state := installation.Unknown
		switch {
		case errors.Is(readErr, os.ErrNotExist):
			state = installation.Missing
		case readErr == nil && string(body) == "install-42":
			state = installation.Satisfied
		case readErr == nil:
			state = installation.Unsupported // Preserve a resource this operation does not own.
		}
		return installation.Observation{RequirementID: "private.marker", State: state}
	}
	plan, err := installation.Assess(profile, []installation.Observation{inspect()}, support, installation.Progress{})
	if err != nil {
		panic(err)
	}
	fmt.Println(plan.Status())

	// The product durably records the attempt before its own effect. The private
	// journal carries only operation metadata, never credentials or raw errors.
	progress := installation.Progress{PlanID: plan.ID(), Attempts: []installation.Attempt{{RequirementID: "private.marker", State: installation.Attempted}}}
	data, err := json.Marshal(progress)
	if err != nil {
		panic(err)
	}
	if err = os.WriteFile(journal, data, 0o600); err != nil {
		panic(err)
	}
	// For this example, flush the private journal before attempting the effect.
	file, err := os.OpenFile(journal, os.O_RDWR, 0)
	if err != nil {
		panic(err)
	}
	if err = file.Sync(); err != nil {
		panic(err)
	}
	if err = file.Close(); err != nil {
		panic(err)
	}
	// Persist creation of the journal entry before crossing the effect boundary.
	directory, err := os.Open(root)
	if err != nil {
		panic(err)
	}
	if err = directory.Sync(); err != nil {
		panic(err)
	}
	if err = directory.Close(); err != nil {
		panic(err)
	}
	if len(plan.Actions()) != 1 {
		panic("preflight did not allow a proposal")
	}
	file, err = os.OpenFile(resource, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o600)
	if err != nil {
		panic(err)
	}
	if _, err = file.WriteString("install-42"); err != nil {
		panic(err)
	}
	if err = file.Sync(); err != nil {
		panic(err)
	}
	if err = file.Close(); err != nil {
		panic(err)
	}

	// Simulate losing the effect acknowledgement and returning to the operation
	// with progress loaded from disk. Unknown evidence cannot cause a replay.
	data, err = os.ReadFile(journal)
	if err != nil {
		panic(err)
	}
	progress = installation.Progress{}
	if err = json.Unmarshal(data, &progress); err != nil {
		panic(err)
	}
	uncertain := []installation.Observation{{RequirementID: "private.marker", State: installation.Unknown}}
	resumed, err := plan.Recheck(profile, uncertain, support, progress)
	if err != nil {
		panic(err)
	}
	fmt.Println(resumed.Status())
	if len(resumed.Actions()) != 0 {
		panic("unknown acknowledgement permitted a replay")
	}

	// Exact product-owned verification resolves the existing effect. No second
	// create is performed; a mere not-found read would still require a fence.
	verified, err := plan.Recheck(profile, []installation.Observation{inspect()}, support, progress)
	if err != nil {
		panic(err)
	}
	fmt.Println(verified.Status())
	fmt.Println("created once")
}
Output:
changes-required
reconciliation-required
ready
created once

func (*Plan) Status

func (p *Plan) Status() PlanStatus

type PlanStatus

type PlanStatus string
const (
	Ready                  PlanStatus = "ready"
	ChangesRequired        PlanStatus = "changes-required"
	OperatorActionRequired PlanStatus = "operator-action-required"
	ReconciliationRequired PlanStatus = "reconciliation-required"
	UnsupportedPlan        PlanStatus = "unsupported"
)

type Profile

type Profile struct {
	Binding      Binding
	Requirements []Requirement
}

type Progress

type Progress struct {
	PlanID   string
	Attempts []Attempt
}

Progress is product-owned durable evidence bound to the original plan. Persist Attempted before a mutation, then Verified only after actual postflight. A new empty Progress is valid only when no operation has previously started.

type Reason

type Reason string
const (
	ReasonVerified             Reason = "verified"
	ReasonMissing              Reason = "missing"
	ReasonUnverified           Reason = "unverified"
	ReasonUnsupported          Reason = "unsupported"
	ReasonAdapterRequired      Reason = "adapter-required"
	ReasonAuthorityRequired    Reason = "authority-required"
	ReasonReconcileRequired    Reason = "reconciliation-required"
	ReasonVerificationLost     Reason = "verification-lost"
	ReasonDefinitelyNotApplied Reason = "definitely-not-applied"
)

type Requirement

type Requirement struct {
	ID       string
	Required bool
}

Requirement order is the product's intended action order. Context entries (Required=false) never create actions or block required outcomes.

type Support

type Support struct {
	RequirementID string
	AdapterID     string
	Authorized    bool
}

Support is the selected adapter's current explicit support/authority assertion. Products must derive Authorized from their real deployment authority boundary; this declaration is not itself a permission or executable callback.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL