Files
Elydora-Go-SDK/cmd/elydora/plugins/runtime_files.go
T

146 lines
3.6 KiB
Go

package plugins
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
)
const (
maxProtectedSecretBytes = 64 * 1024
maxRuntimeConfigBytes = 512 * 1024
)
type agentRuntimeConfig struct {
OrgID string `json:"org_id"`
AgentID string `json:"agent_id"`
KID string `json:"kid"`
BaseURL string `json:"base_url"`
Token string `json:"token"`
AgentName string `json:"agent_name"`
}
// WriteRuntimeFileAtomic replaces one managed runtime file through a flushed,
// same-directory temporary file.
func WriteRuntimeFileAtomic(
path string,
label string,
content []byte,
mode os.FileMode,
) error {
if err := validateRuntimeFileTarget(path, label); err != nil {
return err
}
change, err := prepareFileChange(path, label, content, mode)
if err != nil {
return err
}
return writeChanges([]*fileChange{change}, "write "+label, os.Rename)
}
// GenerateHookScript atomically commits the agent config, private key, and
// self-contained Node.js audit runtime.
func GenerateHookScript(destPath string, config InstallConfig) error {
return generateHookScriptWithRename(destPath, config, os.Rename)
}
func generateHookScriptWithRename(
destPath string,
config InstallConfig,
rename renameFunc,
) error {
agentDirectory, err := ResolveAgentRuntimeDirectory(config.AgentID)
if err != nil {
return err
}
baseURL := config.BaseURL
if baseURL == "" {
baseURL = "https://api.elydora.com"
}
encodedConfig, err := json.MarshalIndent(agentRuntimeConfig{
OrgID: config.OrgID,
AgentID: config.AgentID,
KID: config.KID,
BaseURL: baseURL,
Token: config.Token,
AgentName: config.AgentName,
}, "", " ")
if err != nil {
return fmt.Errorf("encode Elydora runtime config: %w", err)
}
encodedConfig = append(encodedConfig, '\n')
if len(encodedConfig) > maxRuntimeConfigBytes {
return fmt.Errorf(
"Elydora runtime config exceeds %d bytes after JSON encoding",
maxRuntimeConfigBytes,
)
}
items := []struct {
path string
label string
content []byte
mode os.FileMode
}{
{
path: filepath.Join(agentDirectory, "config.json"), label: "Elydora runtime config",
content: encodedConfig, mode: 0600,
},
{
path: filepath.Join(agentDirectory, "private.key"), label: "Elydora private key",
content: []byte(config.PrivateKey), mode: 0600,
},
{
path: destPath, label: "Elydora audit runtime",
content: []byte(buildHookScript(config.AgentName, config.AgentID)), mode: 0700,
},
}
for _, item := range items {
if err := validateRuntimeFileTarget(item.path, item.label); err != nil {
return err
}
}
if _, err := PrepareAgentRuntimeDirectory(config.AgentID); err != nil {
return err
}
if err := EnsurePrivateDirectory(filepath.Dir(destPath)); err != nil {
return fmt.Errorf("create hook script directory: %w", err)
}
changes := make([]*fileChange, 0, len(items))
for _, item := range items {
change, prepareErr := prepareFileChange(
item.path,
item.label,
item.content,
item.mode,
)
if prepareErr != nil {
return prepareErr
}
changes = append(changes, change)
}
if err := writeChanges(changes, "write Elydora agent runtime", rename); err != nil {
return fmt.Errorf("commit Elydora agent runtime: %w", err)
}
return nil
}
func validateRuntimeFileTarget(path, label string) error {
info, err := os.Lstat(path)
if errors.Is(err, os.ErrNotExist) {
return nil
}
if err != nil {
return fmt.Errorf("inspect %s at %s: %w", label, path, err)
}
if info.Mode()&os.ModeSymlink != 0 || !info.Mode().IsRegular() {
return fmt.Errorf("%s path is not a physical file: %s", label, path)
}
return nil
}