package appconfig import "strings" // Agent-owned vars (parent spec §7.3): CARLOS_PURGE is set by the platform // when the box has a cache and REMOVED when it does not — the removal half // is load-bearing, since the env layers are tenant-supplied and a tenant // advertising a purge channel that does not exist would have apps skip // their own invalidation and serve stale content indefinitely. // CARLOS_CONTROL_SOCKET is stripped for the stronger reason: a // tenant-chosen socket path is a name the agent never vouched for. This // mirrors the exec backend's withoutVars (internal/activator/backend_exec.go); // the two lists must not drift. // // The sync appends CARLOS_PURGE=header UNCONDITIONALLY because cache // construction is unconditional in carlos agent (internal/host/agent.go, // edge.NewCache at startup) — every box this sync runs on serves through // that cache. The day the cache grows an off switch, this must learn to // read it. func agentOwned(name string) bool { return name == "CARLOS_PURGE" || name == "CARLOS_CONTROL_SOCKET" } // applyAgentVars rewrites one rendered env file: tenant-supplied // agent-owned vars are dropped and the agent's own CARLOS_PURGE=header is // appended last (last assignment wins under systemd EnvironmentFile, so // even a hand-staged earlier line could not override it — but we strip // those anyway). Deterministic and idempotent, so syncHost's bytes.Equal // convergence check still short-circuits the steady state. // // RenderEnv (materialize.go) emits exactly one `KEY=value\n` line per var, // keys sorted, and nothing else — no comments, no blank lines — so dropping // blank lines here is a no-op against its actual output; it's kept because a // transformation over line-oriented KEY=value content should not depend on // its input never containing one. func applyAgentVars(rendered []byte) []byte { var out strings.Builder for _, line := range strings.Split(string(rendered), "\n") { s := strings.TrimSpace(line) if s == "" { continue } if name, _, ok := strings.Cut(s, "="); ok && agentOwned(strings.TrimSpace(name)) { continue } out.WriteString(line) out.WriteByte('\n') } out.WriteString("CARLOS_PURGE=header\n") return []byte(out.String()) }