diff --git a/cmd/mesh-host/found.go b/cmd/mesh-host/found.go index 2301fb6..eeebed0 100644 --- a/cmd/mesh-host/found.go +++ b/cmd/mesh-host/found.go @@ -68,14 +68,23 @@ func foundDirectories(known store.State) []store.Applied { // 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). func handOverLine() string { node, _ := foundNode.Load().(string) if 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 ` on the control-node", 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. diff --git a/cmd/mesh-host/found_test.go b/cmd/mesh-host/found_test.go index 4a13d2c..f756bfc 100644 --- a/cmd/mesh-host/found_test.go +++ b/cmd/mesh-host/found_test.go @@ -40,6 +40,21 @@ func TestAFoundDirectoryIsStatedUnhealthy(t *testing.T) { 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) } + // 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 `") { + 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 `") { diff --git a/cmd/mesh-host/main.go b/cmd/mesh-host/main.go index c7efd1e..1b6289a 100644 --- a/cmd/mesh-host/main.go +++ b/cmd/mesh-host/main.go @@ -1108,6 +1108,8 @@ func runLink(ctx context.Context, opts options) error { // 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) queue := &link.Session{ Membership: membership, // And the public halves and certificate its record says (novox/hq ADR 0555 §3), read at each facts report. diff --git a/cmd/mesh-host/trust.go b/cmd/mesh-host/trust.go index 61d37fd..e8400cd 100644 --- a/cmd/mesh-host/trust.go +++ b/cmd/mesh-host/trust.go @@ -65,6 +65,16 @@ func (t *meshTrust) Controller() ed25519.PublicKey { 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. func (t *meshTrust) Signers() []delivery.Signer {