Enrolment behind a seam, with both transports
The last of the host's link that still named a transport. `Asking` is one enrolment conversation — a connection made with the token, a question asked, and an answer waited for — and it is its own seam rather than part of `Link` because almost nothing about it is the same: the credential is a one-time secret, there is no declaration to hear, and a node that fails here is not in the mesh at all, where a node that fails in `Link` has merely lost touch with one it belongs to. `Enrol`'s thirteen arguments became an `Approach` — where, which certificate, which bus — and the request it already had. The token says nothing about which bus, and does not need to: every token names the one the mesh runs on today until the rollout. **The reply address is the whole of what changes on the new bus**, and it is forced rather than preferred. Verified against a running server, both halves: the answer reaches the node at the address its request carried in the payload, and the transport's own reply field held something else entirely by the time the consumer saw it — the consumer's ack address, exactly as design 25 §2 says. The test asserts the field is *not* the node's inbox, so a future server that stopped claiming it would fail this rather than let the reason quietly become folklore. The inbox is under `_INBOX.enrol.<node>.`, which is exactly what the enrolling user may subscribe and no wider, with a random tail per attempt: a reply left over from an attempt that timed out is not the answer to this question, which is what the correlation id does on the other transport. Subscribed before anything is published, because a node that published first could miss an answer to a question nobody was listening for.
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
package link
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// The enrolment conversation, as the host's own words for it.
|
||||
//
|
||||
// **Its own seam rather than part of Link**, because almost nothing about it is the same. The
|
||||
// credential is a one-time secret rather than this node's own; there is no declaration to hear; the
|
||||
// whole exchange is a single question asked and possibly asked again. And the stakes differ: a node
|
||||
// that fails here is not in the mesh at all, where a node that fails in Link has merely lost touch
|
||||
// with one it belongs to.
|
||||
|
||||
// Approach is how a node reaches a mesh it does not yet belong to.
|
||||
//
|
||||
// The three things a token carries about where to go, and nothing about who is asking: the address,
|
||||
// the certificate that address must present, and which bus is at the other end. **Every token names
|
||||
// the bus the mesh runs on today until the rollout** (novox/hq ADR 0116 step 5), so an empty
|
||||
// Transport is the ordinary case rather than something missing.
|
||||
type Approach struct {
|
||||
Address string
|
||||
Fingerprint string
|
||||
Transport string
|
||||
}
|
||||
|
||||
// Asking is one open enrolment conversation.
|
||||
type Asking interface {
|
||||
// Ask puts the request to the mesh and waits for one answer, or says why none came.
|
||||
//
|
||||
// Called again, with the same bytes, while the mesh says "try again": the keys this node
|
||||
// generated are the ones it keeps, so the same request is the same enrolment and the mesh holds
|
||||
// the token for it (novox/hq issue 083).
|
||||
Ask(ctx context.Context, request []byte, wait time.Duration) ([]byte, error)
|
||||
|
||||
// Close lets go of the connection made with the token.
|
||||
Close()
|
||||
}
|
||||
|
||||
// Present opens an enrolment conversation with the mesh.
|
||||
//
|
||||
// The connection is made before anything is sent, and the certificate is checked while it is being
|
||||
// made — so a node pointed at the wrong bus finds out before its token has left the machine (ADR
|
||||
// 0004).
|
||||
func Present(ctx context.Context, to Approach, node, secret string,
|
||||
timeout time.Duration) (Asking, error) {
|
||||
|
||||
switch to.Transport {
|
||||
case OnNATS:
|
||||
return presentNats(ctx, to, node, secret, timeout)
|
||||
default:
|
||||
return presentCurrent(ctx, to, node, secret, timeout)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user