S1 sdk-if-config-word: the resource key if-config is a reader capability

A resource carrying `if-config` needs `word:resources.if-config`, which ManifestReads states.

Builds: design 42 zfs-owns-zfs §4, §2; answer A-13
This commit is contained in:
2026-11-13 00:12:06 +01:00
parent e42b9491c9
commit 160912280c
3 changed files with 25 additions and 0 deletions
+11
View File
@@ -38,6 +38,9 @@ const (
var referenceForms = []string{"access", "backup", "bound", "collected", "config", "consumer", "consumers",
"contribution", "dir", "machine", "port", "seat", "secret", "setting"}
// ifConfigWord is the reader capability for a resource's `if-config` (IfConfigField).
const ifConfigWord = ReadWord + "resources." + IfConfigField
// ReferenceFormSeat names the reference form a registry seat answers for a build's artifact,
// `<seat>/<module>/<artifact>@sha256:<digest>` (novox/hq ADR 0605 §9; the registry clients' reference forms), as the reader capability
// `form:seat-reference` a node-engine built with this contract states (ADR 0618 §1, ADR 0624 §1): the builder writes it
@@ -106,6 +109,7 @@ func ManifestReads() []string {
set[ReadForm+f] = true
}
set[ReadForm+ReferenceFormSeatReach] = true
set[ifConfigWord] = true
for _, s := range Seats() {
set[ReadSeat+s.Name] = true
}
@@ -171,6 +175,13 @@ func ManifestNeeds(raw []byte) ([]string, error) {
if claimsAtMesh(top, storeHolderSeat) {
used[ReadForm+ReferenceFormSeatReach] = true
}
for _, r := range asList(top["resources"]) {
if obj, ok := r.(map[string]any); ok {
if _, has := obj[IfConfigField]; has {
used[ifConfigWord] = true
}
}
}
if _, has := top[ConfigField]; has || takesConfigKey.Match(raw) {
used[ReadForm+"config"] = true
}
+8
View File
@@ -96,6 +96,11 @@ func TestOwnManifestUsesNoWordTheRunningControllerRefuses(t *testing.T) {
{"a template naming a seat's reach", `{"module":"d","version":"1","resources":[{"id":"daemon","type":"file",` +
`"path":"/etc/d.json","template":"files/daemon.json"}]}`, []string{"form:seat", "form:seat-reach"},
[]string{"form:seat-reach"}},
// A resource under a config condition (design 42 zfs-owns-zfs §4, A-13).
{"a resource under if-config", `{"module":"z","version":"1","config":{"k":{"type":"string"}},"resources":[{"id":"f","type":"file",` +
`"if-config":["k"]}]}`, []string{"word:resources.if-config"}, []string{"word:resources.if-config"}},
{"a resource without if-config", `{"module":"z","version":"1","resources":[{"id":"f","type":"file"}]}`,
[]string{"word:resources"}, nil},
{"a word this contract does not know", `{"module":"u","version":"1","newer-word":{"inside":1}}`, []string{"word:module"}, nil},
} {
templates := map[string]map[string]string{ // the templates' text a row's module names
@@ -119,6 +124,9 @@ func TestOwnManifestUsesNoWordTheRunningControllerRefuses(t *testing.T) {
if slices.ContainsFunc(needs, func(n string) bool { return n == "word:newer-word" || n == "form:count" }) {
t.Errorf("%s: needs %v, naming what the contract does not read", row.why, needs)
}
if slices.Contains(needs, "word:resources.if-config") != (row.why == "a resource under if-config") {
t.Errorf("%s: needs %v, if-config read wrongly", row.why, needs)
}
if got := MissingManifestReads(needs, nil); !slices.Equal(got, row.missing) {
t.Errorf("%s: an engine stating nothing lacks %v, want %v", row.why, got, row.missing)
}
+6
View File
@@ -12,6 +12,12 @@ import (
// catalogue, never on the machine: the composer takes it out before the node-engine, which parses strictly.
const TrustedField = "trusted"
// IfConfigField is the key any resource may carry to be declared only while a config value of its module holds
// (novox/hq design 42 zfs-owns-zfs §2, §4): a list of conditions, `<key>` or `<key>=<value>`. Said in the catalogue and
// read by the node-engine, which states it as the reader capability `word:resources.if-config` (ManifestReads); a
// manifest whose resource carries it needs that capability (ManifestNeeds).
const IfConfigField = "if-config"
// TerminalKeys are the settings keys of a module that are set at the controller's terminal alone: places and
// accesses, every key its provisions serve and every setting a served value asks for, every setting a file
// asks for unless it says `"trusted": false`, and every setting a directory's owner asks for. Places and accesses first, then the rest sorted.