Make every offer say its credential's form, pair or none where it names no own secret or issuer, and read no default, so the vault never mints a login for a provider that takes none (issue 1954, ADR 0570)

This commit is contained in:
2026-10-29 22:47:32 +01:00
parent dedd988fc9
commit 6b8c694381
7 changed files with 65 additions and 9 deletions
+19 -3
View File
@@ -42,9 +42,10 @@ func consumerOn(t *testing.T, m Manifest, w World) Resolution {
node := workstation()
node.At = "workstation.internal"
stores := shelf(m,
Manifest{Module: "store", Version: "1", Provides: []Offer{{Name: "postgres-database", Scope: ScopeMesh}},
Manifest{Module: "store", Version: "1", Provides: []Offer{{Name: "postgres-database", Scope: ScopeMesh,
Credential: &OfferCredential{Form: CredentialFormPair}}},
Serves: map[string]map[string]any{"postgres-database": {"port": 5432}}},
Manifest{Module: "store-two", Version: "1", Provides: []Offer{{Name: "postgres-database", Scope: ScopeMesh}},
Manifest{Module: "store-two", Version: "1", Provides: []Offer{{Name: "postgres-database", Scope: ScopeMesh, Credential: &OfferCredential{Form: CredentialFormPair}}},
Serves: map[string]map[string]any{"postgres-database": {"port": 5434}}})
r, err := Resolve(stores, []string{m.Module}, node, w)
if err != nil {
@@ -130,7 +131,10 @@ func TestAHandedModulesPlanNamesTheCredentialFormOfItsProvidersOffer(t *testing.
return mustParse(t, raw+`}`)
}
for credential, want := range map[string]string{
``: CredentialFormPair,
// No default (the lead, 2026-10-29): an offer that says nothing has no form, and the catalogue refuses it.
``: "",
`,"credential":{"form":"pair"}`: CredentialFormPair,
`,"credential":{"form":"none"}`: CredentialFormNone,
`,"credential":{"own":"admin"}`: CredentialFormShared,
`,"credential":{"own":"token"}`: CredentialFormGiven,
`,"credential":{"issued-by":"outside"}`: CredentialFormGiven,
@@ -153,6 +157,18 @@ func TestAHandedModulesPlanNamesTheCredentialFormOfItsProvidersOffer(t *testing.
if got := (Manifest{Module: "x"}).CredentialFormOf("postgres-database"); got != "" {
t.Errorf("a module offering nothing has the credential form %q", got)
}
// The catalogue refuses an offer saying no form, and a form beside an own secret or not pair or none.
for credential, refused := range map[string]string{``: "says no credential form", `,"credential":{}`: "says no credential form",
`,"credential":{"form":"pair","own":"admin"}`: "beside an own secret", `,"credential":{"form":"maybe"}`: `not "pair" or "none"`,
`,"credential":{"form":"none"}`: ""} {
raw := `{"module":"store","version":"1","provides":[{"name":"postgres-database","scope":"mesh"` + credential + `}]}`
var m Manifest
_ = json.Unmarshal([]byte(raw), &m)
problems := strings.Join(CredentialFormProblems(m), "; ")
if (refused == "") != (problems == "") || !strings.Contains(problems, refused) {
t.Errorf("%q: %q, want %q", credential, problems, refused)
}
}
}
// A consumer's plan entry carries what its manifest contributes to that provision (issue 776), so the provider's
+33 -2
View File
@@ -287,6 +287,10 @@ type OfferCredential struct {
Own string `json:"own,omitempty"`
IssuedBy string `json:"issued-by,omitempty"`
Tool string `json:"tool,omitempty"`
// Form says, for an offer that names no own secret and no issuer, which it is: CredentialFormPair, a login of
// each consumer's own, or CredentialFormNone, no credential at all (the lead, 2026-10-29, for novox/hq ADR 0570):
// never left to a default.
Form string `json:"form,omitempty"`
}
// IssuedBySoftware says a provision's credential is issued by its provider's software (novox/hq ADR 0305).
@@ -362,6 +366,9 @@ const (
// CredentialFormGiven is given by a person (novox/hq ADR 0337): the offer's credential, or the own secret it
// shares, is issued outside the mesh; the mesh never makes it.
CredentialFormGiven = "given"
// CredentialFormNone is an offer whose consumers present no credential: its outcome is the provider's have, and
// the vault mints nothing for it.
CredentialFormNone = "none"
)
// CredentialFormOf is the credential form this module's offer of a provision declares, read from the offer's
@@ -375,7 +382,7 @@ func (m Manifest) CredentialFormOf(provision string) string {
}
switch c := o.Credential; {
case c == nil:
return CredentialFormPair
return "" // undeclared: refused at the catalogue (CredentialFormProblems), never read as a pair
case c.IssuedBy == IssuedOutside:
return CredentialFormGiven
case c.IssuedBy == IssuedBySoftware:
@@ -385,11 +392,33 @@ func (m Manifest) CredentialFormOf(provision string) string {
case c.Own != "":
return CredentialFormShared
}
return CredentialFormPair
return o.Credential.Form
}
return ""
}
// CredentialFormProblems refuses an offer that does not say its credential's form (the lead, 2026-10-29): an own
// secret, an issuer, or `form` pair or none, and `form` only where neither of the others is said.
func CredentialFormProblems(m Manifest) []string {
var problems []string
for _, o := range m.Provides {
c := o.Credential
switch {
case c == nil || (c.Own == "" && c.IssuedBy == "" && c.Form == ""):
problems = append(problems, fmt.Sprintf("%s provides %q and says no credential form: say `credential` with "+
"`form` pair (a login of each consumer's own) or none (no credential), or the own secret or issuer it is",
m.Module, o.Name))
case c.Form != "" && (c.Own != "" || c.IssuedBy != ""):
problems = append(problems, fmt.Sprintf("%s provides %q with `form` beside an own secret or an issuer; `form` "+
"says only pair or none", m.Module, o.Name))
case c.Form != "" && c.Form != CredentialFormPair && c.Form != CredentialFormNone:
problems = append(problems, fmt.Sprintf("%s provides %q with the form %q, not %q or %q", m.Module, o.Name,
c.Form, CredentialFormPair, CredentialFormNone))
}
}
return problems
}
// ProvisionsSharing is every provision of this module whose credential is the named own secret.
func (m Manifest) ProvisionsSharing(own string) []string {
var out []string
@@ -1995,6 +2024,8 @@ func parseManifest(raw []byte, predefined PredefinedList) (Manifest, error) {
} else if offer.Credential != nil {
own, declared := m.OwnSecrets[offer.Credential.Own]
switch {
case offer.Credential.Own == "" && offer.Credential.Form != "":
// The form alone, pair or none (CredentialFormProblems judges it).
case offer.Credential.Own == "":
problems = append(problems, fmt.Sprintf(
"%s provides %q with a credential that names no own secret", m.Module, p))
+4
View File
@@ -282,6 +282,10 @@ func CatalogueProblems(shelf Shelf) []string {
problems = append(problems, RestrictedHearingProblems(shelf)...)
// A work-queue stream is never asked for by another module's consumes (novox/hq ADR 0436 §6).
problems = append(problems, WorkQueueConsumedElsewhereProblems(shelf)...)
// Every offer says its credential's form, never left to a default (the lead, 2026-10-29, for novox/hq ADR 0570).
for _, module := range shelfOrder(shelf) {
problems = append(problems, CredentialFormProblems(shelf[module])...)
}
// Who declares what, and who declared it first.
declaredBy := map[string]string{}
+2 -2
View File
@@ -109,7 +109,7 @@ func TestAClaimMustMatchTheDeclaredScope(t *testing.T) {
func TestTheMeshsOwnSeatsAreStillClaimable(t *testing.T) {
// It delivers the bus, so its holder provides the bus — the rule this check now enforces.
m := Manifest{Module: "nats",
Provides: []Offer{{Name: "mesh-bus", Scope: ScopeMesh}},
Provides: []Offer{{Name: "mesh-bus", Scope: ScopeMesh, Credential: &OfferCredential{Form: CredentialFormNone}}},
Claims: []Claim{{Name: "mesh-broker", Scope: ScopeMesh}}}
if got := problemsFor(t, Shelf{"nats": m}); got != "" {
t.Fatalf("a mesh seat was refused by the derived check: %s", got)
@@ -186,7 +186,7 @@ func TestTheParserDoesNotJudgeWhatOnlyTheStoreKnows(t *testing.T) {
// A store whose bus seat delivers something this module does provide.
UseSeats([]Seat{{Name: "mesh-broker", Scope: ScopeMesh, Delivers: "mesh-bus", Decision: "test"}})
raw := []byte(`{"module":"a-bus","version":"1",` +
`"provides":[{"name":"mesh-bus","scope":"mesh"}],` +
`"provides":[{"name":"mesh-bus","scope":"mesh","credential":{"form":"none"}}],` +
`"claims":[{"name":"mesh-broker","scope":"mesh"}]}`)
m, err := ParseManifest(raw)
+1 -1
View File
@@ -148,7 +148,7 @@ func TestTheUplinkIsANodeSeatThatDeliversNothing(t *testing.T) {
}
func claimed(claims string) []byte {
return []byte(`{"module":"thing","version":"1","provides":[{"name":"npm-package-registry","scope":"mesh"}],"claims":` + claims + `}`)
return []byte(`{"module":"thing","version":"1","provides":[{"name":"npm-package-registry","scope":"mesh","credential":{"form":"none"}}],"claims":` + claims + `}`)
}
// **The refusal moved, it did not go** (novox/hq ADR 0118, superseding 0110). A module may now
+3
View File
@@ -99,6 +99,9 @@ const (
FormShared = "shared"
FormIssued = "issued"
FormGiven = "given"
// FormNone is an offer whose consumers present no credential (catalogue.CredentialFormNone): the vault mints nothing,
// and the provision depends on no credential.
FormNone = "none"
)
// VaultName is the vault's name of a provision's credential in form, as its verb `own` names it and the provider asks
+3 -1
View File
@@ -74,7 +74,7 @@ func TestAProvisionIsAReferenceUnderItsConsumersKey(t *testing.T) {
}
// A reference's id is the vault's name as its verb own names it, in each form (the host agent, 2026-10-29).
for form, want := range map[string]string{FormPair: "pair/novox/postgres-database/novox/keycloak", FormShared: "provision/novox/postgres-database",
FormIssued: "provision/novox/postgres-database/novox/keycloak", "none": ""} {
FormIssued: "provision/novox/postgres-database/novox/keycloak", FormNone: ""} {
if got := VaultName(form, db); got != want {
t.Errorf("the %s name is %q", form, got)
}
@@ -95,6 +95,8 @@ func TestAProvisionIsAReferenceUnderItsConsumersKey(t *testing.T) {
{FormShared, nil, "ace.postgres.secret-postgres-database"},
{FormPair, providerPlan, "ace.postgres.backup-target,novox.keycloak.secret-postgres-database"},
{"", nil, ""},
// An offer with no credential (the lead, 2026-10-29): nothing minted, nothing to wait for.
{FormNone, nil, ""},
} {
entry := catalogue.PlanProvision{Provision: "postgres-database", Provider: postgres.Node, ProviderModule: postgres.Module,
CredentialForm: c.form}