Class PeerNode
- All Implemented Interfaces:
AutoCloseable
GroupRuntime
per joined group, and the frame plumbing between them: every outbound frame is
a CBOR envelope carrying an Ed25519 signature, or traveling unsigned on a
channel the transport authenticated for exactly its sender (spec §5.6), and
every inbound frame passes that same check before any handler sees it.
Progress is tick-driven: tick() runs one membership probe round
and one self-advertisement refresh per group, plus one anti-entropy round
per group whenever that group's gossip period has elapsed on the node's
clock (spec §5.3). Tests call it explicitly with a test clock; wall-clock
deployments call startTicking.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final classBuilder forPeerNode.static enumHow this node authenticates frames on its connections (spec §5.6).static final recordThe leased, signed self-description that travels on thepeersstream: the canonical bytes of a PeerAdvertisement, the issuer's raw public key, and the signature over those bytes. -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final DurationThe tick cadence assumed when a host drivestick()by hand rather than throughstartTicking(Duration), and the starter's default foragentspaces.tick-millis. -
Method Summary
Modifier and TypeMethodDescriptionlongReturns how many channel-attested (unsigned) frames this node has sent.static PeerNode.Builderbuilder(PeerIdentity identity) Starts building a node.Returns the channel-authentication mode per connected peer, for the console and diagnostics:"attested"when frames to that peer ride an authenticated channel unsigned,"signed"otherwise.voidclose()voidcredentialHint(String key, String value) Sets one of this node's own credential hints (TODO-EFG §4): the value is carried underkeyin every group's next signed self-advertisement, so every member's hint listener sees it within one gossip period.longReturns how many inbound frames the wire codec refused outright (spec §9): failed signature, half-signed, unknown wire version, or garbage.Returns a joined group's runtime.identity()Returns the peer's identity.joinGroup(GroupAdvertisement groupAd, GroupMembership.Config membershipConfig, List<PeerAdvertisement.Endpoint> seeds) Joins a locally configured, literal-id group and returns its runtime.joinGroup(GroupAdvertisement groupAd, GroupMembership.Config membershipConfig, List<PeerAdvertisement.Endpoint> seeds, Map<String, String> membershipHints, MembershipValidator validator) Joins a group, presenting membership credentials and (for POLICY groups) an admission validator (spec §5.1).joinGroup(GroupId groupId, GroupMembership.Config membershipConfig, List<PeerAdvertisement.Endpoint> seeds, Duration timeout) Joins a group knowing only its GroupID and seed endpoints (spec §10.1join: "aspace://<groupID>"): asks each seed for the founding advertisement (GROUP_AD_WANT), waits for the first answer that verifies as the self-certifying founding document of exactly that GroupID (spec §4.4, §5.1), and then joins with it asjoinGroup(SignedGroupAdvertisement, GroupMembership.Config, List)would.joinGroup(SignedGroupAdvertisement founding, GroupMembership.Config membershipConfig, List<PeerAdvertisement.Endpoint> seeds) Joins a self-certifying group with its signed founding advertisement (spec §4.4, §5.1), verifying it first: the founder's key must hash to the issuer, the signature must verify over the founding fields, and the GroupID must equal the hash of the founding document.joinGroup(SignedGroupAdvertisement founding, GroupMembership.Config membershipConfig, List<PeerAdvertisement.Endpoint> seeds, Map<String, String> membershipHints, MembershipValidator validator) Joins a self-certifying group, verifying its founding advertisement and presenting membership credentials and a POLICY validator as injoinGroup(GroupAdvertisement, GroupMembership.Config, List, Map, MembershipValidator).voidRegisters a transport and starts listening on it.voidRegisters a transport, starts listening on it, and advertises the bind address at an explicit priority (spec §5.5): dialers try a peer's endpoints in ascending priority, so a node advertisingquicat 0 andtcpat 1 is reached over QUIC wherever the dialer speaks it.voidonCredentialHints(BiConsumer<PeerId, Map<String, String>> listener) Registers a credential-hint listener on a built node; the same contract asPeerNode.Builder.onCredentialHints(BiConsumer), for wiring that learns of the listener after the node exists (the Spring starter's authorizer bean).peerId()voidRemoves one of this node's own credential hints; the next self-advertisement no longer carries it.booleanWhether this node requires transport attestation of every frame's sender (the CA channel mode, spec §5.6).longReturns how many fully signed frames this node has sent.voidstartTicking(Duration period) Starts a background ticker for wall-clock deployments.voidtick()Runs one protocol round for every joined group: refresh the leased self-advertisement, probe membership, and — once per gossip period of the node's clock (spec §5.3,GossipParameters.period) — reconcile state with one partner.The wall-clock period betweentick()calls, when this node knows it: the periodstartTicking(Duration)was called with.voidRegisters a transport for outbound dialing only, without listening or advertising an endpoint.The transport schemes registered on this node, for dialing and for the endpoints it advertises.
-
Field Details
-
DEFAULT_TICK_PERIOD
The tick cadence assumed when a host drivestick()by hand rather than throughstartTicking(Duration), and the starter's default foragentspaces.tick-millis.
-
-
Method Details
-
builder
Starts building a node.- Parameters:
identity- the peer identity- Returns:
- the builder
-
identity
Returns the peer's identity. -
onCredentialHints
Registers a credential-hint listener on a built node; the same contract asPeerNode.Builder.onCredentialHints(BiConsumer), for wiring that learns of the listener after the node exists (the Spring starter's authorizer bean).- Parameters:
listener- receives the peer and its current hint map
-
credentialHint
Sets one of this node's own credential hints (TODO-EFG §4): the value is carried underkeyin every group's next signed self-advertisement, so every member's hint listener sees it within one gossip period. Refreshing a token is calling this again with the new value. Hints are bounded only by the advertisement they ride in; keep them small (an OIDC token is typically under 2 KiB). A per-group join hint of the same key takes precedence for that group.- Parameters:
key- the hint key, e.g.JoinCredentials.OIDC_HINT_KEYvalue- the hint value
-
removeCredentialHint
Removes one of this node's own credential hints; the next self-advertisement no longer carries it.- Parameters:
key- the hint key
-
requiresAttestation
public boolean requiresAttestation()Whether this node requires transport attestation of every frame's sender (the CA channel mode, spec §5.6). Exposed so a deployment can assert the posture it configured actually reached the node.- Returns:
- whether attestation is required
-
transportSchemes
The transport schemes registered on this node, for dialing and for the endpoints it advertises. A node in the attested mode registers only transports that can attest, so an unattestable scheme's absence here is what keeps an honest peer from dialing in on a channel that would get it evicted.- Returns:
- the registered schemes
-
peerId
-
signedFramesSent
public long signedFramesSent()Returns how many fully signed frames this node has sent. -
bareFramesSent
public long bareFramesSent()Returns how many channel-attested (unsigned) frames this node has sent. -
framesDropped
public long framesDropped()Returns how many inbound frames the wire codec refused outright (spec §9): failed signature, half-signed, unknown wire version, or garbage. Such frames name no verified sender, so they count here and strike nobody. -
channelModes
-
listen
Registers a transport and starts listening on it. The bind address is also advertised to other peers.- Parameters:
transport- the transportbindAddress- the transport-specific bind (and advertised) address- Throws:
IOException- if the address cannot be bound
-
listen
Registers a transport, starts listening on it, and advertises the bind address at an explicit priority (spec §5.5): dialers try a peer's endpoints in ascending priority, so a node advertisingquicat 0 andtcpat 1 is reached over QUIC wherever the dialer speaks it. The two-argument overload assigns priorities in listen order.- Parameters:
transport- the transportbindAddress- the transport-specific bind (and advertised) addresspriority- the advertised priority; lower dials first- Throws:
IOException- if the address cannot be bound
-
transport
Registers a transport for outbound dialing only, without listening or advertising an endpoint. This is the NAT-restricted posture (spec §5.4): the peer dials out to seeds and members, its self-advertisement carries no endpoints, and peers that need to reach it route through a RELAY-role member instead.- Parameters:
transport- the transport
-
joinGroup
public GroupRuntime joinGroup(GroupAdvertisement groupAd, GroupMembership.Config membershipConfig, List<PeerAdvertisement.Endpoint> seeds) Joins a locally configured, literal-id group and returns its runtime.This overload performs no self-certification check: the advertisement is this node's own configuration, handed in by the operator, so the network never had a chance to swap its policy. It is the right call for fleets whose members all ship the same configured group, and for tests. A group whose advertisement must be learned — fetched from a seed by GroupID, or received in any other way from the network — must instead go through
joinGroup(SignedGroupAdvertisement, GroupMembership.Config, List)orjoinGroup(GroupId, GroupMembership.Config, List, Duration), which verify the founding document (spec §4.4, §5.1). A literal-id group is never served to a newcomer asking by GroupID.- Parameters:
groupAd- the group's configured advertisementmembershipConfig- membership tuningseeds- bootstrap endpoints of any existing members; empty for the first member- Returns:
- the group runtime
-
joinGroup
public GroupRuntime joinGroup(SignedGroupAdvertisement founding, GroupMembership.Config membershipConfig, List<PeerAdvertisement.Endpoint> seeds) Joins a self-certifying group with its signed founding advertisement (spec §4.4, §5.1), verifying it first: the founder's key must hash to the issuer, the signature must verify over the founding fields, and the GroupID must equal the hash of the founding document. A document that fails is refused and nothing is joined. Members joined this way serve the document to newcomers that ask by GroupID.- Parameters:
founding- the signed founding advertisementmembershipConfig- membership tuningseeds- bootstrap endpoints of existing members- Returns:
- the group runtime
- Throws:
IllegalArgumentException- when the founding document does not verify
-
joinGroup
public GroupRuntime joinGroup(SignedGroupAdvertisement founding, GroupMembership.Config membershipConfig, List<PeerAdvertisement.Endpoint> seeds, Map<String, String> membershipHints, MembershipValidator validator) Joins a self-certifying group, verifying its founding advertisement and presenting membership credentials and a POLICY validator as injoinGroup(GroupAdvertisement, GroupMembership.Config, List, Map, MembershipValidator).- Parameters:
founding- the signed founding advertisementmembershipConfig- membership tuningseeds- bootstrap endpoints of existing membersmembershipHints- resource hints for our self-ad (join credentials)validator- POLICY admission validator, ornull- Returns:
- the group runtime
- Throws:
IllegalArgumentException- when the founding document does not verify
-
joinGroup
public GroupRuntime joinGroup(GroupId groupId, GroupMembership.Config membershipConfig, List<PeerAdvertisement.Endpoint> seeds, Duration timeout) throws TimeoutException Joins a group knowing only its GroupID and seed endpoints (spec §10.1join: "aspace://<groupID>"): asks each seed for the founding advertisement (GROUP_AD_WANT), waits for the first answer that verifies as the self-certifying founding document of exactly that GroupID (spec §4.4, §5.1), and then joins with it asjoinGroup(SignedGroupAdvertisement, GroupMembership.Config, List)would. Answers that fail verification — a different policy under the same id, a forged signature — are ignored, so a hostile seed can delay the join but never substitute the group; only honest seeds that themselves joined with the founding document ever answer.- Parameters:
groupId- the self-certifying GroupID to joinmembershipConfig- membership tuningseeds- seed endpoints to ask and then introduce ourselves totimeout- wall-clock bound on the fetch- Returns:
- the group runtime
- Throws:
TimeoutException- when no seed served a verified founding advertisement within the timeoutIllegalStateException- when the group is already joined or a fetch for it is already in progress
-
joinGroup
public GroupRuntime joinGroup(GroupAdvertisement groupAd, GroupMembership.Config membershipConfig, List<PeerAdvertisement.Endpoint> seeds, Map<String, String> membershipHints, MembershipValidator validator) Joins a group, presenting membership credentials and (for POLICY groups) an admission validator (spec §5.1). ThemembershipHintstravel in this peer's signed self-advertisement so other members can admit it: an INVITE group expectsJoinCredentials.HINT_KEYto carry a founder-issued credential. Thevalidator, when non-null, decides admission of other peers into a POLICY group.- Parameters:
groupAd- the group's founding advertisementmembershipConfig- membership timing configurationseeds- seed endpoints to introduce ourselves tomembershipHints- resource hints for our self-ad (join credentials)validator- POLICY admission validator, ornull- Returns:
- the joined group's runtime
-
group
Returns a joined group's runtime.- Parameters:
groupId- the group- Returns:
- the runtime, when joined
-
tick
public void tick()Runs one protocol round for every joined group: refresh the leased self-advertisement, probe membership, and — once per gossip period of the node's clock (spec §5.3,GossipParameters.period) — reconcile state with one partner. Rumor forwarding and probes are not paced by the period. Also announces on the bootstrap beacon, when one is configured, once per beacon interval. -
startTicking
Starts a background ticker for wall-clock deployments.- Parameters:
period- the tick period
-
tickPeriod
The wall-clock period betweentick()calls, when this node knows it: the periodstartTicking(Duration)was called with. Empty when the host drivestick()by hand, because then only the host knows the cadence, and a test advancing aTestClocka simulated second per tick is not ticking at any wall-clock rate at all.Layers above peering that express their semantics in ticks read this to convert to wall time, and must keep their own declared cadence when it is empty rather than substituting a guess; see
CapabilityProvider.driverCadence(QA3 A3-4).- Returns:
- the cadence ticks arrive at, or empty when hand-driven
-
close
public void close()- Specified by:
closein interfaceAutoCloseable
-