File size: 5,018 Bytes
857a91b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package config

import (
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

// Defaults returns a Config populated with mode-aware defaults.
func Defaults(mode Mode) *Config {
	if !mode.Valid() {
		mode = ModeLocalDev
	}
	maxConcurrent := 2
	switch mode {
	case ModeHFSpace:
		maxConcurrent = 1
	case ModeCustomerAgent, ModeCloudWorker:
		maxConcurrent = 5
	}
	dataDir := "/var/lib/matrix-runtime"
	if mode == ModeLocalDev {
		if home, err := os.UserHomeDir(); err == nil {
			dataDir = filepath.Join(home, ".matrix", "runtime", "data")
		}
	}
	return &Config{
		Mode:                   mode,
		Port:                   8080,
		DataDir:                dataDir,
		MaxTTLSeconds:          600,
		MaxConcurrentJobs:      maxConcurrent,
		InstallTimeoutSeconds:  120,
		StartupTimeoutSeconds:  45,
		RPCTimeoutSeconds:      20,
		MaxLogBytes:            1024 * 1024,
		CloudURL:               "https://cloud.matrixhub.io",
		HFCacheDir:             filepath.Join(dataDir, "models", "huggingface"),
		DBPath:                 filepath.Join(dataDir, "matrixcloud.db"),
		DBSchema:               "matrixcloud",
		JobRetentionHours:      24,
		LogRetentionHours:      72,
		CleanupIntervalMinutes: 15,
		RateLimitRPM:           120,
	}
}

// FromEnv builds a Config from environment variables, layered on top of the
// mode-aware defaults. The mode is resolved from modeOverride (a CLI flag) when
// non-empty, otherwise from MATRIX_RUNTIME_MODE, otherwise local-dev.
func FromEnv(modeOverride string) *Config {
	mode := Mode(firstNonEmpty(modeOverride, os.Getenv("MATRIX_RUNTIME_MODE"), string(ModeLocalDev)))
	c := Defaults(mode)

	// Honor MATRIX_RUNTIME_PORT, else the PaaS convention $PORT (Cloud Run,
	// Render, Railway, Koyeb, Heroku, …) so hosted deploys work out of the box.
	if v := firstNonEmpty(os.Getenv("MATRIX_RUNTIME_PORT"), os.Getenv("PORT")); v != "" {
		if n, err := strconv.Atoi(v); err == nil {
			c.Port = n
		}
	}
	if v := os.Getenv("MATRIX_RUNTIME_DATA_DIR"); v != "" {
		c.DataDir = v
		c.HFCacheDir = filepath.Join(v, "models", "huggingface")
		c.DBPath = filepath.Join(v, "matrixcloud.db")
	}
	if v := os.Getenv("MATRIX_RUNTIME_DB_PATH"); v != "" {
		c.DBPath = v
	}
	if v := os.Getenv("MATRIX_RUNTIME_MAX_TTL_SECONDS"); v != "" {
		if n, err := strconv.Atoi(v); err == nil && n > 0 {
			c.MaxTTLSeconds = n
		}
	}
	if v := os.Getenv("MATRIX_RUNTIME_MAX_CONCURRENT_JOBS"); v != "" {
		if n, err := strconv.Atoi(v); err == nil && n > 0 {
			c.MaxConcurrentJobs = n
		}
	}
	c.RuntimeID = firstNonEmpty(os.Getenv("MATRIX_RUNTIME_ID"), c.RuntimeID)
	c.Workspace = firstNonEmpty(os.Getenv("MATRIX_RUNTIME_WORKSPACE"), c.Workspace)
	c.CloudURL = firstNonEmpty(os.Getenv("MATRIX_CLOUD_URL"), c.CloudURL)
	c.JoinToken = firstNonEmpty(os.Getenv("MATRIX_RUNTIME_JOIN_TOKEN"), c.JoinToken)
	c.APIToken = firstNonEmpty(os.Getenv("MATRIX_RUNTIME_API_TOKEN"), c.APIToken)
	c.HFToken = firstNonEmpty(os.Getenv("HF_TOKEN"), c.HFToken)
	if v := os.Getenv("MATRIX_RUNTIME_HF_CACHE_DIR"); v != "" {
		c.HFCacheDir = v
	}
	// PostgreSQL/Neon connection for the hosted control plane. Several common
	// env names are accepted; the first non-empty one wins. Never hardcode the
	// DSN — it carries credentials and must come from the environment.
	c.DatabaseURL = firstNonEmpty(
		os.Getenv("MATRIXCLOUD_DATABASE_URL"),
		os.Getenv("MATRIX_RUNTIME_DB_URL"),
		os.Getenv("DATABASE_URL"),
	)
	c.DBSchema = firstNonEmpty(os.Getenv("MATRIXCLOUD_DB_SCHEMA"), c.DBSchema, "matrixcloud")
	c.PublicURL = firstNonEmpty(os.Getenv("MATRIX_RUNTIME_PUBLIC_URL"), os.Getenv("MATRIXCLOUD_APP_URL"))
	if n, ok := envInt("MATRIX_RUNTIME_JOB_RETENTION_HOURS"); ok {
		c.JobRetentionHours = n
	}
	if n, ok := envInt("MATRIX_RUNTIME_LOG_RETENTION_HOURS"); ok {
		c.LogRetentionHours = n
	}
	if n, ok := envInt("MATRIX_RUNTIME_CLEANUP_INTERVAL_MINUTES"); ok {
		c.CleanupIntervalMinutes = n
	}
	if n, ok := envInt("MATRIX_RUNTIME_RATE_LIMIT_RPM"); ok {
		c.RateLimitRPM = n
	}

	// MatrixShell executes commands in a local sandbox: safe-by-default means it
	// is ON only in local-dev unless explicitly enabled. MATRIX_SHELL_ENABLED
	// (or MATRIX_RUNTIME_MATRIXSHELL_ENABLED) overrides in either direction.
	c.MatrixShellEnabled = mode == ModeLocalDev
	if v := firstNonEmpty(os.Getenv("MATRIX_RUNTIME_MATRIXSHELL_ENABLED"), os.Getenv("MATRIX_SHELL_ENABLED")); v != "" {
		c.MatrixShellEnabled = parseBool(v)
	}
	return c
}

// parseBool interprets common truthy/falsey strings; unknown values are false.
func parseBool(s string) bool {
	switch strings.ToLower(strings.TrimSpace(s)) {
	case "1", "true", "yes", "on", "enabled":
		return true
	default:
		return false
	}
}

// envInt reads an integer env var; ok is false when unset or unparseable.
func envInt(key string) (int, bool) {
	v := os.Getenv(key)
	if v == "" {
		return 0, false
	}
	n, err := strconv.Atoi(v)
	if err != nil {
		return 0, false
	}
	return n, true
}

func firstNonEmpty(vals ...string) string {
	for _, v := range vals {
		if v != "" {
			return v
		}
	}
	return ""
}