Trust the vault's key alone and write the grants part's user list alone
The controller ended by its unassignment and its lease is never handed, so the engine's lease gate kept trusting the controller's key and the bus node kept the controller's list as a fallback nobody writes (issue 1126, A6). The engine now trusts no controller signature, its hand-over line names the node seat's hand-over, and the user-list follower writes only the grants part's list: a key unreadable or deleted keeps the list loaded, and with no key heard nothing is written. Compositions in the node's bucket name the file's place alone. The guard no longer asks for the controller's user. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -12,7 +12,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/buscompose"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/overlay"
|
||||
|
||||
@@ -41,21 +40,24 @@ func TestTheAppliedAuthorityIsKeptForTheRecord(t *testing.T) {
|
||||
t.Errorf("the record's keys read the authority %q", got.Authority)
|
||||
}
|
||||
|
||||
// L3 s6: before the vault's signers are read the node trusts the controller's key alone; once read, a message the
|
||||
// vault signed is accepted and the record names both; once the lease is handed away the controller's key is refused
|
||||
// and the record names the vault's alone. Each change tells the record's writer.
|
||||
// Issue 1126, A6 (ADR 0550 §2; the controller ends by its unassignment): a node enrolled with the controller's key
|
||||
// trusts it never, whatever the lease says; before the vault's signers are read it trusts nothing, once read the
|
||||
// vault's key alone, and the record names the vault's alone. Each change tells the record's writer.
|
||||
controllerPub, controller, _ := ed25519.GenerateKey(nil)
|
||||
vaultPub, vault, _ := ed25519.GenerateKey(nil)
|
||||
trust := newMeshTrust(controllerPub)
|
||||
trust := newMeshTrust()
|
||||
told := 0
|
||||
trust.onChange(func() { told++ })
|
||||
keys := recordKeys(state, trust.Trusts)
|
||||
ctrlFP, vaultFP := delivery.SignerFingerprint(controllerPub), delivery.SignerFingerprint(vaultPub)
|
||||
if got := keys().Trusts; !delivery.TrustsKey(got, delivery.SignerController, ctrlFP) || delivery.TrustsKey(got, delivery.SignerVault, vaultFP) {
|
||||
t.Errorf("before the signers are read the record trusts %v", got)
|
||||
}
|
||||
enrol, _ := delivery.SignContext(delivery.KindEnrolment)
|
||||
msg := []byte("a reply")
|
||||
if got := keys().Trusts; len(got) != 0 {
|
||||
t.Errorf("before the signers are read the record trusts %v", got)
|
||||
}
|
||||
if _, err := trust.Verify(delivery.KindEnrolment, msg, delivery.SignMessage(controller, enrol, msg)); err == nil {
|
||||
t.Error("a controller-signed message is accepted")
|
||||
}
|
||||
if _, err := trust.Verify(delivery.KindEnrolment, msg, delivery.SignMessage(vault, enrol, msg)); err == nil {
|
||||
t.Error("a vault-signed message is accepted before the vault's key is read")
|
||||
}
|
||||
@@ -68,22 +70,13 @@ func TestTheAppliedAuthorityIsKeptForTheRecord(t *testing.T) {
|
||||
if s, err := trust.Verify(delivery.KindEnrolment, msg, delivery.SignMessage(vault, enrol, msg)); err != nil || s.Name != delivery.SignerVault {
|
||||
t.Errorf("a vault-signed message on a node that read signers: %+v, %v", s, err)
|
||||
}
|
||||
if got := keys().Trusts; !delivery.TrustsKey(got, delivery.SignerController, ctrlFP) || !delivery.TrustsKey(got, delivery.SignerVault, vaultFP) ||
|
||||
if _, err := trust.Verify(delivery.KindEnrolment, msg, delivery.SignMessage(controller, enrol, msg)); err == nil {
|
||||
t.Error("a controller-signed message is accepted once the vault's key is read")
|
||||
}
|
||||
if got := keys().Trusts; delivery.TrustsKey(got, delivery.SignerController, ctrlFP) || !delivery.TrustsKey(got, delivery.SignerVault, vaultFP) ||
|
||||
trust.VaultAuthority() != vaultCA || told != 2 {
|
||||
t.Errorf("after the signers are read the record trusts %v (told %d)", got, told)
|
||||
}
|
||||
handed, _ := json.Marshal(buscompose.LeaseHolder{HandedTo: "mesh-vault", HandedBy: "person.operator"})
|
||||
trust.heardLease(handed, true, nil)
|
||||
if _, err := trust.Verify(delivery.KindEnrolment, msg, delivery.SignMessage(controller, enrol, msg)); err == nil || trust.Controller() != nil {
|
||||
t.Error("after the lease is handed away the controller's key is still trusted")
|
||||
}
|
||||
if got := keys().Trusts; delivery.TrustsKey(got, delivery.SignerController, ctrlFP) || !delivery.TrustsKey(got, delivery.SignerVault, vaultFP) || told != 3 {
|
||||
t.Errorf("after the lease is handed away the record trusts %v (told %d)", got, told)
|
||||
}
|
||||
trust.heardLease(handed, true, nil)
|
||||
if told != 3 {
|
||||
t.Error("a lease heard again unchanged wrote the record again")
|
||||
}
|
||||
// A signers' key naming another fingerprint is not trusted.
|
||||
bad, _ := json.Marshal(delivery.SignerKey{Key: base64.StdEncoding.EncodeToString(controllerPub), Fingerprint: vaultFP})
|
||||
trust.heardSigner(delivery.SignersKeyKey, bad, false, func(string) {})
|
||||
|
||||
+5
-14
@@ -64,27 +64,18 @@ func foundDirectories(known store.State) []store.Applied {
|
||||
return out
|
||||
}
|
||||
|
||||
// handOverLine is the operator's line that hands a directory to the mesh, with this node's name when it is
|
||||
// known (novox/hq ADR 0272): typed on the control-node, from the operator's own account. The directory's
|
||||
// path is the resource's target beside the reason, never in it: the controller says a reason on the
|
||||
// operator's channel, which withholds a path (ADR 0234 §6), and keeps the target as evidence.
|
||||
//
|
||||
// From the lease write, heard as the engine hears it (meshTrust), the line has no answerer: the way out is the node
|
||||
// seat's hand-over with the directory's path, which asks the operator on their channel (novox/hq ADR 0563 §5).
|
||||
// handOverLine is the way out of a directory used as found, with this node's name when it is known: the node seat's
|
||||
// hand-over with the directory's path, which asks the operator on their channel (novox/hq ADR 0563 §5). The controller
|
||||
// that answered `nox node hand-over` ended by its unassignment (issue 1126), so that line has no answerer. The path is
|
||||
// the resource's target beside the reason, never in it: the operator's channel withholds a path (ADR 0234 §6).
|
||||
func handOverLine() string {
|
||||
node, _ := foundNode.Load().(string)
|
||||
if node == "" {
|
||||
node = "<node>"
|
||||
}
|
||||
if t := foundTrust.Load(); t != nil && t.HandedAway() {
|
||||
return fmt.Sprintf("`%s/node-engine.hand-over` with the directory's path, then your yes on your channel", node)
|
||||
}
|
||||
return fmt.Sprintf("`nox node hand-over %s <directory>` on the control-node", node)
|
||||
return fmt.Sprintf("`%s/node-engine.hand-over` with the directory's path, then your yes on your channel", node)
|
||||
}
|
||||
|
||||
// foundTrust is what the engine trusts, whose lease decides the way out a directory used as found names.
|
||||
var foundTrust atomic.Pointer[meshTrust]
|
||||
|
||||
// withFoundDirectories adds to a statement every directory used as found, as its module's resource of kind
|
||||
// directory, unhealthy: the reason names its owner and mode as they are now, what was declared, and the line
|
||||
// that hands it over. The path is the target, which the controller keeps as evidence.
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
// Every directory used as found is its module's resource of kind directory, unhealthy, naming its owner, on
|
||||
// every statement (novox/hq issue 339): the controller raises that as the module's condition. The way out it
|
||||
// names is the operator's line, with this node's name (novox/hq ADR 0272, issue 356).
|
||||
// names is the node seat's hand-over, with this node's name (novox/hq ADR 0563 §5).
|
||||
func TestAFoundDirectoryIsStatedUnhealthy(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
known := store.State{Resources: []store.Applied{
|
||||
@@ -35,29 +35,15 @@ func TestAFoundDirectoryIsStatedUnhealthy(t *testing.T) {
|
||||
t.Fatalf("stated as: %+v", r)
|
||||
}
|
||||
// The path is the target, kept as evidence, and never in the reason: the operator's channel withholds a path
|
||||
// (novox/hq ADR 0234 §6), and the controller says the reason there.
|
||||
if !strings.Contains(r.Reason, "`nox node hand-over laptop <directory>` on the control-node") ||
|
||||
// (novox/hq ADR 0234 §6). The way out is the node seat's hand-over (ADR 0563 §5), never the controller's terminal line,
|
||||
// which nothing answers since the controller ended (issue 1126).
|
||||
if !strings.Contains(r.Reason, "`laptop/node-engine.hand-over`") || strings.Contains(r.Reason, "nox node hand-over") ||
|
||||
strings.Contains(r.Reason, "mesh-host") || strings.Contains(r.Reason, dir) {
|
||||
t.Fatalf("the way out is not the operator's line, or carries the path: %q", r.Reason)
|
||||
t.Fatalf("the way out is not the node seat's hand-over, or carries the path: %q", r.Reason)
|
||||
}
|
||||
// ADR 0563 §5: from the lease write the remedy is the node seat's hand-over, answered on the operator's channel, and
|
||||
// no longer the controller's terminal line; the lease is the one the engine hears.
|
||||
handed := newMeshTrust(nil)
|
||||
handed.handedAway = true
|
||||
foundTrust.Store(handed)
|
||||
if r := withFoundDirectories(&link.Health{}, foundDirectories(known)).Resources[0]; !strings.Contains(r.Reason,
|
||||
"`laptop/node-engine.hand-over`") || strings.Contains(r.Reason, "nox node hand-over") || strings.Contains(r.Reason, dir) {
|
||||
t.Fatalf("after the lease write the way out is %q", r.Reason)
|
||||
}
|
||||
foundTrust.Store(newMeshTrust(nil))
|
||||
if r := withFoundDirectories(&link.Health{}, foundDirectories(known)).Resources[0]; !strings.Contains(r.Reason,
|
||||
"`nox node hand-over laptop <directory>`") {
|
||||
t.Fatalf("while the controller holds its lease the way out is %q", r.Reason)
|
||||
}
|
||||
foundTrust.Store((*meshTrust)(nil))
|
||||
foundNode.Store("")
|
||||
if r := withFoundDirectories(&link.Health{}, foundDirectories(known)).Resources[0]; !strings.Contains(r.Reason,
|
||||
"`nox node hand-over <node> <directory>`") {
|
||||
"`<node>/node-engine.hand-over`") {
|
||||
t.Fatalf("before enrolment the line names a placeholder: %q", r.Reason)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1113,17 +1113,16 @@ func runLink(ctx context.Context, opts options) error {
|
||||
// the bus, answer what the controller asks of this machine, and say its facts: when a link opens, and when the
|
||||
// controller asks.
|
||||
proof := &proving{statePath: opts.state, version: runningVersion(), say: say}
|
||||
// What this engine trusts a mesh signature from: the controller's key until its lease is handed away, and the vault's
|
||||
// once read from its signers (novox/hq ADR 0550 §2).
|
||||
trust := newMeshTrust(mine.Membership.Signer)
|
||||
// And the way out a directory used as found names follows the lease it hears (novox/hq ADR 0563 §5).
|
||||
foundTrust.Store(trust)
|
||||
// What this engine trusts a mesh signature from: the vault's key alone, once read from its signers (novox/hq ADR 0550
|
||||
// §2; issue 1126: the controller's key never, the controller ended by its unassignment).
|
||||
trust := newMeshTrust()
|
||||
queue := &link.Session{
|
||||
Membership: membership,
|
||||
// And the public halves and certificate its record says (novox/hq ADR 0555 §3), read at each facts report.
|
||||
Keys: recordKeys(opts.state, trust.Trusts),
|
||||
// And the controller's key, read per signed ask: none once its lease is handed away (novox/hq ADR 0563 §7).
|
||||
ControllerKey: trust.Controller,
|
||||
// And no controller's key: every ask the controller would sign is refused, never verified with the membership's
|
||||
// signer (novox/hq ADR 0563 §7; issue 1126, the controller ended by its unassignment).
|
||||
ControllerKey: noControllerKey,
|
||||
// And the first have this build writes is what proves it to the launcher (ADR 0539 §9, ADR 0227 §8).
|
||||
HaveWritten: proof.haveWritten,
|
||||
// And the operator's hand-over of a directory used as found, carried by the controller from the
|
||||
|
||||
+11
-10
@@ -380,10 +380,11 @@ func resourcesOf(cs []delivery.Composed) []declaration.Resource {
|
||||
// controller's key the node was enrolled with (novox/hq ADR 0539 §3, §11); a node adopted later has it at its adoption.
|
||||
const operatorKeysFile = "operator-keys"
|
||||
|
||||
// trustedKeys are the keys a composition may be signed by: the controller's, which the node was enrolled with, while the
|
||||
// controller holds its lease (ADR 0550 §2: trusted no more once it is handed away), and the operator's, read from
|
||||
// operatorKeysFile each time it is asked, so a key that arrives while the agent runs is trusted at the next composition.
|
||||
// A file that cannot be read trusts the controller's alone, and says so.
|
||||
// trustedKeys are the keys a composition in the node's composition bucket may be signed by: the one given (none for the
|
||||
// agent; the enrolment's signer for the user-list follower, which reads only where the bus's user-list file is from
|
||||
// such a composition, never its list) and the operator's, read from operatorKeysFile each time it is asked, so a key
|
||||
// that arrives while the agent runs is trusted at the next composition. A file that cannot be read trusts the one given
|
||||
// alone, and says so.
|
||||
func trustedKeys(opts options, controllerNow func() ed25519.PublicKey) func() []ed25519.PublicKey {
|
||||
return func() []ed25519.PublicKey {
|
||||
keys := []ed25519.PublicKey{}
|
||||
@@ -509,9 +510,11 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, trus
|
||||
}
|
||||
}
|
||||
}
|
||||
// The bus's user list, written alone from the newest composition of the bus module (ADR 0547 §8): on every
|
||||
// node, and a node whose compositions carry no list writes nothing.
|
||||
users := userListFollower(opts, session, composedBucket, trustedKeys(opts, trust.Controller), say, func() {
|
||||
// The bus's user list, the grants part's alone (ADR 0547 §8, clause 11; issue 1126), written into the file the
|
||||
// bus module's composition in the node's bucket declares: on every node, and a node whose compositions carry no
|
||||
// list writes nothing. Those compositions were signed with the enrolment's signer and nobody writes them now; they
|
||||
// are read for the file's place alone, never for a list, until the place is read elsewhere (milestone B17).
|
||||
users := userListFollower(opts, session, composedBucket, trustedKeys(opts, func() ed25519.PublicKey { return signer }), say, func() {
|
||||
// This engine wrote the list, so the bus reloads its accounts and answers no JetStream request for a few
|
||||
// seconds: what failed in them is tried again once that is over, not at its next wait.
|
||||
time.AfterFunc(reloadSettles, func() {
|
||||
@@ -520,8 +523,6 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, trus
|
||||
}
|
||||
})
|
||||
})
|
||||
// A vault key heard (or heard again changed) looks at the list again, so its hashes are handed (ADR 0547 §9).
|
||||
vaultKey.following(users.Again)
|
||||
go func() {
|
||||
if err := users.Run(linked); err != nil && linked.Err() == nil {
|
||||
say("the bus's user list is not followed on this link: " + err.Error())
|
||||
@@ -551,7 +552,7 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, trus
|
||||
}
|
||||
return out, nil
|
||||
},
|
||||
Trusted: trustedKeys(opts, trust.Controller),
|
||||
Trusted: trustedKeys(opts, noControllerKey),
|
||||
Backup: backupNode(session),
|
||||
// What the filling refused before this apply may be named now: the provisioner's client is told to fill
|
||||
// again (ace, 2026-10-25 21:17).
|
||||
|
||||
@@ -160,7 +160,7 @@ func (b *itBed) start(trustOperator bool) (stop func()) {
|
||||
go func() {
|
||||
defer close(done)
|
||||
controller := b.controll.Public().(ed25519.PublicKey)
|
||||
nodeAgent(ctx, b.opts, controller, newMeshTrust(controller), b.session, nil, b.say, func() {},
|
||||
nodeAgent(ctx, b.opts, controller, newMeshTrust(), b.session, nil, b.say, func() {},
|
||||
&agentNow{}, func(context.Context, *agent.Agent) {})
|
||||
}()
|
||||
return func() { cancel(); <-done }
|
||||
|
||||
+20
-80
@@ -13,7 +13,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/buscompose"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/overlay"
|
||||
|
||||
@@ -21,30 +20,25 @@ import (
|
||||
"github.com/novox/mesh-host/internal/link"
|
||||
)
|
||||
|
||||
// **What this engine trusts a mesh signature from** (novox/hq ADR 0550 §2; design 47's L3 item d). Two signers: the
|
||||
// controller's key, which the node was enrolled with (its membership's signer), and the vault's key, read from the vault's
|
||||
// state `signers`, which only the vault may write: the bus grants are its anchor, as the wants' and the placement's (the
|
||||
// lead's design limit, 2026-10-28). The controller's key is trusted until its lease is handed away, heard on the lease key
|
||||
// (buscompose.LeaseHandedAway) and never on a clock; from then the vault's alone. What it trusts now the node states in its
|
||||
// record (NodeRecord.Trusts), so the gate before the lease write reads every node's from the bus.
|
||||
// **What this engine trusts a mesh signature from** (novox/hq ADR 0550 §2; design 47's L3 item d; issue 1126): the
|
||||
// vault's key alone, read from the vault's state `signers`, which only the vault may write: the bus grants are its
|
||||
// anchor, as the wants' and the placement's. The controller's key, which the node was enrolled with (its membership's
|
||||
// signer), is trusted never: the controller ended by its unassignment (the operator, 2026-11-02) and its lease is never
|
||||
// handed, so no gate waits on it. What it trusts now the node states in its record (NodeRecord.Trusts).
|
||||
type meshTrust struct {
|
||||
mu sync.Mutex
|
||||
controller ed25519.PublicKey
|
||||
handedAway bool
|
||||
vault ed25519.PublicKey
|
||||
authority string
|
||||
mu sync.Mutex
|
||||
vault ed25519.PublicKey
|
||||
authority string
|
||||
// changed is told when what it trusts changed, so the record is written again.
|
||||
changed func()
|
||||
}
|
||||
|
||||
// newMeshTrust trusts the controller's key alone until the vault's is read.
|
||||
func newMeshTrust(controller ed25519.PublicKey) *meshTrust {
|
||||
t := &meshTrust{}
|
||||
if len(controller) == ed25519.PublicKeySize {
|
||||
t.controller = controller
|
||||
}
|
||||
return t
|
||||
}
|
||||
// newMeshTrust trusts nothing until the vault's key is read.
|
||||
func newMeshTrust() *meshTrust { return &meshTrust{} }
|
||||
|
||||
// noControllerKey is the controller's key as this engine trusts it: none. A link told it refuses every ask signed as
|
||||
// the controller's, where a link told nothing would verify it with the membership's signer (link.HandOverCheck).
|
||||
func noControllerKey() ed25519.PublicKey { return nil }
|
||||
|
||||
// onChange names who is told when what it trusts changed.
|
||||
func (t *meshTrust) onChange(f func()) {
|
||||
@@ -53,38 +47,11 @@ func (t *meshTrust) onChange(f func()) {
|
||||
t.mu.Unlock()
|
||||
}
|
||||
|
||||
// Controller is the controller's key while the controller holds its lease, and nil once the lease is handed away.
|
||||
func (t *meshTrust) Controller() ed25519.PublicKey {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
if t.handedAway {
|
||||
return nil
|
||||
}
|
||||
return t.controller
|
||||
}
|
||||
|
||||
// HandedAway says the controller's lease was handed away, as heard on the lease key.
|
||||
func (t *meshTrust) HandedAway() bool {
|
||||
if t == nil {
|
||||
return false
|
||||
}
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
return t.handedAway
|
||||
}
|
||||
|
||||
// Signers are the keys a mesh signature is verified against now: the controller's while it holds the lease, and the
|
||||
// vault's once read.
|
||||
// Signers are the keys a mesh signature is verified against now: the vault's once read, and none before.
|
||||
func (t *meshTrust) Signers() []delivery.Signer {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
var out []delivery.Signer
|
||||
if t.controller != nil && !t.handedAway {
|
||||
out = append(out, delivery.Signer{Name: delivery.SignerController, Key: t.controller})
|
||||
}
|
||||
if t.vault != nil {
|
||||
out = append(out, delivery.Signer{Name: delivery.SignerVault, Key: t.vault})
|
||||
}
|
||||
@@ -165,21 +132,11 @@ func (t *meshTrust) heardSigner(key string, value []byte, deleted bool, say link
|
||||
}
|
||||
}
|
||||
|
||||
// heardLease takes the controller's lease key as read: once handed away to a holder that is no controller, the
|
||||
// controller's key is trusted no more. A lease that cannot be read changes nothing.
|
||||
func (t *meshTrust) heardLease(raw []byte, found bool, readErr error) {
|
||||
if readErr != nil {
|
||||
return
|
||||
}
|
||||
handed, _ := buscompose.LeaseHandedAway(raw, found, nil)
|
||||
t.change(func() { t.handedAway = handed })
|
||||
}
|
||||
|
||||
// trustFollowAgain bounds the wait before a watch that could not be made is tried again on the same link: the vault may
|
||||
// not serve `sign` yet, and until it does this node is not granted the reads.
|
||||
var trustFollowAgain = 2 * time.Minute
|
||||
|
||||
// followTrust follows the vault's `signers` and the controller's lease on every link, as watches (never a poll), for as
|
||||
// followTrust follows the vault's `signers` on every link, as watches (never a poll), for as
|
||||
// long as ctx lasts. A watch that cannot be made is said once per reason and tried again.
|
||||
func followTrust(ctx context.Context, session *link.Session, trust *meshTrust, say link.Announce) {
|
||||
said := ""
|
||||
@@ -198,33 +155,16 @@ func followTrust(ctx context.Context, session *link.Session, trust *meshTrust, s
|
||||
}
|
||||
watching, stop := context.WithCancel(linked)
|
||||
signers, serr := session.KVWatch(watching, delivery.SignersBucket, ">")
|
||||
lease, lerr := session.KVWatch(watching, buscompose.LeaseBucket, buscompose.ControllerLeaseKey)
|
||||
if serr != nil || lerr != nil {
|
||||
if serr != nil {
|
||||
stop()
|
||||
sayOnce(fmt.Sprintf("what this node trusts a mesh signature from is not followed yet (the vault's signers: %v; "+
|
||||
"the controller's lease: %v): it trusts what it last read, and looks again in %s", serr, lerr, trustFollowAgain))
|
||||
signers, lease = nil, nil
|
||||
sayOnce(fmt.Sprintf("what this node trusts a mesh signature from is not followed yet (the vault's signers: %v): "+
|
||||
"it trusts what it last read, and looks again in %s", serr, trustFollowAgain))
|
||||
} else {
|
||||
sayOnce("")
|
||||
}
|
||||
for signers != nil || lease != nil {
|
||||
select {
|
||||
case u, ok := <-signers:
|
||||
if !ok {
|
||||
signers = nil
|
||||
continue
|
||||
}
|
||||
for u := range signers {
|
||||
if !u.Caught {
|
||||
trust.heardSigner(u.Key, u.Value, u.Deleted, say)
|
||||
}
|
||||
case u, ok := <-lease:
|
||||
if !ok {
|
||||
lease = nil
|
||||
continue
|
||||
}
|
||||
if !u.Caught {
|
||||
trust.heardLease(u.Value, !u.Deleted && len(u.Value) > 0, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
stop()
|
||||
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
"github.com/novox/mesh-host/internal/userlist"
|
||||
)
|
||||
|
||||
// The bridge of novox/hq ADR 0547 §8, wired: the bus node's engine writes the bus's user list alone from the newest
|
||||
// composition of the bus module, under the apply's two locks as a filled file is written (filling.go), and says what
|
||||
// The bridge of novox/hq ADR 0547 §8, wired: the bus node's engine writes the grants part's user list alone into the file
|
||||
// the bus module's composition declares, under the apply's two locks as a filled file is written (filling.go), and says what
|
||||
// it wrote in the bus module's have.
|
||||
|
||||
// userListFollower is the follower of the bus's user list for one link: the node's composition bucket, the keys the
|
||||
@@ -32,10 +32,7 @@ func userListFollower(opts options, session *link.Session, composedBucket string
|
||||
Node: session.Membership.Node,
|
||||
Composed: bucketWatch{session, composedBucket},
|
||||
Trusted: trusted,
|
||||
Resolve: func(ctx context.Context, raw json.RawMessage) ([]byte, error) {
|
||||
return resolveUserList(session.WithLink(ctx), raw)
|
||||
},
|
||||
OnDisk: userListOnDisk,
|
||||
OnDisk: userListOnDisk,
|
||||
Write: func(ctx context.Context, module string, raw json.RawMessage, body []byte) (string, bool, error) {
|
||||
digest, changed, err := writeUserListAlone(session.WithLink(ctx), opts, raw, body, say)
|
||||
if changed && wrote != nil {
|
||||
@@ -75,10 +72,7 @@ func userListFollower(opts options, session *link.Session, composedBucket string
|
||||
Kept: func() ([]byte, uint64) { return keptGrantsKey(opts) },
|
||||
Keep: func(value []byte, rev uint64) { keepGrantsKey(opts, value, rev, say) },
|
||||
Backoff: link.Backoff,
|
||||
// The vault's key from its answer to node-key: the loaded list's hashes are handed sealed to it (ADR 0547
|
||||
// clause 9).
|
||||
VaultKey: vaultKey.get,
|
||||
Say: announceOr(say),
|
||||
Say: announceOr(say),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -98,20 +92,6 @@ func userListFile(raw []byte) (*declaration.File, error) {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// resolveUserList is the list a user-list resource holds: its content, or the object its digest names in this node's
|
||||
// file bucket, fetched and verified (apply.ResolveFileReferences, ADR 0486 §1).
|
||||
func resolveUserList(ctx context.Context, raw []byte) ([]byte, error) {
|
||||
f, err := userListFile(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d := &declaration.Declaration{Version: declaration.Version, Resources: []declaration.Resource{f}}
|
||||
if err := apply.ResolveFileReferences(ctx, d, link.FilesFrom(ctx)); err != nil {
|
||||
return nil, fmt.Errorf("the user list could not be had by its digest: %w", err)
|
||||
}
|
||||
return []byte(d.Resources[0].(*declaration.File).Content), nil
|
||||
}
|
||||
|
||||
// userListOnDisk is what the user list's file holds on this machine now; nil when there is none.
|
||||
func userListOnDisk(raw json.RawMessage) ([]byte, error) {
|
||||
f, err := userListFile(raw)
|
||||
|
||||
Reference in New Issue
Block a user