func run(newCfg *Config, start bool) error {
	var err error
	if newCfg == nil {
		newCfg = new(Config)
	}
	ctx, cancel := NewContext(Context{Context: context.Background(), cfg: newCfg})
	defer func() {
		if err != nil {
			cancel()
			if currentCfg != nil {
				certmagic.Default.Storage = currentCfg.storage
			}
		}
	}()
	newCfg.cancelFunc = cancel  
	if newCfg.Logging == nil {
		newCfg.Logging = new(Logging)
	}
	err = newCfg.Logging.openLogs(ctx)
	if err != nil {
		return err
	}
	if start {
		err = replaceLocalAdminServer(newCfg)
		if err != nil {
			return fmt.Errorf("starting caddy administration endpoint: %v", err)
		}
	}
	newCfg.apps = make(map[string]App)
	err = func() error {
		if newCfg.StorageRaw != nil {
			val, err := ctx.LoadModule(newCfg, "StorageRaw")
			if err != nil {
				return fmt.Errorf("loading storage module: %v", err)
			}
			stor, err := val.(StorageConverter).CertMagicStorage()
			if err != nil {
				return fmt.Errorf("creating storage value: %v", err)
			}
			newCfg.storage = stor
		}
		if newCfg.storage == nil {
			newCfg.storage = DefaultStorage
		}
		certmagic.Default.Storage = newCfg.storage
		return nil
	}()
	if err != nil {
		return err
	}
	err = func() error {
		for appName := range newCfg.AppsRaw {
			if _, err := ctx.App(appName); err != nil {
				return err
			}
		}
		return nil
	}()
	if err != nil {
		return err
	}
	if !start {
		return nil
	}
	err = func() error {
		var started []string
		for name, a := range newCfg.apps {
			err := a.Start()
			if err != nil {
				for _, otherAppName := range started {
					err2 := newCfg.apps[otherAppName].Stop()
					if err2 != nil {
						err = fmt.Errorf("%v; additionally, aborting app %s: %v",
							err, otherAppName, err2)
					}
				}
				return fmt.Errorf("%s app module: start: %v", name, err)
			}
			started = append(started, name)
		}
		return nil
	}()
	if err != nil {
		return err
	}
	return finishSettingUp(ctx, newCfg)
}
run runs newCfg and starts all its apps if
start is true. If any errors happen, cleanup
is performed if any modules were provisioned;
apps that were started already will be stopped,
so this function should not leak resources if
an error is returned. However, if no error is
returned and start == false, you should cancel
the config if you are not going to start it,
so that each provisioned module will be
cleaned up.

This is a low-level function; most callers
will want to use Run instead, which also
updates the config's raw state.
package caddy
import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"runtime/debug"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/caddyserver/caddy/v2/notify"
	"github.com/caddyserver/certmagic"
	"github.com/google/uuid"
	"go.uber.org/zap"
)
type Config struct {
	Admin   *AdminConfig `json:"admin,omitempty"`
	Logging *Logging     `json:"logging,omitempty"`
	StorageRaw json.RawMessage `json:"storage,omitempty" caddy:"namespace=caddy.storage inline_key=module"`
	AppsRaw ModuleMap `json:"apps,omitempty" caddy:"namespace="`
	apps    map[string]App
	storage certmagic.Storage
	cancelFunc context.CancelFunc
}
type App interface {
	Start() error
	Stop() error
}
func Run(cfg *Config) error {
	cfgJSON, err := json.Marshal(cfg)
	if err != nil {
		return err
	}
	return Load(cfgJSON, true)
}
func Load(cfgJSON []byte, forceReload bool) error {
	if err := notify.NotifyReloading(); err != nil {
		Log().Error("unable to notify reloading to service manager", zap.Error(err))
	}
	defer func() {
		if err := notify.NotifyReadiness(); err != nil {
			Log().Error("unable to notify readiness to service manager", zap.Error(err))
		}
	}()
	return changeConfig(http.MethodPost, "/"+rawConfigKey, cfgJSON, forceReload)
}
func changeConfig(method, path string, input []byte, forceReload bool) error {
	switch method {
	case http.MethodGet,
		http.MethodHead,
		http.MethodOptions,
		http.MethodConnect,
		http.MethodTrace:
		return fmt.Errorf("method not allowed")
	}
	currentCfgMu.Lock()
	defer currentCfgMu.Unlock()
	err := unsyncedConfigAccess(method, path, input, nil)
	if err != nil {
		return err
	}
	newCfg, err := json.Marshal(rawCfg[rawConfigKey])
	if err != nil {
		return APIError{
			HTTPStatus: http.StatusBadRequest,
			Err:        fmt.Errorf("encoding new config: %v", err),
		}
	}
	if !forceReload && bytes.Equal(rawCfgJSON, newCfg) {
		Log().Named("admin.api").Info("config is unchanged")
		return nil
	}
	idx := make(map[string]string)
	err = indexConfigObjects(rawCfg[rawConfigKey], "/"+rawConfigKey, idx)
	if err != nil {
		return APIError{
			HTTPStatus: http.StatusInternalServerError,
			Err:        fmt.Errorf("indexing config: %v", err),
		}
	}
	err = unsyncedDecodeAndRun(newCfg, true)
	if err != nil {
		if len(rawCfgJSON) > 0 {
			var oldCfg interface{}
			err2 := json.Unmarshal(rawCfgJSON, &oldCfg)
			if err2 != nil {
				err = fmt.Errorf("%v; additionally, restoring old config: %v", err, err2)
			}
			rawCfg[rawConfigKey] = oldCfg
		}
		return fmt.Errorf("loading new config: %v", err)
	}
	rawCfgJSON = newCfg
	rawCfgIndex = idx
	return nil
}
func readConfig(path string, out io.Writer) error {
	currentCfgMu.RLock()
	defer currentCfgMu.RUnlock()
	return unsyncedConfigAccess(http.MethodGet, path, nil, out)
}
func indexConfigObjects(ptr interface{}, configPath string, index map[string]string) error {
	switch val := ptr.(type) {
	case map[string]interface{}:
		for k, v := range val {
			if k == idKey {
				switch idVal := v.(type) {
				case string:
					index[idVal] = configPath
				case float64:  
					index[fmt.Sprintf("%v", idVal)] = configPath
				default:
					return fmt.Errorf("%s: %s field must be a string or number", configPath, idKey)
				}
				continue
			}
			err := indexConfigObjects(val[k], path.Join(configPath, k), index)
			if err != nil {
				return err
			}
		}
	case []interface{}:
		for i := range val {
			err := indexConfigObjects(val[i], path.Join(configPath, strconv.Itoa(i)), index)
			if err != nil {
				return err
			}
		}
	}
	return nil
}
func unsyncedDecodeAndRun(cfgJSON []byte, allowPersist bool) error {
	strippedCfgJSON := RemoveMetaFields(cfgJSON)
	var newCfg *Config
	err := strictUnmarshalJSON(strippedCfgJSON, &newCfg)
	if err != nil {
		return err
	}
	if !allowPersist &&
		newCfg != nil &&
		newCfg.Admin != nil &&
		newCfg.Admin.Config != nil &&
		newCfg.Admin.Config.LoadRaw != nil &&
		newCfg.Admin.Config.LoadInterval <= 0 {
		return fmt.Errorf("recursive config loading detected: pulled configs cannot pull other configs without positive load_interval")
	}
	err = run(newCfg, true)
	if err != nil {
		return err
	}
	oldCfg := currentCfg
	currentCfg = newCfg
	unsyncedStop(oldCfg)
	if allowPersist &&
		newCfg != nil &&
		(newCfg.Admin == nil ||
			newCfg.Admin.Config == nil ||
			newCfg.Admin.Config.Persist == nil ||
			*newCfg.Admin.Config.Persist) {
		dir := filepath.Dir(ConfigAutosavePath)
		err := os.MkdirAll(dir, 0700)
		if err != nil {
			Log().Error("unable to create folder for config autosave",
				zap.String("dir", dir),
				zap.Error(err))
		} else {
			err := os.WriteFile(ConfigAutosavePath, cfgJSON, 0600)
			if err == nil {
				Log().Info("autosaved config (load with --resume flag)", zap.String("file", ConfigAutosavePath))
			} else {
				Log().Error("unable to autosave config",
					zap.String("file", ConfigAutosavePath),
					zap.Error(err))
			}
		}
	}
	return nil
}