Give the controller the contract it can fill (hq ADR 0325, issue 567)

The controller cannot know the operator's account's uid, names an access by its
declared id (which holds a slash), and should not parse words. A tool runner's
account may now come by name alone and is read here (refused when absent or root's,
its uid said in the access's listing); the take act matches the declared id exactly;
an access wait says its reason (keep or disk) and, for a filesystem that refuses
ACLs, whether the take act can enable them.
This commit is contained in:
2026-10-12 19:51:09 +02:00
parent ef26dba275
commit aad33dfe5c
12 changed files with 354 additions and 21 deletions
+6 -4
View File
@@ -489,10 +489,10 @@ const (
)
// AccessUser is one identity that uses an accessed folder: its name and uid as the controller allocated them, its
// use, and where it runs.
// use, and where it runs. A tool runner's account may come with no uid (0 or absent): the engine reads it by name.
type AccessUser struct {
Identity string `json:"identity"`
UID int `json:"uid"`
UID int `json:"uid,omitempty"`
Use string `json:"use"`
From string `json:"from"`
}
@@ -529,9 +529,11 @@ func (a *Access) validate(where string, _ bool) []string {
case u.Identity == "" || !accountName.MatchString(u.Identity):
problems = append(problems, fmt.Sprintf("%s: a user of an access is an account's name, and %q is not one",
where, u.Identity))
case u.UID <= 0:
case u.UID < 0 || (u.UID == 0 && u.From != FromToolRunner):
// Never root (novox/hq ADR 0325 §11): an entry for uid 0 would be a right root has anyway, and a
// number below it is no account.
// number below it is no account. A tool runner's account alone may come without its number: the
// controller cannot know the operator's account's, and the engine reads it by name
// (apply.ResolveRunners), refusing one that is root's.
problems = append(problems, fmt.Sprintf("%s: the user %s has uid %d, and a module's identity is never "+
"root or below it (novox/hq ADR 0325 §11)", where, u.Identity, u.UID))
}