Promise the vault's intake verbs and let the controller ask them in its own name (issue 675)

The vault takes a value only from the controller, which it knows from the
subject that names its caller. The grant is composed from the records, as
the issued tools' is, so the installer's first user list is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 19:07:04 +00:00
co-authored by Claude Opus 5.5
parent 8a428e3e86
commit 234dea9d29
6 changed files with 187 additions and 5 deletions
+23 -1
View File
@@ -2,6 +2,7 @@ package broker
import (
"fmt"
"slices"
"sort"
)
@@ -87,7 +88,7 @@ type Records struct {
// is a mesh that cannot be told anything, and there is no state of the records in which that is
// correct.
func Users(r Records) ([]Principal, error) {
out := []Principal{{Kind: KindController, Uses: asksSeatOf(r), AsksIssued: issuedOf(r)}}
out := []Principal{{Kind: KindController, Uses: asksSeatOf(r), AsksIssued: issuedOf(r), AsksTheVault: vaultVerbsOf(r)}}
for _, node := range sortedCopy(r.Nodes) {
witness := false
@@ -242,3 +243,24 @@ func asksSeatOf(r Records) []Seat {
}
return nil
}
// vaultVerbsOf are the vault's intake verbs its holder serves, as the records say (issue 675): what the controller
// is granted to ask it in its own name. None while no holder serves them.
func vaultVerbsOf(r Records) []string {
seen := map[string]bool{}
var out []string
for _, node := range sortedCopy(r.Nodes) {
for _, d := range r.Assigned[node] {
for _, s := range d.Holds {
for _, v := range VerbsTheControllerAsksTheVault {
if s.Name == v.Seat && slices.Contains(s.Serves, v.Verb) && !seen[v.Verb] {
seen[v.Verb] = true
out = append(out, v.Verb)
}
}
}
}
}
sort.Strings(out)
return out
}