carlos / platform Public

activator: mint CARLOS_INSTANCE_KEY create-only into <host>.env, durable on every call (L21)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by claude agent, operated by paul@keymail.dev 4c2e881aed5272972d3ec56eeda70f1a27a3f180 parent 6509c2d
2 files changed, +666 −0
  • internal/activator/instancekey.go +329 −0
  • internal/activator/instancekey_test.go +337 −0
diff --git a/internal/activator/instancekey.go b/internal/activator/instancekey.go
new file mode 100644
index 000000000..2ab5ae99f
--- /dev/null
+++ b/internal/activator/instancekey.go
@@ -0,0 +1,329 @@
+package activator
+
+// The instance key (region HA request L21; rastrillo/rastrillo #66). An app
+// on CARLOS seals its data with a key. Before this file the app minted that
+// key itself after it started, so a box that died before the key reached a
+// backup left a database nothing could unseal. The platform now mints the
+// key on the box, into the instance's own <host>.env as
+// CARLOS_INSTANCE_KEY, before the app's first start, and never rewrites it.
+// rastrillo's carlos.InstanceKey reads it verbatim when the app has no key
+// of its own.
+//
+// The rules, each a ruling in
+// docs/superpowers/plans/2026-10-10-instance-key-provision.md:
+// - create-only: a key already in the file is never rewritten, whatever
+// its shape. A malformed one is reported and left in place, as
+// EnsureAdminToken does with a malformed token;
+// - the decision is made on the effective env (instancekey.Effective,
+// the rule mergeEnv applies): a key the rendered configuration
+// supplies is the app's key and nothing is minted; an empty own value
+// is no key;
+// - durable every time: before an ensure reports success the file is
+// 0600 (fchmod on its descriptor), the file is fsynced and so is its
+// directory, when the key was already there too. A sync that failed
+// once is redone by the next ensure, never skipped because the bytes
+// are already there;
+// - the value is never logged, never put in an error, and never leaves
+// the file except in the launch environment.
+
+import (
+ "crypto/rand"
+ "encoding/base64"
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/carlosframework/platform/internal/instancefs"
+ "github.com/carlosframework/platform/internal/instancekey"
+ "github.com/carlosframework/platform/internal/registry"
+)
+
+// InstanceKeyVar is the variable the key is delivered as.
+const InstanceKeyVar = instancekey.Var
+
+// instanceKeyBytes is the key's length before encoding: 32 CSPRNG bytes,
+// standard base64 with padding, 44 characters. rastrillo uses the exact
+// bytes the env file yields, so this encoding is fixed for the life of
+// every key minted with it; changing it is a rotation.
+const instanceKeyBytes = 32
+
+// InstanceKeyOutcome is what one ensure did.
+type InstanceKeyOutcome int
+
+const (
+ // InstanceKeyPresent: the own env file already carried a key. No byte
+ // was written; the file was made durable.
+ InstanceKeyPresent InstanceKeyOutcome = iota
+ // InstanceKeyMinted: a key was appended and made durable.
+ InstanceKeyMinted
+ // InstanceKeyFromConfig: the rendered configuration supplies the key.
+ // Nothing was written.
+ InstanceKeyFromConfig
+ // InstanceKeyNoEnvFile: the backfill found no own env file, so the host
+ // has never been started here. Nothing was written; its first start
+ // mints the key.
+ InstanceKeyNoEnvFile
+ // InstanceKeyReplica: a replica row (#162). A replica is never minted
+ // a key: it may hold only its home's (ruling 26, replicaKeyGate).
+ InstanceKeyReplica
+)
+
+func (o InstanceKeyOutcome) String() string {
+ switch o {
+ case InstanceKeyPresent:
+ return "present"
+ case InstanceKeyMinted:
+ return "minted"
+ case InstanceKeyFromConfig:
+ return "from-config"
+ case InstanceKeyNoEnvFile:
+ return "no-env-file"
+ case InstanceKeyReplica:
+ return "replica"
+ }
+ return fmt.Sprintf("outcome(%d)", int(o))
+}
+
+// instanceKeySync is the durability step's two syncs, a seam so a test can
+// fail each one. Read and replaced only under adminTokenMu.
+var instanceKeySync = struct {
+ file func(*os.File) error
+ dir func(string) error
+}{file: (*os.File).Sync, dir: syncDir}
+
+// syncDir fsyncs a directory, so an entry created in it survives a power
+// cut.
+func syncDir(dir string) error {
+ d, err := os.Open(dir)
+ if err != nil {
+ return err
+ }
+ err = d.Sync()
+ if cerr := d.Close(); err == nil {
+ err = cerr
+ }
+ return err
+}
+
+func mintInstanceKey() (string, error) {
+ raw := make([]byte, instanceKeyBytes)
+ if _, err := rand.Read(raw); err != nil {
+ return "", err
+ }
+ return base64.StdEncoding.EncodeToString(raw), nil
+}
+
+// wellFormedInstanceKey reports whether key has the shape this file mints.
+// A key that fails it is not necessarily broken (an operator may have
+// written one by hand), which is why a failure is a warning, never a
+// rotation.
+func wellFormedInstanceKey(key string) bool {
+ if len(key) != base64.StdEncoding.EncodedLen(instanceKeyBytes) {
+ return false
+ }
+ raw, err := base64.StdEncoding.Strict().DecodeString(key)
+ return err == nil && len(raw) == instanceKeyBytes
+}
+
+// configEnvName is the rendered configuration's file name beside an own env
+// file: <host>.env's sibling <host>.config.env, the pair pathsFor names.
+func configEnvName(envName string) string {
+ return strings.TrimSuffix(envName, ".env") + ".config.env"
+}
+
+// EnsureInstanceKey makes sure path (an instance's own <host>.env on the
+// flat layout) carries CARLOS_INSTANCE_KEY, creating the file if needed.
+// Launch paths call it right after EnsureAdminToken.
+func EnsureInstanceKey(path string) (InstanceKeyOutcome, error) {
+ return ensureInstanceKey(filepath.Dir(path), filepath.Base(path), false, true)
+}
+
+// EnsureInstanceKeyBeneath is EnsureInstanceKey for a nested row's own env
+// file, read and written through instancefs because the directory is
+// tenant-writable (isolated-exec design §3.2).
+func EnsureInstanceKeyBeneath(dir, name string) (InstanceKeyOutcome, error) {
+ return ensureInstanceKey(dir, name, true, true)
+}
+
+// ensureInstanceKeyFor is the launch paths' ensure for rt: none for a
+// replica row, the nested body for a nested row, the flat one otherwise.
+func (b *ExecBackend) ensureInstanceKeyFor(rt registry.Route, p instancePaths) (InstanceKeyOutcome, error) {
+ if rt.Replica {
+ return InstanceKeyReplica, nil
+ }
+ if p.layout == LayoutNested {
+ return EnsureInstanceKeyBeneath(p.dataDir, rt.Host+".env")
+ }
+ return EnsureInstanceKey(p.envFile)
+}
+
+// BackfillInstanceKey is the startup backfill's step for one row: a key is
+// appended only to an own env file that already exists. A host with no file
+// has never been started on this box, and its first start mints the key;
+// creating a file here could also occupy the path a move's install expects
+// to be empty (moveslandinstall.go placeStaged). A row this backend would
+// refuse to start (fenced, conflict) is an error and nothing is written.
+func (b *ExecBackend) BackfillInstanceKey(rt registry.Route) (InstanceKeyOutcome, error) {
+ if rt.Replica {
+ return InstanceKeyReplica, nil
+ }
+ p := b.pathsFor(rt)
+ switch p.layout {
+ case LayoutNested:
+ return ensureInstanceKey(p.dataDir, rt.Host+".env", true, false)
+ case LayoutFenced, LayoutConflict:
+ return InstanceKeyPresent, fmt.Errorf("%s: not backfilled: %s", rt.Host, p.reason)
+ }
+ return ensureInstanceKey(p.dataDir, rt.Host+".env", false, false)
+}
+
+// openInstanceEnv opens an own env file for append. Only a create passes
+// a mode: openat2 refuses a non-zero mode without O_CREAT (EINVAL), and an
+// existing file's mode is set by the fchmod that follows anyway.
+func openInstanceEnv(dir, name string, beneath, create bool) (*os.File, error) {
+ flags, perm := os.O_APPEND|os.O_WRONLY, os.FileMode(0)
+ if create {
+ flags, perm = flags|os.O_CREATE, 0o600
+ }
+ if beneath {
+ return instancefs.OpenBeneath(dir, name, flags, perm)
+ }
+ return os.OpenFile(filepath.Join(dir, name), flags, perm)
+}
+
+// settleInstanceEnv is the durability step every successful ensure ends
+// with: 0600 on the descriptor, then the file, then its directory. Caller
+// holds adminTokenMu.
+func settleInstanceEnv(f *os.File, dir, path string) error {
+ if err := f.Chmod(0o600); err != nil {
+ return fmt.Errorf("chmod %s: %w", path, err)
+ }
+ if err := instanceKeySync.file(f); err != nil {
+ return fmt.Errorf("sync %s: %w", path, err)
+ }
+ if err := instanceKeySync.dir(dir); err != nil {
+ return fmt.Errorf("sync %s: %w", dir, err)
+ }
+ return nil
+}
+
+// ensureInstanceKey is the shared body. It holds adminTokenMu, the lock
+// every mint into <host>.env takes, so a key append and a token append can
+// never interleave. create false is the backfill: an absent file is
+// InstanceKeyNoEnvFile and nothing is created.
+func ensureInstanceKey(dir, name string, beneath, create bool) (InstanceKeyOutcome, error) {
+ path := filepath.Join(dir, name)
+ adminTokenMu.Lock()
+ defer adminTokenMu.Unlock()
+ read := func(n string) ([]byte, error) {
+ if beneath {
+ return instancefs.ReadBeneathLimit(dir, n, 1<<20)
+ }
+ return os.ReadFile(filepath.Join(dir, n))
+ }
+ raw, err := read(name)
+ switch {
+ case errors.Is(err, os.ErrNotExist):
+ if !create {
+ return InstanceKeyNoEnvFile, nil
+ }
+ case err != nil:
+ return InstanceKeyPresent, fmt.Errorf("read %s: %w", path, err)
+ }
+ cfg, err := read(configEnvName(name))
+ if err != nil && !errors.Is(err, os.ErrNotExist) {
+ return InstanceKeyPresent, fmt.Errorf("read %s: %w", filepath.Join(dir, configEnvName(name)), err)
+ }
+ // The decision is the effective env's: the key the launch will hand
+ // the app, by the same rule mergeEnv applies.
+ key, layer := instancekey.Effective(parseEnvBytes(cfg), parseEnvBytes(raw))
+ switch layer {
+ case instancekey.LayerConfig:
+ logPerFile("key-config", path, fmt.Sprintf("activator: %s is set by the app's configuration for %s: the platform mints none while it is", InstanceKeyVar, path))
+ return InstanceKeyFromConfig, nil
+ case instancekey.LayerOwn:
+ if !wellFormedInstanceKey(key) {
+ logPerFile("key-shape", path, fmt.Sprintf("activator: %s in %s is not the shape the platform mints (44 base64 characters, 32 bytes): left in place, because the app may already have sealed data with it", InstanceKeyVar, path))
+ }
+ // Present is not a no-op: a mint whose sync failed left these
+ // bytes behind, and this call is the retry that makes them
+ // durable before anything is started with them.
+ f, err := openInstanceEnv(dir, name, beneath, false)
+ if err != nil {
+ return InstanceKeyPresent, fmt.Errorf("open %s: %w", path, err)
+ }
+ defer f.Close()
+ if err := settleInstanceEnv(f, dir, path); err != nil {
+ return InstanceKeyPresent, err
+ }
+ return InstanceKeyPresent, nil
+ }
+ minted, err := mintInstanceKey()
+ if err != nil {
+ return InstanceKeyPresent, fmt.Errorf("mint instance key for %s: %w", path, err)
+ }
+ f, err := openInstanceEnv(dir, name, beneath, create)
+ if errors.Is(err, os.ErrNotExist) && !create {
+ return InstanceKeyNoEnvFile, nil // removed since the read: nothing to backfill
+ }
+ if err != nil {
+ return InstanceKeyPresent, fmt.Errorf("open %s: %w", path, err)
+ }
+ defer f.Close()
+ prefix := ""
+ if len(raw) > 0 && raw[len(raw)-1] != '\n' {
+ prefix = "\n"
+ }
+ if _, err := fmt.Fprintf(f, "%s%s=%s\n", prefix, InstanceKeyVar, minted); err != nil {
+ return InstanceKeyPresent, fmt.Errorf("write %s: %w", path, err)
+ }
+ if err := settleInstanceEnv(f, dir, path); err != nil {
+ return InstanceKeyPresent, err
+ }
+ return InstanceKeyMinted, nil
+}
+
+// errInstanceEnvNeedsWrite is settleClaimInstanceEnv's answer for a file
+// whose mode is not 0600: making it so is a write, which the claim path
+// does only under its guard.
+var errInstanceEnvNeedsWrite = errors.New("activator: the instance env file needs its mode set")
+
+// settleClaimInstanceEnv is the claim launch source's durability step for
+// an env file that already carries what the launch needs: it makes the
+// file and its directory durable and writes nothing. fsync changes no
+// byte, mode or name, so it is not a host write and runs without the
+// claim's guard. A file that is not 0600 is errInstanceEnvNeedsWrite, and
+// the caller takes the guarded path, whose ensure sets the mode.
+func (b *ExecBackend) settleClaimInstanceEnv(rt registry.Route) error {
+ p := b.pathsFor(rt)
+ name := rt.Host + ".env"
+ adminTokenMu.Lock()
+ defer adminTokenMu.Unlock()
+ var f *os.File
+ var err error
+ if p.layout == LayoutNested {
+ f, err = instancefs.OpenBeneath(p.dataDir, name, os.O_RDONLY, 0)
+ } else {
+ f, err = os.Open(p.envFile)
+ }
+ if err != nil {
+ return fmt.Errorf("open %s: %w", p.envFile, err)
+ }
+ defer f.Close()
+ st, err := f.Stat()
+ if err != nil {
+ return fmt.Errorf("stat %s: %w", p.envFile, err)
+ }
+ if st.Mode().Perm() != 0o600 {
+ return errInstanceEnvNeedsWrite
+ }
+ if err := instanceKeySync.file(f); err != nil {
+ return fmt.Errorf("sync %s: %w", p.envFile, err)
+ }
+ if err := instanceKeySync.dir(p.dataDir); err != nil {
+ return fmt.Errorf("sync %s: %w", p.dataDir, err)
+ }
+ return nil
+}
diff --git a/internal/activator/instancekey_test.go b/internal/activator/instancekey_test.go
new file mode 100644
index 000000000..263d8a9dd
--- /dev/null
+++ b/internal/activator/instancekey_test.go
@@ -0,0 +1,337 @@
+package activator
+
+import (
+ "bytes"
+ "encoding/base64"
+ "errors"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+ "testing"
+
+ "github.com/carlosframework/platform/internal/registry"
+)
+
+// keyLines returns every CARLOS_INSTANCE_KEY value in raw, in order.
+func keyLines(raw []byte) []string {
+ var out []string
+ for _, line := range strings.Split(string(raw), "\n") {
+ if v, ok := strings.CutPrefix(line, InstanceKeyVar+"="); ok {
+ out = append(out, v)
+ }
+ }
+ return out
+}
+
+// syncProbe counts the key's syncs and fails them on demand. Set the
+// error fields only between calls.
+type syncProbe struct {
+ file, dir int
+ fileErr, dirErr error
+}
+
+func probeInstanceKeySync(t *testing.T) *syncProbe {
+ t.Helper()
+ p := &syncProbe{}
+ adminTokenMu.Lock()
+ old := instanceKeySync
+ instanceKeySync.file = func(f *os.File) error {
+ p.file++
+ if p.fileErr != nil {
+ return p.fileErr
+ }
+ return f.Sync()
+ }
+ instanceKeySync.dir = func(d string) error {
+ p.dir++
+ if p.dirErr != nil {
+ return p.dirErr
+ }
+ return syncDir(d)
+ }
+ adminTokenMu.Unlock()
+ t.Cleanup(func() {
+ adminTokenMu.Lock()
+ instanceKeySync = old
+ adminTokenMu.Unlock()
+ })
+ return p
+}
+
+func TestEnsureInstanceKeyMintsOnceInTheAgreedFormat(t *testing.T) {
+ p := filepath.Join(t.TempDir(), "shop.example.com.env")
+ got, err := EnsureInstanceKey(p)
+ if err != nil || got != InstanceKeyMinted {
+ t.Fatalf("first ensure = %v, %v; want minted", got, err)
+ }
+ raw, _ := os.ReadFile(p)
+ keys := keyLines(raw)
+ if len(keys) != 1 {
+ t.Fatalf("file holds %d keys, want 1", len(keys))
+ }
+ dec, err := base64.StdEncoding.Strict().DecodeString(keys[0])
+ if err != nil || len(dec) != 32 || len(keys[0]) != 44 {
+ t.Fatalf("key is not 32 bytes in padded standard base64 (len %d, decode err %v)", len(keys[0]), err)
+ }
+ if !bytes.HasSuffix(raw, []byte("\n")) {
+ t.Fatal("the key line has no newline: the next append would join it")
+ }
+ if st, _ := os.Stat(p); st.Mode().Perm() != 0o600 {
+ t.Fatalf("mode = %o, want 600", st.Mode().Perm())
+ }
+ // The parser every launch path uses yields the exact characters minted.
+ env, _ := readEnvFile(p)
+ if env[InstanceKeyVar] != keys[0] {
+ t.Fatal("readEnvFile does not yield the minted key verbatim")
+ }
+ again, err := EnsureInstanceKey(p)
+ if err != nil || again != InstanceKeyPresent {
+ t.Fatalf("second ensure = %v, %v; want present", again, err)
+ }
+ raw2, _ := os.ReadFile(p)
+ if !bytes.Equal(raw, raw2) {
+ t.Fatal("second ensure changed the file")
+ }
+}
+
+// Never overwrite: a key already there, of any shape, is kept byte for byte
+// and only reported.
+func TestEnsureInstanceKeyNeverRewritesAnExistingKey(t *testing.T) {
+ for _, existing := range []string{
+ "CARLOS_INSTANCE_KEY=hand-written-not-base64\n",
+ "A=1\nCARLOS_INSTANCE_KEY=" + strings.Repeat("A", 43) + "=", // well formed, no trailing newline
+ } {
+ p := filepath.Join(t.TempDir(), "shop.example.com.env")
+ if err := os.WriteFile(p, []byte(existing), 0o600); err != nil {
+ t.Fatal(err)
+ }
+ var buf bytes.Buffer
+ log.SetOutput(&buf)
+ got, err := EnsureInstanceKey(p)
+ log.SetOutput(os.Stderr)
+ if err != nil || got != InstanceKeyPresent {
+ t.Fatalf("ensure over %q = %v, %v; want present", existing, got, err)
+ }
+ if raw, _ := os.ReadFile(p); string(raw) != existing {
+ t.Fatalf("file changed: %q -> %q", existing, raw)
+ }
+ if strings.Contains(buf.String(), "hand-written-not-base64") {
+ t.Fatal("the shape warning logged the key's value")
+ }
+ }
+}
+
+func TestEnsureInstanceKeyAppendsOnAFreshLineAndKeepsEveryOtherLine(t *testing.T) {
+ p := filepath.Join(t.TempDir(), "shop.example.com.env")
+ before := "CARLOS_ADMIN_TOKEN=" + strings.Repeat("a", 64) + "\n# comment\nDATABASE_URL=sqlite" // no trailing newline
+ if err := os.WriteFile(p, []byte(before), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ if got, err := EnsureInstanceKey(p); err != nil || got != InstanceKeyMinted {
+ t.Fatalf("ensure = %v, %v", got, err)
+ }
+ raw, _ := os.ReadFile(p)
+ if !strings.HasPrefix(string(raw), before+"\n"+InstanceKeyVar+"=") {
+ t.Fatalf("file = %q: the key must land on its own line after the untouched original", raw)
+ }
+ env, _ := readEnvFile(p)
+ if env["DATABASE_URL"] != "sqlite" || len(env["CARLOS_ADMIN_TOKEN"]) != 64 {
+ t.Fatal("an existing line was lost")
+ }
+ if st, _ := os.Stat(p); st.Mode().Perm() != 0o600 {
+ t.Fatalf("mode = %o, want the 0644 file tightened to 600", st.Mode().Perm())
+ }
+}
+
+// A key the app's configuration supplies is the app's key: minting one into
+// <host>.env would override it (mergeEnv) and change the key under its data.
+// The decision is the effective env's, so an empty CARLOS_INSTANCE_KEY= in
+// the own file does not hide the configured key (finding 4).
+func TestEnsureInstanceKeyDefersToTheRenderedConfiguration(t *testing.T) {
+ for _, own := range []string{"A=1\n", "A=1\n" + InstanceKeyVar + "=\n"} {
+ dir := t.TempDir()
+ p := filepath.Join(dir, "shop.example.com.env")
+ if err := os.WriteFile(filepath.Join(dir, "shop.example.com.config.env"), []byte(InstanceKeyVar+"=from-config\n"), 0o600); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(p, []byte(own), 0o600); err != nil {
+ t.Fatal(err)
+ }
+ got, err := EnsureInstanceKey(p)
+ if err != nil || got != InstanceKeyFromConfig {
+ t.Fatalf("own %q: ensure = %v, %v; want from-config", own, got, err)
+ }
+ if raw, _ := os.ReadFile(p); string(raw) != own {
+ t.Fatalf("own file changed: %q", raw)
+ }
+ }
+}
+
+func TestBackfillInstanceKeyAppendsOnlyToAnExistingEnvFile(t *testing.T) {
+ b := &ExecBackend{DataDir: t.TempDir()}
+ fresh := registry.Route{Host: "new.example.com", Socket: "/run/carlos/new.sock"}
+ got, err := b.BackfillInstanceKey(fresh)
+ if err != nil || got != InstanceKeyNoEnvFile {
+ t.Fatalf("backfill with no env file = %v, %v; want no-env-file", got, err)
+ }
+ if _, err := os.Lstat(filepath.Join(b.DataDir, fresh.Host+".env")); !os.IsNotExist(err) {
+ t.Fatalf("backfill created an env file: %v", err)
+ }
+ old := registry.Route{Host: "shop.example.com", Socket: "/run/carlos/shop.sock"}
+ envPath := filepath.Join(b.DataDir, old.Host+".env")
+ tok := "CARLOS_ADMIN_TOKEN=" + strings.Repeat("b", 64) + "\n"
+ if err := os.WriteFile(envPath, []byte(tok), 0o600); err != nil {
+ t.Fatal(err)
+ }
+ if got, err := b.BackfillInstanceKey(old); err != nil || got != InstanceKeyMinted {
+ t.Fatalf("backfill = %v, %v; want minted", got, err)
+ }
+ raw, _ := os.ReadFile(envPath)
+ if !strings.HasPrefix(string(raw), tok) || len(keyLines(raw)) != 1 {
+ t.Fatalf("file = %q", raw)
+ }
+ // Idempotent: a second backfill (the next agent start) writes nothing.
+ if got, err := b.BackfillInstanceKey(old); err != nil || got != InstanceKeyPresent {
+ t.Fatalf("second backfill = %v, %v; want present", got, err)
+ }
+ if raw2, _ := os.ReadFile(envPath); !bytes.Equal(raw, raw2) {
+ t.Fatal("second backfill changed the file")
+ }
+}
+
+// Review focus 1: a hand-edited file with two keys. Readers take the last
+// (parseEnvBytes), and the ensure writes nothing.
+func TestEnsureInstanceKeyLeavesAFileWithTwoKeysAlone(t *testing.T) {
+ p := filepath.Join(t.TempDir(), "shop.example.com.env")
+ body := InstanceKeyVar + "=first\n" + InstanceKeyVar + "=second\n"
+ if err := os.WriteFile(p, []byte(body), 0o600); err != nil {
+ t.Fatal(err)
+ }
+ if got, err := EnsureInstanceKey(p); err != nil || got != InstanceKeyPresent {
+ t.Fatalf("ensure = %v, %v; want present", got, err)
+ }
+ if raw, _ := os.ReadFile(p); string(raw) != body {
+ t.Fatalf("file changed: %q", raw)
+ }
+ if env, _ := readEnvFile(p); env[InstanceKeyVar] != "second" {
+ t.Fatalf("readers see %q, want the last line's value", env[InstanceKeyVar])
+ }
+}
+
+// Review focus 2: an empty value is no key. One is appended after it, and
+// readers take the appended one.
+func TestEnsureInstanceKeyTreatsAnEmptyValueAsAbsent(t *testing.T) {
+ p := filepath.Join(t.TempDir(), "shop.example.com.env")
+ if err := os.WriteFile(p, []byte(InstanceKeyVar+"=\n"), 0o600); err != nil {
+ t.Fatal(err)
+ }
+ if got, err := EnsureInstanceKey(p); err != nil || got != InstanceKeyMinted {
+ t.Fatalf("ensure = %v, %v; want minted", got, err)
+ }
+ env, _ := readEnvFile(p)
+ if !wellFormedInstanceKey(env[InstanceKeyVar]) {
+ t.Fatal("readers do not see the appended key")
+ }
+}
+
+// Review focus 3: a seed, a visitor's wake and the backfill can ask at
+// once. Exactly one key is written.
+func TestEnsureInstanceKeyMintsOneKeyUnderConcurrentCallers(t *testing.T) {
+ p := filepath.Join(t.TempDir(), "shop.example.com.env")
+ var wg sync.WaitGroup
+ for i := 0; i < 8; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ if _, err := EnsureInstanceKey(p); err != nil {
+ t.Error(err)
+ }
+ }()
+ }
+ wg.Wait()
+ if raw, _ := os.ReadFile(p); len(keyLines(raw)) != 1 {
+ t.Fatalf("%d keys written", len(keyLines(raw)))
+ }
+}
+
+// Review focus 5: an empty CARLOS_INSTANCE_KEY in the rendered
+// configuration supplies nothing, so the platform mints one.
+func TestEnsureInstanceKeyMintsWhenTheConfigurationValueIsEmpty(t *testing.T) {
+ dir := t.TempDir()
+ if err := os.WriteFile(filepath.Join(dir, "shop.example.com.config.env"), []byte(InstanceKeyVar+"=\n"), 0o600); err != nil {
+ t.Fatal(err)
+ }
+ if got, err := EnsureInstanceKey(filepath.Join(dir, "shop.example.com.env")); err != nil || got != InstanceKeyMinted {
+ t.Fatalf("ensure = %v, %v; want minted", got, err)
+ }
+}
+
+// Finding 2: a mint whose file or directory sync fails reports failure,
+// and so does every retry until a sync succeeds. The retry finds the key
+// present, keeps its bytes, and still completes the chmod and both syncs
+// before it reports success.
+func TestEnsureInstanceKeyRedoesAFailedSyncBeforeReportingSuccess(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ fail func(*syncProbe, error)
+ }{
+ {"file sync", func(p *syncProbe, err error) { p.fileErr = err }},
+ {"directory sync", func(p *syncProbe, err error) { p.dirErr = err }},
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ probe := probeInstanceKeySync(t)
+ p := filepath.Join(t.TempDir(), "shop.example.com.env")
+ tc.fail(probe, errors.New("injected: EIO"))
+ if _, err := EnsureInstanceKey(p); err == nil {
+ t.Fatal("a mint whose sync failed reported success")
+ }
+ raw, _ := os.ReadFile(p)
+ if len(keyLines(raw)) != 1 {
+ t.Fatalf("the failed mint left %d keys, want its 1", len(keyLines(raw)))
+ }
+ if _, err := EnsureInstanceKey(p); err == nil {
+ t.Fatal("a retry reported success while the sync still fails")
+ }
+ if err := os.Chmod(p, 0o644); err != nil {
+ t.Fatal(err)
+ }
+ tc.fail(probe, nil)
+ before := *probe
+ got, err := EnsureInstanceKey(p)
+ if err != nil || got != InstanceKeyPresent {
+ t.Fatalf("retry = %v, %v; want present", got, err)
+ }
+ if probe.file != before.file+1 || probe.dir != before.dir+1 {
+ t.Fatalf("the retry synced the file %d and the directory %d times, want once each", probe.file-before.file, probe.dir-before.dir)
+ }
+ if raw2, _ := os.ReadFile(p); !bytes.Equal(raw, raw2) {
+ t.Fatal("a retry rewrote the key")
+ }
+ if st, _ := os.Stat(p); st.Mode().Perm() != 0o600 {
+ t.Fatalf("mode = %o, want the present path to set 600", st.Mode().Perm())
+ }
+ })
+ }
+}
+
+// Ruling 26: a replica row is never minted a key, on any path; the only
+// key it may hold is its home's (replicaKeyGate).
+func TestReplicaRowsGetNoPlatformKey(t *testing.T) {
+ b := &ExecBackend{DataDir: t.TempDir()}
+ rt := registry.Route{Host: "shop-au.example.com", Kind: "instance", Socket: "/run/carlos/shop-au.sock", Replica: true}
+ envPath := filepath.Join(b.DataDir, rt.Host+".env")
+ if err := os.WriteFile(envPath, []byte("CARLOS_ADMIN_TOKEN=t\n"), 0o600); err != nil {
+ t.Fatal(err)
+ }
+ if got, err := b.ensureInstanceKeyFor(rt, b.pathsFor(rt)); err != nil || got != InstanceKeyReplica {
+ t.Fatalf("launch ensure = %v, %v; want replica", got, err)
+ }
+ if got, err := b.BackfillInstanceKey(rt); err != nil || got != InstanceKeyReplica {
+ t.Fatalf("backfill = %v, %v; want replica", got, err)
+ }
+ if raw, _ := os.ReadFile(envPath); len(keyLines(raw)) != 0 {
+ t.Fatal("a replica row was given a platform key")
+ }
+}