carlos / platform Public

console: resolve an instance to one replica prefix, refusing before S3

The first half of the carlos db door (spec 2026-09-02 §6): an instance
record becomes the activator's own prefix, or a refusal. Another region,
a fleet placement, a pool and a hostile or mismatched host are all
refused with no store in reach, so none of them can become an S3 call.

Config gains the CARLOS_HIBERNATE_* triple and CARLOS_REGION.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev c85bbd2da8515f87ff47fa1ed57fb7582897b97b parent 98db0e0
3 files changed, +183 −0
  • internal/console/apidb.go +57 −0
  • internal/console/apidb_test.go +111 −0
  • internal/console/server.go +15 −0
diff --git a/internal/console/apidb.go b/internal/console/apidb.go
new file mode 100644
index 000000000..ea29755e6
--- /dev/null
+++ b/internal/console/apidb.go
@@ -0,0 +1,57 @@
+// The `carlos db` replica door (spec 2026-09-02 §6). It serves an
+// instance's replicated LTX files BY COORDINATE (level plus TXID span) from
+// the region data bucket, to a member of the account and nobody else, so the
+// CLI can restore a private copy on the member's own machine. The console
+// never executes tenant SQL and never holds a restored database.
+//
+// resolveReplica is the half that makes no S3 call: resolving an instance
+// record to one replica prefix, and refusing everything that is not this
+// console's to read. Every refusal there happens BEFORE the bucket is
+// touched. That is deliberate: a door that tried and mapped AccessDenied
+// would leak the attempt, the latency, and eventually a bucket name into a
+// log a member can provoke.
+
+package console
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/carlosframework/platform/internal/activator"
+ "github.com/carlosframework/platform/internal/provision"
+ "github.com/carlosframework/platform/internal/registry"
+)
+
+var (
+ // ErrResidency: the data is in another region's bucket, or on a
+ // customer's own fleet box. IAM makes those reads impossible by design,
+ // so this is the design working rather than a fault to retry.
+ ErrResidency = errors.New("db: the instance's data is outside this region")
+ // ErrPooled: a pooled record's data lives on the pool box, the same
+ // class the instance-move worker refuses.
+ ErrPooled = errors.New("db: pooled instances have no replica of their own")
+ // ErrBadHost: path.Join cleans "../x" to "x" rather than refusing it,
+ // so a host becomes a key segment only after ValidHostShape, and only
+ // when it is the host the record itself names.
+ ErrBadHost = errors.New("db: invalid host")
+)
+
+func (s *Server) resolveReplica(rec provision.Record, host string) (string, error) {
+ if !provision.ValidHostShape(host) || rec.Host != host {
+ return "", fmt.Errorf("%w: %q", ErrBadHost, host)
+ }
+ if rec.Pool != "" {
+ return "", fmt.Errorf("%w: %s is in pool %q", ErrPooled, host, rec.Pool)
+ }
+ // A fleet placement is checked on its own, not left to the region
+ // compare: the data is on the customer's box whatever this console's
+ // CARLOS_REGION says.
+ if _, fleet := registry.FleetPlacement(rec.Region); fleet {
+ return "", fmt.Errorf("%w: %s is placed on fleet %q", ErrResidency, host, rec.Region)
+ }
+ if rec.Region != s.cfg.Region {
+ return "", fmt.Errorf("%w: %s is homed in %q, this console serves %q",
+ ErrResidency, host, rec.Region, s.cfg.Region)
+ }
+ return activator.HibernatePrefix(s.cfg.HibernatePrefix, host), nil
+}
diff --git a/internal/console/apidb_test.go b/internal/console/apidb_test.go
new file mode 100644
index 000000000..cc5fea1ea
--- /dev/null
+++ b/internal/console/apidb_test.go
@@ -0,0 +1,111 @@
+package console
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/carlosframework/platform/internal/provision"
+)
+
+// Every one of these is refused BEFORE any S3 call. A door that reached the
+// bucket and mapped AccessDenied would leak both latency and the fact that
+// a bucket was tried. resolveReplica takes no store at all, so it cannot.
+func TestResolveReplicaRefusesBeforeTouchingS3(t *testing.T) {
+ s := &Server{}
+ s.cfg.Region = "eu-west-1"
+ s.cfg.HibernatePrefix = "instances"
+
+ cases := []struct {
+ name string
+ rec provision.Record
+ host string
+ want error
+ }{
+ {
+ name: "another region",
+ rec: provision.Record{Host: "a.bab.oncarlos.com", Region: "us-east-1"},
+ host: "a.bab.oncarlos.com",
+ want: ErrResidency,
+ },
+ {
+ name: "the home region seen from a regional console",
+ rec: provision.Record{Host: "a.bab.oncarlos.com"},
+ host: "a.bab.oncarlos.com",
+ want: ErrResidency,
+ },
+ {
+ name: "fleet-homed",
+ rec: provision.Record{Host: "a.bab.oncarlos.com", Region: "fleet/acme"},
+ host: "a.bab.oncarlos.com",
+ want: ErrResidency,
+ },
+ {
+ name: "pooled",
+ rec: provision.Record{Host: "a.bab.oncarlos.com", Region: "eu-west-1", Pool: "shared"},
+ host: "a.bab.oncarlos.com",
+ want: ErrPooled,
+ },
+ {
+ name: "hostile host",
+ rec: provision.Record{Host: "../../etc", Region: "eu-west-1"},
+ host: "../../etc",
+ want: ErrBadHost,
+ },
+ {
+ name: "record for another host",
+ rec: provision.Record{Host: "b.bab.oncarlos.com", Region: "eu-west-1"},
+ host: "a.bab.oncarlos.com",
+ want: ErrBadHost,
+ },
+ }
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ _, err := s.resolveReplica(tc.rec, tc.host)
+ if !errors.Is(err, tc.want) {
+ t.Fatalf("resolveReplica = %v, want %v", err, tc.want)
+ }
+ })
+ }
+}
+
+// A fleet placement is refused even on a console whose own region string
+// happened to match it: the data is on the customer's box, whatever a
+// misconfigured CARLOS_REGION says.
+func TestResolveReplicaRefusesAFleetEvenWhenTheRegionMatches(t *testing.T) {
+ s := &Server{}
+ s.cfg.Region = "fleet/acme"
+ s.cfg.HibernatePrefix = "instances"
+ if _, err := s.resolveReplica(provision.Record{Host: "a.bab.oncarlos.com", Region: "fleet/acme"}, "a.bab.oncarlos.com"); !errors.Is(err, ErrResidency) {
+ t.Fatalf("resolveReplica = %v, want ErrResidency", err)
+ }
+}
+
+// The happy path is an in-region, non-pooled instance, and the prefix is
+// the activator's own layout, not a string this package invents.
+func TestResolveReplicaBuildsTheActivatorPrefix(t *testing.T) {
+ s := &Server{}
+ s.cfg.Region = "eu-west-1"
+ s.cfg.HibernatePrefix = "instances"
+ got, err := s.resolveReplica(
+ provision.Record{Host: "a.bab.oncarlos.com", Region: "eu-west-1"},
+ "a.bab.oncarlos.com")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got != "instances/a.bab.oncarlos.com" {
+ t.Fatalf("prefix = %q, want instances/a.bab.oncarlos.com", got)
+ }
+}
+
+// A single-region deployment leaves both empty, and "" == "" must be a
+// match rather than a refusal, otherwise the door never works anywhere
+// that has not set CARLOS_REGION. Home-region records keep region "" too
+// (Config.HomeRegion), so this is also the flagship's home console.
+func TestResolveReplicaTreatsEmptyRegionsAsAMatch(t *testing.T) {
+ s := &Server{}
+ s.cfg.HibernatePrefix = "instances"
+ if _, err := s.resolveReplica(
+ provision.Record{Host: "a.bab.oncarlos.com"}, "a.bab.oncarlos.com"); err != nil {
+ t.Fatalf("single-region deployment refused: %v", err)
+ }
+}
diff --git a/internal/console/server.go b/internal/console/server.go
index db0eea533..7f91d2001 100644
--- a/internal/console/server.go
+++ b/internal/console/server.go
@@ -482,6 +482,21 @@ type Config struct {
// and nothing about placement changes when it is set. "" is legal: the
// regions endpoint then reports no home row and an empty default.
HomeRegion string
+ // HibernateBucket/Region/Prefix name the region data bucket the
+ // `carlos db` door reads (apidb.go; spec 2026-09-02 §6, Option A): the
+ // same CARLOS_HIBERNATE_* triple the activator on this box writes
+ // instances/<host>/ with, read through activator.ConfigFromEnv so the
+ // two cannot parse it differently. Read-only, in-region, LTX files by
+ // coordinate only. An empty bucket disables the door (503) rather than
+ // failing it open.
+ HibernateBucket string
+ HibernateRegion string
+ HibernatePrefix string
+ // Region is this console's own placement (CARLOS_REGION, the value the
+ // box agent beside it reconciles with), compared against an instance
+ // record's Region before any S3 call. Empty on the home region, whose
+ // records keep region "" too (see HomeRegion).
+ Region string
ReleasePub ed25519.PublicKey // pinned channel-pointer verification key
// Key signs channel pointers for promote/rollback (CARLOS_RELEASE_KEY).
// nil is a legal deployment: the console runs read-only, the promotion