carlos / platform Public

settings-2 final review: the fleet store door serves a box its own account's promotion records

Astra final A3, ruling R17. aclRead grants GET of exactly
releases/<sqid>/<app>/promoted/<version> for the box's own account: five
segments, a valid app and version, nothing else under releases/. Listing
(aclDir, aclListScope) and writes (aclWrite) are unchanged. Without it
every record read as absent through the door, so a record-only build
could never stage on a fleet box.

Tests: ACL matrix allow/deny rows (another sqid, a sixth segment, a
missing version, the pointer-digests marker, writes, deletes, traversal),
a listing-denial test, and an end-to-end test through the real edge door
and fleetapi client: ReadPromotionRecord and AuthorizedManifest read an
own-account record, another account's is refused, and a replaced
manifest is ErrManifestDigest.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 0fc74c11894cf933d6c10cb013b2c64e5ec81ec0 parent 6f79b01
3 files changed, +135 −1
  • internal/edge/fleetapi_e2e_test.go +64 −0
  • internal/edge/storedoor.go +28 −1
  • internal/edge/storedoor_test.go +43 −0
diff --git a/internal/edge/fleetapi_e2e_test.go b/internal/edge/fleetapi_e2e_test.go
index 9f6e5e6d6..f6ab8c353 100644
--- a/internal/edge/fleetapi_e2e_test.go
+++ b/internal/edge/fleetapi_e2e_test.go
@@ -619,3 +619,67 @@ func TestFleetBoxCatchallYieldsToAnotherTenantsClaim(t *testing.T) {
t.Fatalf("aliases = %+v, want acme.%s derived for the unclaimed name", aliases, parent)
}
}
+
+// Settings spec §4.1, ruling R17: a fleet box reads its own account's
+// promotion records through the store door, so a build authorized by record
+// alone (no pointer names it) stages on a fleet box, and a manifest that does
+// not hash to the record's digest is refused. Another account's record and a
+// listing of the records stay out of reach, and a box cannot write one.
+func TestFleetBoxReadsItsOwnPromotionRecords(t *testing.T) {
+ ctx := context.Background()
+ w := newFleetDoorWorld(t)
+ const version = "v7"
+ for _, acct := range []string{w.sqid, w.other} {
+ if err := release.Ship(ctx, w.store, acct, release.Manifest{
+ App: fapiApp, Version: version, Kind: "binary",
+ Artifacts: map[string]release.Artifact{fapiPlatform: {}},
+ }, map[string][]byte{fapiPlatform: []byte("authorized bytes")}); err != nil {
+ t.Fatalf("Ship(%s): %v", acct, err)
+ }
+ _, raw, err := release.GetManifestRaw(ctx, w.store, acct, fapiApp, version)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := release.RootSigner(w.priv).WritePromotionRecord(ctx, w.store, release.PromotionRecord{
+ Account: acct, App: fapiApp, Version: version, ManifestDigest: release.ManifestDigest(raw), Authorized: true,
+ }); err != nil {
+ t.Fatalf("WritePromotionRecord(%s): %v", acct, err)
+ }
+ }
+ _, raw, err := release.GetManifestRaw(ctx, w.store, w.sqid, fapiApp, version)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ rec, err := release.ReadPromotionRecord(ctx, w.boxA, w.pub, w.sqid, fapiApp, version)
+ if err != nil || rec.ManifestDigest != release.ManifestDigest(raw) {
+ t.Fatalf("ReadPromotionRecord through the door = %+v, %v; want the record signing %s", rec, err, release.ManifestDigest(raw))
+ }
+ if m, err := release.AuthorizedManifest(ctx, w.boxA, w.pub, w.sqid, fapiApp, version); err != nil || m.Version != version {
+ t.Fatalf("AuthorizedManifest through the door = %+v, %v; want the record-only authorized %s", m, err, version)
+ }
+
+ if _, err := release.ReadPromotionRecord(ctx, w.boxA, w.pub, w.other, fapiApp, version); err == nil {
+ t.Fatal("a fleet box read another account's promotion record through the door")
+ }
+ keys, _ := w.boxA.List(ctx, "releases/"+w.sqid+"/")
+ for _, k := range keys {
+ if strings.HasPrefix(k, "releases/") {
+ t.Fatalf("a fleet box listed promotion records through the door: %v", keys)
+ }
+ }
+ if _, err := w.boxA.Put(ctx, release.PromotionRecordKey(w.sqid, fapiApp, "v8"), []byte("{}"), ""); err == nil {
+ t.Fatal("a fleet box wrote a promotion record through the door")
+ }
+
+ _, etag, err := w.store.Get(ctx, release.ManifestKey(w.sqid, fapiApp, version))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, err := w.store.Put(ctx, release.ManifestKey(w.sqid, fapiApp, version), append(raw, '\n'), etag); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := release.AuthorizedManifest(ctx, w.boxA, w.pub, w.sqid, fapiApp, version); !errors.Is(err, release.ErrManifestDigest) {
+ t.Fatalf("AuthorizedManifest over a replaced manifest = %v; want ErrManifestDigest", err)
+ }
+}
diff --git a/internal/edge/storedoor.go b/internal/edge/storedoor.go
index ca7c447d5..e341b9712 100644
--- a/internal/edge/storedoor.go
+++ b/internal/edge/storedoor.go
@@ -380,6 +380,9 @@ func canonicalPrefix(raw string) (string, bool) {
// the box (knownAccounts -> ParseRecordKey)
// apps/<sqid>/** its owning account's whole app estate
// fleets/<fleet>/** its own corner
+// releases/<sqid>/<app>/promoted/<version>
+// a promotion record of its owning account's
+// build — see promotionRecordRead
//
// domains/ is NOT here, and is served by its own two paths instead
// (serveClaim, serveClaimListing): a box can list every claimed hostname,
@@ -411,6 +414,20 @@ func canonicalPrefix(raw string) (string, bool) {
// NAMES are claimed at all, whoever by, so the listing stays whole
// (serveClaimListing). Write stays console-only: aclWrite never answers true
// for a domains/ key.
+//
+// A promotion record (settings spec §4.1, release.PromotionRecordKey) is
+// granted because a box needs it and it holds nothing secret: it is the
+// root-signed digest of one of the account's own builds, readable by anyone
+// holding the public key it verifies under. HeldAuthorized reads it before
+// staging a held release, and AuthorizedManifest before any install the
+// settings add. Without the grant every record reads as absent through the
+// door, so a build authorized by record alone could never stage on a fleet
+// box, and one a legacy pointer still names would fall back to that
+// pointer's weaker word. The grant is one object shape, not the prefix the
+// records sit under: releases/ also holds the account's pointer digest
+// marker, which a box has no use for. Listing is not granted (aclDir and
+// aclListScope are unchanged), and write stays console-only: aclWrite never
+// answers true for a releases/ key.
func aclRead(sqid, fleet, key string) bool {
if sqid == "" || fleet == "" {
return false
@@ -419,7 +436,7 @@ func aclRead(sqid, fleet, key string) bool {
case "control/serial", "bootstrap/carlos", release.LayoutIntentKey():
return true
}
- if systemReadKey(key) {
+ if systemReadKey(key) || promotionRecordRead(sqid, key) {
return true
}
return strings.HasPrefix(key, "accounts/"+sqid+"/") ||
@@ -427,6 +444,16 @@ func aclRead(sqid, fleet, key string) bool {
strings.HasPrefix(key, "fleets/"+fleet+"/")
}
+// promotionRecordRead is the one releases/ shape aclRead grants: exactly
+// releases/<sqid>/<app>/promoted/<version>, five segments, the box's own
+// account, and a valid app and version name (release.PromotionRecordKey's
+// inputs). canonicalKey has already refused any "." or ".." segment.
+func promotionRecordRead(sqid, key string) bool {
+ seg := strings.Split(key, "/")
+ return len(seg) == 5 && seg[0] == "releases" && seg[1] == sqid &&
+ release.ValidApp(seg[2]) && seg[3] == "promoted" && release.ValidVersion(seg[4])
+}
+
// systemReadKey is the two shapes the system-update spec (§3) grants every
// fleet box: its platform channel pointer and a mirrored platform artifact.
// Shapes, not prefixes — exactly three segments each, so nothing else that
diff --git a/internal/edge/storedoor_test.go b/internal/edge/storedoor_test.go
index b1a89f5e2..c67b38de6 100644
--- a/internal/edge/storedoor_test.go
+++ b/internal/edge/storedoor_test.go
@@ -178,6 +178,24 @@ func TestStoreDoorACLMatrix(t *testing.T) {
{"apps/ab12/web/channels/errors/stable.json", opWrite, false, "\"errors\" deeper in the key grants nothing"},
{"apps/ab12/web/errorsx/box-a/x.jsonl.gz", opWrite, false, "the segment is errors/, not a prefix of a longer name"},
+ // --- own account's promotion records (settings spec §4.1, ruling R17) ---
+ // Signed digests of the account's own builds, which HeldAuthorized
+ // reads on a fleet box. One shape, exactly five segments; nothing else
+ // under releases/ and never a write.
+ {"releases/ab12/web/promoted/v1.2.3", opRead, true, "own account's promotion record"},
+ {"releases/zz99/web/promoted/v1.2.3", opRead, false, "another account's promotion record"},
+ {"releases/ab12x/web/promoted/v1.2.3", opRead, false, "sqid must match exactly, not as a prefix"},
+ {"releases/ab12/web/promoted/v1.2.3/x", opRead, false, "a sixth segment"},
+ {"releases/ab12/web/promoted", opRead, false, "a missing version"},
+ {"releases/ab12/web/promoted/", opRead, false, "an empty version"},
+ {"releases/ab12/pointer-digests", opRead, false, "the account's digest marker is not granted"},
+ {"releases/ab12/web/other/v1.2.3", opRead, false, "segment 3 is exactly promoted"},
+ {"releases/ab12/Web!/promoted/v1.2.3", opRead, false, "an invalid app"},
+ {"releases/ab12/web/promoted/v1~bad", opRead, false, "an invalid version"},
+ {"releases/ab12/web/promoted/v1.2.3", opWrite, false, "a box never writes a promotion record"},
+ {"releases/ab12/web/promoted/v1.2.3", opDelete, false, "nor deletes one"},
+ {"releases/ab12/web/promoted/../../zz99/web/promoted/v1", opRead, false, "traversal out of own account"},
+
// --- other tenants ---
{"apps/zz99/web/channels/stable.json", opRead, false, "another account's app"},
{"apps/ab12x/web/channels/stable.json", opRead, false, "sqid prefix must not match a longer sqid"},
@@ -377,6 +395,31 @@ func TestACLDirIsPerTenant(t *testing.T) {
}
}
+// The promotion-record grant (ruling R17) is a read of one exact object
+// shape: no prefix under releases/ maps to a listing scope or shows as a
+// directory, the box's own account's included.
+func TestPromotionRecordsAreNotListable(t *testing.T) {
+ for _, prefix := range []string{
+ "releases/",
+ "releases",
+ "releases/" + testSqid + "/",
+ "releases/" + testSqid + "/web/",
+ "releases/" + testSqid + "/web/promoted/",
+ "releases/" + testSqid + "/web/promoted",
+ } {
+ if got, ok := aclListScope(testSqid, testFleet, prefix); ok {
+ t.Errorf("aclListScope(%q) = (%q, true), want refused", prefix, got)
+ }
+ if aclDir(testSqid, testFleet, strings.TrimSuffix(prefix, "/")+"/") {
+ t.Errorf("aclDir(%q) = true, want false", prefix)
+ }
+ }
+ key := "releases/" + testSqid + "/web/promoted/v1"
+ if aclRead("", testFleet, key) || aclRead(testSqid, "", key) {
+ t.Errorf("aclRead(%q) without both identities = true, want false", key)
+ }
+}
+
// ---- handler tests ------------------------------------------------------
// The credentials every handler test runs with: a fleet channel