package main import ( "context" "crypto/ed25519" "errors" "path/filepath" "github.com/novox/mesh-host/internal/apply" "github.com/novox/mesh-host/internal/declaration" "github.com/novox/mesh-host/internal/filling" "github.com/novox/mesh-host/internal/store" "github.com/novox/mesh-host/internal/system" ) // serveFilling writes filled files on the provisioner client's request for as long as this engine serves (novox/hq // ADR 0349 §4). A socket that cannot be served is said once, and this engine goes on applying. func serveFilling(ctx context.Context, opts options, signer ed25519.PublicKey, say func(string)) { if err := filling.Serve(ctx, filling.Socket, fillingEngine(opts, signer, say), say); err != nil { say("filled files are not written on request on this machine: " + err.Error()) } } // fillingEngine is the write of a filled file as this engine does it: the declaration it keeps, verified, read under // the apply's lock — the same two locks every apply takes, the process's and the one beside the state — and the file // written through the apply (apply.WriteFilled), its outcome recorded in the node's state as an apply's is. func fillingEngine(opts options, signer ed25519.PublicKey, say func(string)) *filling.Engine { return &filling.Engine{ Declared: func() (*declaration.Declaration, error) { raw, err := store.LoadDeclared(store.DeclaredPath(opts.state), signer) if err != nil { return nil, err } return declaration.Parse(raw) }, UIDOf: filling.UIDOf, Lock: func() (func(), error) { applying.Lock() unlock, err := store.Lock(opts.state, nil) if err != nil { applying.Unlock() return nil, err } return func() { unlock(); applying.Unlock() }, nil }, Place: func(ctx context.Context, d *declaration.Declaration, path string, content []byte) (string, string, error) { built, err := system.For(builtFor) if err != nil { return "", "", err } known, err := store.Load(opts.state) if err != nil { return "", "", err } report, updated, writeErr := apply.WriteFilled(ctx, built, d, known, path, content, apply.ExecRunner, announceOr(say), apply.KeepIn(filepath.Dir(opts.state)), apply.WindowsIn(filepath.Dir(opts.state))) // Saved whichever way it went, as an apply's: what was written is on the machine either way. if err := store.Save(opts.state, updated); err != nil { return "", "", errors.Join(writeErr, errors.New("written, and the node's state could not be saved: "+ err.Error())) } action, digest := "", "" for _, o := range report.Outcomes { if o.Target == path && o.Type == string(declaration.TypeFile) { action = o.Action } } if rec, ok := updated.At(string(declaration.TypeFile), path); ok { digest = rec.Wrote } return action, digest, writeErr }, } }