package link import ( "context" "time" ) // What a host hears, as the host's own words for it. // // The outbound half went behind `Bus` (bus.go) and a node's two statements stopped naming a // transport. This is the other half — dialling, and the declarations that arrive — and it is where // the transport reached furthest: the run loop selected on a channel of the client library's own // delivery type, so every part of holding a node in its mesh knew which bus it was on. // // **The host still imports nothing of the mesh's own** (novox/hq ADR 0005). This is its own // interface over its own libraries, and it agrees with the controller only because a conformance // fixture holds both to one envelope. // Link is this node's live connection to its mesh: what it hears, and what it says. // // One interface rather than two, because **dialling is where the transport is chosen** and choosing // it twice is how one half of a node ends up on a different bus from the other. type Link interface { // Bus is what this node says: what it applied, and that it is here. Bus // Declarations is what the mesh tells this node to be. Declarations() <-chan Declaration // Lost says the link ended, and why. // // **Read rather than discovered.** A node that finds out by noticing silence is a node that // believed it was in the mesh for as long as the silence lasted, which is the one state ADR // 0004 says must never look like being connected. Lost() <-chan error // Close lets go of whatever was dialled. Close() } // Declaration is one thing the mesh told this node to be. // // **Handled, once — after the report is published.** A node that dies between applying and // reporting leaves the declaration with the mesh and applies it again on return, which is safe // because applying is reconciliation: it converges rather than repeating. // // There is one way of being done rather than two. A declaration set aside because a newer arrived // with it is settled exactly as an applied one is, on both buses, and the difference between them // is a fact the *report* carries — a second method here would be a distinction the transport does // not make. type Declaration interface { // Body is the signed declaration as it arrived, bytes unchanged: a node verifies what it // received rather than what it re-encoded. Body() []byte // Handled settles it. Called after the report for it has been published, either way. Handled() error } // Open opens this node's link to its mesh. // // Named Open rather than Dial because Dial is this package's raw TLS dial, which the enrolment path // uses to see a certificate before it trusts anything. // // **Both transports ship and this is the one place that chooses** (novox/hq ADR 0116: nothing moves // a node's bus before step 5). Until then every membership names the bus the mesh runs on today, // and the rollout is this switch and the credential behind it — not a change anywhere in the loop // that reads from what comes back. func Open(ctx context.Context, m Membership, timeout time.Duration) (Link, error) { switch m.Transport { case OnNATS: return dialNats(ctx, m, timeout) default: return dialCurrent(ctx, m, timeout) } } // The buses a node can be on. Empty is the one the mesh runs on today, which is every node until // the rollout — so a membership recorded before any of this existed reads as correct rather than as // unset. const ( OnCurrent = "" OnNATS = "nats" )