A module may hold several secrets from one provider, each a pair of its own

secrets: maps a requirement to several files under local names. Each local name is
its own need, its own pair credential (the pair is keyed on it: migration 0027),
its own file on the consumer, its own holder at the provider (the identity with the
local name after it) and rotates apart from the others. The plain shape is
unchanged and every existing row is the credential it was (novox/hq 04-ISSUES/069,
ADR 0094).
This commit is contained in:
2026-09-21 20:28:16 +02:00
parent f3bfc11565
commit 6ae4ae1dba
13 changed files with 567 additions and 142 deletions
+23 -3
View File
@@ -157,6 +157,10 @@ type Needed struct {
Sealed string
// For is the module that wanted it.
For string
// Local is the name this credential goes by inside that module, where the module wants several
// for one requirement (ADR 0094); empty for the ordinary one. Part of what identifies the pair
// credential, so two secrets from one provider to one module are two secrets.
Local string
// Manager is set when this holder is a refreshable-grant licence's MANAGER, delivered the refresh
// token rather than an access token (novox/hq ADR 0050). It changes one thing downstream: an empty
// Sealed is tolerated — the manager has not adopted a refresh token yet, which is a real waiting
@@ -298,7 +302,7 @@ func Resolve(catalogue map[string]Manifest, assigned []string, node Node, world
if at == "" {
at = "127.0.0.1"
}
needs = append(needs, Needed{
needs = eachLocal(needs, catalogue, Needed{
Name: want, From: node.Name, At: at,
Serves: servedHere(catalogue, chosen, want), For: because[want]})
} else if served := servedHere(catalogue, chosen, want); len(served) > 0 {
@@ -319,7 +323,7 @@ func Resolve(catalogue map[string]Manifest, assigned []string, node Node, world
if at == "" {
at = "127.0.0.1"
}
needs = append(needs, Needed{
needs = eachLocal(needs, catalogue, Needed{
Name: want, From: node.Name, At: at, Serves: served, For: because[want]})
}
continue
@@ -347,7 +351,7 @@ func Resolve(catalogue map[string]Manifest, assigned []string, node Node, world
node.Name, want, p.Node, meshNetwork))
return
}
needs = append(needs, Needed{Name: want, From: p.Node, At: p.At,
needs = eachLocal(needs, catalogue, Needed{Name: want, From: p.Node, At: p.At,
Serves: p.Serves, For: because[want]})
}
switch {
@@ -854,3 +858,19 @@ func perConsumer(needs []Needed, order []string, catalogue map[string]Manifest)
}
return out
}
// eachLocal appends the need once per file the wanting module keeps the credential in: once, with
// no local name, in the ordinary case; once per local name where the module wants several values
// from one provider (ADR 0094). Each is its own pair credential downstream.
func eachLocal(needs []Needed, catalogue map[string]Manifest, n Needed) []Needed {
files := catalogue[n.For].SecretFiles(n.Name)
if len(files) <= 1 {
return append(needs, n)
}
for _, f := range files {
one := n
one.Local = f.Local
needs = append(needs, one)
}
return needs
}