Help us shape the future of Go! Take the 2026 Go Developer Survey

deploy

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Oct 20, 2026 License: MIT Imports: 18 Imported by: 2

Documentation

Overview

Package deploy assembles a deployment from a resource+agent document: the Document DTO (resources forming a dependency DAG, agents binding them) and the Builder, in three phases.

  • Builder.Plan is pure. It resolves every kind to a factory, validates that every dependency a document supplies is declared, checks the DAG is acyclic, and checks that every dependency edge is type-correct — a resource factory's build type (or a container's item type, or an external's injected type) must satisfy the consumer's resource.Requirement. Every problem is reported at once, and nothing is constructed, so a configuration error is diagnosed without side effects.
  • Plan.Build constructs resources in topological order. Dependency types are already proven, so the only conversions left are the typed reads inside factories and Resolve/Collect at the call site. On failure every value built so far is closed in reverse order.
  • Builder.Wire attaches observers and hooks, binds agents and hands the assembled deployment to resources implementing Binder.

Builder.Build is Plan plus Build, and Builder.Deploy adds Wire. A caller that wants a configuration preflight calls Plan directly — it needs a registry containing every kind the document names.

Two opt-outs make an edge check conditional: a factory that instantiates Factory[any], and a dependency declared with Requirement.Type == any. Both are for test doubles and plugin hosts that forward a value they do not model; such a contract is then enforced where the value is read.

Document and DAG shape errors (a malformed entry, a cycle) are reported one at a time, because the later checks need a resolved order; every dependency edge is reported together.

The package owns only two concrete resource kinds: the secret stores it assembles first (so their scheme can serve every other settings subtree) and nothing else. Everything kind-specific arrives through the registry.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BindAgent added in v0.1.11

func BindAgent(
	ctx context.Context,
	reg *resource.Registry,
	result *Result,
	loader *resource.Loader,
	resolver *resource.ReferenceResolver,
	secrets *resource.SecretResolver,
	name string,
	def agent.Definition,
) (*agent.Agent, error)

BindAgent assembles one agent.Agent from its Definition: the engine from the registry (kind = EngineRef.Kind / Impl), then each hook under "hook.<slot>", wiring resource.Wireable hooks before recording them on the agent. It never mutates result (result.agents stays untouched), so callers decide where the assembled agent is recorded — deployment build records it on the Result, while core/runtime registers it in the live agent registry.

On failure, every component already constructed (engine and any wired hooks) is closed in reverse order so a partial assembly never leaks attached subscriptions or other resources.

func Collect added in v0.9.0

func Collect[T any](result *Result) []T

Collect returns every built value that is a T, in construction order. It is the typed form of "walk the deployment and keep what implements X", used by post-build attachment passes that do not care which resource it was.

func LoadLayers

func LoadLayers(ctx context.Context, layers []Layer) (Document, Provenance, error)

LoadLayers loads every layer with its own loader (file refs resolve against that layer's BaseDir), merges them in ascending priority order, and returns the merged document plus provenance.

func Resolve added in v0.9.0

func Resolve[T any](result *Result, name string) (T, error)

Resolve returns the built value registered under name, narrowed to T.

It is the typed read internal wiring should use: the caller names the type it expects, so a mismatch is reported once, here, instead of being re-asserted at every call site. Result.Value stays for host-level discovery, where the resource name comes from configuration and no type is known at compile time. Resolve is a free function because Go has no generic methods.

Types

type Binder added in v0.9.0

type Binder interface {
	BindDeployment(view View) error
}

Binder is implemented by resource values that need the fully assembled deployment after every resource and agent has been wired: backends registering into hosts, directories snapshotting the agent set.

Binder lives in deploy — the package that calls it — rather than in core/resource, because its argument is the assembly layer's view and only this package can name it. That keeps core/resource free of agent and deployment concepts.

type Builder

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

Builder constructs a deployment's resources from a Document using an explicit resource.Registry. The registry is owned by the caller; Builder never touches global state.

func NewBuilder

func NewBuilder(registry *resource.Registry, opts ...BuilderOption) *Builder

NewBuilder returns a Builder over registry. A nil registry yields an empty one.

func (*Builder) Build

func (b *Builder) Build(ctx context.Context, doc Document) (*Result, error)

Build validates the document, resolves the resource DAG, and constructs every resource in dependency order. On failure, values already built are closed in reverse construction order (rollback).

Build is Builder.Plan plus Plan.Build: a caller that wants to report configuration errors before constructing anything (a preflight, a CLI --check, a config validator) calls Plan directly.

func (*Builder) Deploy

func (b *Builder) Deploy(ctx context.Context, doc Document) (*Result, error)

Deploy is the convenience entry point: Build, then Wire. On wire failure every built value is closed before returning.

func (*Builder) Plan added in v0.9.0

func (b *Builder) Plan(doc Document) (*Plan, error)

Plan validates doc against the builder's registry without constructing anything.

Every problem is reported at once, joined into one error, because a deployment document is authored as a whole: fixing one dep at a time is the slow path for both humans and models.

func (*Builder) Wire

func (b *Builder) Wire(ctx context.Context, result *Result, doc Document) error

Wire runs the post-build wiring phase: resource values implementing resource.Wireable attach themselves (observers to buses, hooks to streams), then every agent is bound from its engine and hooks, and finally values implementing Binder receive the assembled deployment (agents included). Wire never participates in the construction DAG, so observed values never depend on their observers.

type BuilderOption

type BuilderOption func(*Builder)

BuilderOption configures a Builder.

func WithExternalResources added in v0.2.6

func WithExternalResources(externals []ExternalResource) BuilderOption

WithExternalResources configures the builder with caller-owned dependency values. Values are copied by reference: deploy never closes or mutates them. Multiple calls accumulate; duplicates are rejected at Build time.

func WithLoader

func WithLoader(loader *resource.Loader) BuilderOption

WithLoader supplies the deployment-level loader used to materialize settings subtrees that are {"file":…} / {"embed":…} references, and passed to factories for their own source resolution.

func WithResolver added in v0.2.0

func WithResolver(resolver *resource.ReferenceResolver) BuilderOption

WithResolver adds custom schemes to the expansion used for inline ${scheme:ref} strings in every settings subtree (resources, agent engine, agent hooks) before a factory decodes them. The custom schemes are merged on top of the all-open default resolver (env + home, plus base when the loader declares a base directory); a custom scheme with the same name overrides the built-in one.

func WithSecretCacheCapacity added in v0.8.0

func WithSecretCacheCapacity(n int) BuilderOption

WithSecretCacheCapacity bounds how many secret names the lazy-lookup cache holds at once. Zero keeps the default (see resource.WithCacheCapacity); raise it for a deployment that resolves more distinct names than that within one TTL window, so they are not re-fetched from the backend.

func WithSecretCacheTTL added in v0.2.0

func WithSecretCacheTTL(ttl time.Duration) BuilderOption

WithSecretCacheTTL sets how long lazy secret lookups reuse a cached value before hitting the backend again. Zero keeps the default (one minute); a negative value disables caching.

type Document

type Document struct {
	Version   string                      `json:"version,omitempty"`
	Resources resource.Resources          `json:"resources,omitempty"`
	Agents    map[string]agent.Definition `json:"agents,omitempty"`
	// Runtime is an application-runtime-owned configuration subtree.
	// Parse preserves it verbatim so the runtime layer can decode it
	// strictly without weakening strictness for the deployment schema.
	Runtime *resource.Opaque `json:"runtime,omitempty"`
}

Document is the top-level deployment DTO: a versioned resource area (the DAG) plus an agent area binding resources.

func Parse

func Parse(data []byte) (Document, error)

Parse decodes one deployment document from YAML or JSON into a Document. It applies the shared strict semantics of core/utils: JSON is detected by the Kubernetes rule (first non-whitespace byte is an open brace), YAML is converted strictly, unknown fields are rejected, and a trailing document is an error. After decoding, Parse validates the document semantically (version, resources, agents).

func (Document) Validate

func (d Document) Validate() error

Validate checks the document without resolving any factory.

type External added in v0.2.6

type External struct {
	// Name is the resource-ref name used in document deps, e.g. "db".
	Name string `json:"name"`
	// Contract is the author's declared contract name, e.g. "db.Pool". It is
	// document-facing: it travels with the deployment model so a reviewer can
	// read what an injected value is meant to be. The check the assembly
	// layer actually enforces is [ExternalResource.Type], derived from the
	// injected Go value, so a stale contract string can never admit a
	// mismatched value.
	Contract string `json:"contract"`
}

External declares one caller-owned dependency supplied to the deployment builder. Externals are visible to dependency resolution but are never constructed, wired, or closed by deploy.

func (External) Validate added in v0.2.6

func (e External) Validate() error

Validate checks the static fields of an External declaration.

type ExternalResource added in v0.2.6

type ExternalResource struct {
	External
	Value any
	Type  reflect.Type
}

ExternalResource pairs one External declaration with the caller-owned value that satisfies it, plus the value's Go type.

Type is recorded at injection time and is what the plan checks a consuming factory's resource.Requirement against. It is the value's concrete type, so injecting a *tool.Registry satisfies a dependency that requires the tool.Source interface.

func NewExternal added in v0.9.0

func NewExternal(name, contract string, value any) ExternalResource

NewExternal records one caller-owned dependency together with its Go type. Injection sites use this constructor rather than a struct literal so an external can never enter the builder without a type to check against.

func (ExternalResource) Validate added in v0.2.6

func (e ExternalResource) Validate() error

Validate checks the declaration and rejects nil values.

type Layer

type Layer struct {
	Priority int
	Name     string
	Source   resource.Source
	BaseDir  string
	Embed    fs.FS
}

Layer is one configuration layer. Layers are merged in ascending Priority order, so a higher priority layer overrides a lower one. The lowest-priority layer must be a complete document (with version); later layers may be partial.

type LayerRef

type LayerRef struct {
	Name     string `json:"name,omitempty"`
	Priority int    `json:"priority"`
}

LayerRef identifies the layer that provided a merged key.

type Named added in v0.9.0

type Named[T any] struct {
	Name  string
	Value T
}

Named is one built value together with the resource name it was registered under.

func CollectNamed added in v0.9.0

func CollectNamed[T any](result *Result) []Named[T]

CollectNamed is Collect with the resource names kept, for passes whose diagnostics have to say which resource caused a problem (a duplicate binder, for example).

type Plan added in v0.9.0

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

Plan is a deployment document checked against a registry: every kind resolves to a factory, every dependency a document supplies is declared by that factory, every required dependency is present, the resource graph is acyclic, and every dependency edge is type-correct.

Nothing is constructed and no side effect is performed. Plan is therefore the place a configuration error is reported — one run lists every bad edge instead of the first — and the place a validator (a CLI preflight, the flowcraft-config skill) can run without touching the network, the filesystem or a sandbox.

func (*Plan) Build added in v0.9.0

func (p *Plan) Build(ctx context.Context) (*Result, error)

Build constructs every resource and binds every agent from the checked plan. On failure, values already built are closed in reverse construction order (rollback).

type Provenance

type Provenance struct {
	Resources map[string]LayerRef `json:"resources"`
	Agents    map[string]LayerRef `json:"agents"`
}

Provenance records which layer provided each top-level resource and agent in the merged document: the highest-priority layer that declared the key.

type Result

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

Result owns the built resource values in construction order. The caller closes it (or the runtime layer closes it) when done.

func (*Result) Agent

func (r *Result) Agent(name string) (*agent.Agent, bool)

Agent returns the assembled agent registered under name.

func (*Result) AgentNames

func (r *Result) AgentNames() []string

AgentNames returns the sorted bound agent names.

func (*Result) Close

func (r *Result) Close() error

Close closes every io.Closer resource value in reverse construction order, then closes every bound agent (engine and lifecycle hooks).

func (*Result) Detach added in v0.1.14

func (r *Result) Detach(names ...string)

Detach marks resource names as caller-owned: Result.Close will not close them, and the caller takes over their lifecycle. It is used by the runtime to reject a reload whose event bus factory returned the current generation's bus without letting the aborted result close a bus another generation still uses.

func (*Result) Names

func (r *Result) Names() []string

Names returns the sorted built resource names.

func (*Result) Resolver added in v0.4.10

func (r *Result) Resolver() *resource.ReferenceResolver

Resolver returns the effective settings reference resolver assembled by Build. Callers that construct values after the build (for example a runtime registering dynamic agents) pass it back into BindAgent so dynamic settings expand exactly like deployment-time settings.

func (*Result) Secrets added in v0.4.10

func (r *Result) Secrets() *resource.SecretResolver

Secrets returns the effective secret resolver assembled by Build, or nil when the deployment declared no secret stores.

func (*Result) Value

func (r *Result) Value(name string) (any, bool)

Value returns the built resource registered under name, or false.

It is the dynamic accessor for host-level discovery, where the resource name comes from configuration and the caller cannot name the type at compile time (core/runtime.Runtime.Resource and delegation/hostwrap both need that). Core wiring uses Resolve and Collect, which name the type.

type View added in v0.9.0

type View interface {
	// Agent returns the assembled agent registered under name.
	Agent(name string) (*agent.Agent, bool)
	// AgentNames returns the sorted bound agent names.
	AgentNames() []string
	// Names returns the sorted built resource names.
	Names() []string
}

View is the read-only view of an assembled deployment handed to resource values after every resource and agent exists.

It is deliberately narrow: a resource that needs to look something up after assembly declares exactly what it reads, and deploy hands it this view instead of the mutable Result. Consumers narrow it structurally — a package that only needs agent lookup declares its own two-method interface and the view satisfies it.

Jump to

Keyboard shortcuts

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