A new AgentSpaces peer has to find the group it wants to join. This project lets it find that group through the directory your network already runs: DNS names, SRV records, DNS-SD, a Kubernetes service, or a document at an HTTPS address. Spring Boot and Micronaut applications turn it on with a few properties, and you can add your own way of finding a group as a bean.
Peer, group, seed, locator, beacon, and chain each have a specific meaning here, and several of them sound alike. The next section introduces them one at a time, with one running example. If you know the terms already, skip to Coordinates, or use the terms at a glance table as a reference.
The running example: Acme Corp runs a fleet of agents called research-fleet. Its network domain is corp.example. Each idea below appears in that fleet.
A peer is one running AgentSpaces process: a Spring Boot service, a Micronaut service, a Python script. Each peer has its own identity key. A peer can listen for connections on an address, or it can only dial out, as a laptop behind a home router does.
Example. Acme starts a peer called seed-a on a server. It listens on seed-a.corp.example:7500.
A group is a named set of peers that share state and gossip about who belongs. A group has a group id, which looks like aspace://z7f3a..., and a signed document called its founding, which the id is a hash of. A peer enters a group in one of two ways:
- Found it. You give the peer a founding text, such as
founding: research-fleet-v1. Every peer that receives the same text derives the same group id by itself, so there is no secret to distribute. - Join it by id. You give the peer the group id, as
join: aspace://z7f3a.... The peer asks a member for the founding document, checks that the document hashes to exactly the id it asked for, and only then introduces itself.
Example. seed-a founds research-fleet with founding: research-fleet-v1. The group now has one member.
Once a peer has met one member of a group, the fabric does the rest. The member's signed self-advertisement spreads over the peers stream, membership is learned by gossip, and if the peer later loses contact, the lost-member retry re-dials the members it last knew. A peer's view of a group is the list of members it currently knows.
A peer that has just started has an empty view and knows nobody. It has to dial something, and nothing inside the group can tell it what. Finding that first address is bootstrap. In this project the word means that one step. Admission and gossip start afterward, and the core handles them.
Example. Acme starts a new peer, summarizer, and configures it for research-fleet. summarizer knows the group's name and no addresses. Bootstrap is how it learns that seed-a.corp.example:7500 is a place to start.
A seed is an address that a newcomer dials first, written host:port or scheme://host:port (for example tls://seed-a.corp.example:7500). Any member that listens for connections can act as a seed. A peer that has joined no longer needs its seed.
Example. Acme configures summarizer with seeds: [ "seed-a.corp.example:7500" ]. summarizer dials seed-a, introduces itself, and learns the other members by gossip. It keeps working if seed-a goes away later.
The core means the AgentSpaces libraries this project builds on: the peering library, and the Spring Boot starter and Micronaut factory that host it. On its own, the core answers the bootstrap question in three ways:
- A static list of seeds for each group.
- Name expansion. When a seed is a name that DNS resolves to several addresses, the core dials every address of the name.
- An opt-in UDP multicast datagram that peers on one LAN segment use to announce themselves.
Example. In DNS, seeds.corp.example has two A records and one AAAA record, so it has three addresses. Acme configures seeds: [ "seeds.corp.example:7500" ], and the core dials all three.
A static list works until the addresses move. Kubernetes reschedules a seed pod and its address changes. A laptop joins the corporate Wi-Fi with no list at all. A partner organization holds only a group id and a URL. Enterprise networks already publish answers to "where is this service?" for everything else they run, and this project reads those answers.
A locator is one way of finding where to dial. The peer tells it which group it wants, and the locator answers with a hint: addresses to dial, in priority order. A hint can also carry the group id the locator believes the group has, the signed founding document, a lifetime (how long the answer stays valid), and the evidence that produced it, such as SRV research-fleet._agentspaces._tcp.corp.example or GET https://agentspaces.corp.example/.... A locator that has nothing returns an empty hint.
A locator only suggests addresses, and it never admits anyone. The peer dials each address as it would dial any stranger, so the usual signature checks and admission policy apply. A wrong DNS answer can delay a join, and it cannot place a stranger in the group.
The seed list and multicast are locators too, named seeds and multicast, and they come with the core. This project adds the others:
| Locator name | What it asks | Acme example |
|---|---|---|
seeds (core) |
the configured list, with name expansion | seeds.corp.example:7500 |
dns-sd |
DNS-SD (RFC 6763) under the network domain: PTR records at _agentspaces._tcp.<domain> name the instances, each instance's SRV records give hosts and ports, and its TXT record gives group, id, scheme, and url |
the instance research-fleet._agentspaces._tcp.corp.example, with SRV records for seed-a and seed-b |
dns-txt |
one TXT record at agentspaces.<domain> that holds url=, the address of a beacon document |
agentspaces.corp.example TXT "url=https://agentspaces.corp.example/.well-known/agentspaces/beacon.json" |
dns-srv |
SRV records (RFC 2782) at an owner name you choose | _aspace._tcp.agentspaces-seeds.fleet.svc.cluster.local |
k8s |
the SRV records Kubernetes publishes for a named port of a headless service, with no Kubernetes client | service agentspaces-seeds, namespace fleet, port aspace |
url |
fetches a beacon document from an HTTPS address | https://agentspaces.corp.example/.well-known/agentspaces/beacon.json |
document |
reads a beacon document from a file or classpath resource, for a network with no outside access | /etc/agentspaces/beacon.json |
multicast (core) |
the addresses this peer heard in LAN multicast datagrams | a lab where every peer shares one network segment |
The network domain is the DNS domain the host belongs to. dns-sd and dns-txt look under it. With domain: auto, the peer finds it the way the host does, from the AGENTSPACES_DOMAIN environment variable, the search and domain lines of /etc/resolv.conf, or on Windows the search list that ipconfig /all prints. A laptop on Acme's Wi-Fi receives the search domain corp.example from DHCP, so dns-sd finds research-fleet without any seeds configured.
The locators that read DNS do their lookups through a small interface, DnsResolver. The default implementation uses the JDK's JNDI provider and adds no dependency. The agentspaces-bootstrap-dns project supplies one with DNS over HTTPS and DNSSEC validation.
The word beacon names three different things. This section keeps them apart.
- A beacon is a peer that newcomers can find and dial first. It is an ordinary group member with a stable address, usually playing the
RENDEZVOUSrole (a peer that caches many members' advertisements and answers discovery queries). Nothing in the group protocol marks a peer as a beacon: the word describes how an operator uses a member. Operators run a beacon so that newcomers have somewhere to go. Theagentspaces-beaconproject is a ready-made one. It runs as a standalone peer, serves the document below, and prints the DNS records an operator registers. - The beacon document is a signed JSON file that a beacon serves at
/.well-known/agentspaces/beacon.json. For each group it lists the name, the id, the signed founding, the seeds, and the wire hints. It never carries a secret. A newcomer that holds only a group id and the document's URL can fetch the document, check the founding against the id, and learn the seeds. Theurlanddocumentlocators read it, anddns-sdanddns-txtfollow aurl=record to it. - The multicast beacon is the UDP datagram, off by default, that members send so that peers on the same LAN segment find each other without configuration. It is a core feature, and this project lists it in the chain as the
multicastlocator.
Example. Acme runs a beacon peer at beacon.corp.example. It serves this document at https://agentspaces.corp.example/.well-known/agentspaces/beacon.json:
{
"v": 1,
"issuer": "z6Mk...beaconPeerId",
"issued": "2026-10-13T14:02:11Z",
"ttl": 300,
"groups": [
{
"name": "research-fleet",
"id": "aspace://z7f3a...",
"founding": "<base64 of the signed founding advertisement>",
"seeds": [
{ "transport": "tls", "address": "seed-a.corp.example:7500", "priority": 0 },
{ "transport": "tls", "address": "seed-b.corp.example:7500", "priority": 1 },
{ "transport": "tls", "address": "beacon.corp.example:7500", "priority": 2 }
]
}
],
"signature": "<base64 Ed25519 signature over every other field>"
}A partner organization's peer holds only join: aspace://z7f3a... and this URL. It fetches the document, verifies the founding against the id, and dials the seeds.
A chain is the ordered list of locators that one group uses. Every group has its own chain, because each group can need a different way of being found: research-fleet through DNS inside Acme, a partner's group through a URL. The host builds each group's chain from the fleet-wide agentspaces.bootstrap.* settings, then applies the overrides in that group's own bootstrap block. The core's BootstrapChain class implements the chain, and ChainComposer in this project builds it.
Example. summarizer belongs to two groups, and each has its own chain:
agentspaces:
groups:
- name: research-fleet
join: aspace://z7f3a...
seeds: [ "seeds.corp.example:7500" ]
bootstrap:
order: [ seeds, dns-sd, url ] # research-fleet's chain
url: https://agentspaces.corp.example/.well-known/agentspaces/beacon.json
- name: partner-fleet
join: aspace://q9c2b...
bootstrap:
order: [ url ] # partner-fleet's chain
url: https://partner.example/.well-known/agentspaces/beacon.json
beacon-peer: z6Mk...partnerBeacon # the PeerId that must have signed the documentHow a chain answers. When a peer asks a chain for a group, the chain works through these steps:
- It picks the locators that apply to that group. A locator you define as a bean can limit itself to one group.
- By default it asks all of them at once, each with its own deadline (
timeout, 3 seconds). A locator that fails or times out contributes nothing, and the others carry on. - It merges the answers in chain order, so the first locator's addresses rank highest, and it removes duplicates.
- It keeps the first founding document it finds. It accepts a group id claim only from the locators you named in
accept-group-id-from. - It expands names into addresses last.
- It sets the answer's lifetime to the shortest lifetime among the locators that contributed.
With first-wins: true, the chain asks one locator at a time and stops at the first one that returns addresses.
Seeds first. In the default order, seeds is the first entry. The chain therefore asks the configured list first and ranks its addresses first in the merged answer. With first-wins: true, a non-empty seed list also means that no later locator is asked. After the seeds, the chain runs the locators your properties name, in order, and then any BeaconLocator beans your application defines. bean-locators-first: true puts the beans before the property locators. When multicast is enabled, the host adds the multicast locator in the same place as the application's beans.
When the chain runs. A peer asks its group's chain when it joins, and again whenever its view of the group is empty. The re-asks come no sooner than refresh-min (30 seconds) and no later than refresh-max (10 minutes). If the first answer holds no addresses and no founding, the peer logs one line and waits for the next ask. With require: true, the application instead fails at startup and names the group. With trace: true, the peer logs every locate at INFO, with each locator's evidence.
Example. summarizer starts on a laptop inside Acme. Its chain for research-fleet is [seeds, dns-sd, url]:
summarizer wants research-fleet
|
v
research-fleet's chain: [seeds, dns-sd, url], all asked at once
+--------------------+-----------------------+---------------------+
| seeds | dns-sd | url |
| seeds.corp.example | SRV: seed-a, seed-b | document lists |
| :7500 | TXT: url=... | seed-a, seed-b, |
| | | beacon, and the |
| | | founding |
+--------------------+-----------------------+---------------------+
|
v
merge in chain order, remove duplicates, expand names to addresses
|
v
dial the addresses in order, check the founding against the id,
introduce itself; from here gossip takes over
A chain suggests where to dial. The peer decides what to believe:
- Addresses are only dialed. Every connection goes through the same handshake, signature verification, and admission policy as a connection from any stranger.
- A founding is verified. The peer checks it against the id it was configured with. A peer configured with no id checks it against itself.
- A group id claim needs a named source. A locator can say what it believes the group id is. The chain accepts that claim only from the locators listed in
accept-group-id-from, which default tourlanddocument, the sources that arrive over HTTPS or from a file you control. Plain DNS answers carry no signature. Namingdns-sd,dns-txt,dns-srv,k8s, ormulticastthere stops the application at startup unless you setinsecure-group-id: trueor give the host aDnsResolverwhose answers are authenticated, such as the DNSSEC-validating one inagentspaces-bootstrap-dns. - A document can be pinned.
beacon-peernames the PeerId that must have signed any document. Theurllocator also fetches over HTTPS by default, caps the size, follows at most two redirects on the same host, and refuses private addresses unlessurl-allow-privateallows them.
Example. An attacker poisons the DNS answer for seeds.corp.example, and summarizer dials the attacker's host. That host cannot show a founding document that hashes to aspace://z7f3a..., so summarizer does not join it and asks the chain again later.
A host is the framework that creates your peer: the Spring Boot starter (agentspaces-spring-boot-starter) or the Micronaut factory (agentspaces-micronaut). The host builds one chain per group from the agentspaces.bootstrap.* properties and from any BeaconLocator beans you define. This project provides the library of locators and the beacon document, the chain composition both hosts share, and one adapter for each host. Coordinates lists the four artifacts. The sections after it show the properties, the bean interfaces, and recipes for Kubernetes and Docker Compose.
| Term | What it is | In the Acme example |
|---|---|---|
| Peer | one running AgentSpaces process | seed-a, summarizer |
| Group | a named set of peers that share state | research-fleet |
| Group id | the aspace:// identifier, a hash of the founding |
aspace://z7f3a... |
| Founding | the signed document that defines a group | made from founding: research-fleet-v1 |
| View | the members a peer currently knows | empty when summarizer starts |
| Bootstrap | finding where to dial first | how summarizer learns about seed-a |
| Seed | an address a newcomer dials first | seed-a.corp.example:7500 |
| Core | the AgentSpaces libraries this project builds on; they provide the seed list, name expansion, and multicast | the seeds list in summarizer's configuration |
| Name expansion | dialing every address that a seed name resolves to | seeds.corp.example resolves to three addresses |
| Locator | one way of finding where to dial | seeds, dns-sd, url |
| Hint | a locator's answer: addresses, and optionally an id, a founding, a lifetime, and evidence | the SRV answer that lists seed-a and seed-b |
| Network domain | the DNS domain the host belongs to, usually the DHCP search domain | corp.example |
| Beacon | a peer that newcomers can find and dial first | beacon.corp.example |
| Beacon document | the signed JSON a beacon serves at /.well-known/agentspaces/beacon.json |
the document in the Beacon section |
| Multicast beacon | the opt-in LAN datagram that members send | a lab on one network segment |
| Rendezvous | a role for a peer that caches many members' advertisements and answers discovery queries | beacon.corp.example |
| Chain | the ordered locators one group uses | [seeds, dns-sd, url] |
| Group's chain | each group has its own chain, built from fleet-wide settings plus the group's overrides | research-fleet uses [seeds, dns-sd, url] and partner-fleet uses [url] |
| Seeds first | seeds leads the default order, so the configured list is asked first and ranks first |
seeds.corp.example heads the merged answer |
| First-wins | a chain setting that asks one locator at a time and stops at the first that returns addresses | first-wins: true |
| Host | the framework that creates the peer and builds the chains | Spring Boot or Micronaut |
DnsResolver |
the interface the DNS locators use for lookups | JNDI by default, DNS over HTTPS and DNSSEC in agentspaces-bootstrap-dns |
| Artifact | What | Depends on |
|---|---|---|
ai.badmonkey.agentspaces:agentspaces-bootstrap:0.4.0 |
the library: DnsResolver and the JNDI resolver, SrvLocator, DnsSdLocator, TxtLocator, UrlLocator, K8sLocator, StaticDocumentLocator, DomainFinder, BeaconDocument, BootstrapSettings, LocatorAssembly |
agentspaces-api, -common, -identity, Jackson |
ai.badmonkey.agentspaces:agentspaces-bootstrap-chain:0.4.0 |
ChainComposer, the composition both hosts share, and BootstrapTree, the nested-map binding of the keys |
the library, agentspaces-peering (BootstrapChain), the core autoconfigure jar (the BootstrapChainFactory seam; it carries no Spring class) |
ai.badmonkey.agentspaces:agentspaces-bootstrap-spring-boot-autoconfigure:0.4.0 |
BootstrapProperties, BootstrapAutoConfiguration |
chain, Spring Boot 4.1 |
ai.badmonkey.agentspaces:agentspaces-bootstrap-micronaut:0.4.0 |
BootstrapFactory |
chain, Micronaut 5.2 |
An application adds the host module beside the core starter (agentspaces-spring-boot-starter)
or the core Micronaut factory (agentspaces-micronaut); the library and the
chain come transitively.
<dependency>
<groupId>ai.badmonkey.agentspaces</groupId>
<artifactId>agentspaces-bootstrap-spring-boot-autoconfigure</artifactId>
<version>0.4.0</version>
</dependency>agentspaces:
keystore: ./peer-keys
bind: 0.0.0.0:7500
advertise: ${POD_IP:localhost}:7500 # what others dial (core starter)
roles: [ rendezvous ] # on the beacon only
bootstrap: # fleet-wide defaults
domain: auto # or [ corp.example, fleet.svc.cluster.local ]
order: [ seeds, dns-sd, dns-txt, url, multicast ]
timeout: PT3S
refresh-min: PT30S
refresh-max: PT10M
accept-group-id-from: [ url ]
beacon-peer: "" # PeerId to pin the document signer, optional
groups:
- name: research-fleet
join: aspace://z7f3a... # or founding: research-fleet-v1
seeds: [ "seeds.corp.example:7500" ] # one name, three addresses: all three dialed
bootstrap: # per-group overrides
dns-srv: _aspace._tcp.agentspaces-seeds.fleet.svc.cluster.local
url: https://agentspaces.corp.example/.well-known/agentspaces/beacon.json
multicast: { enabled: false } # unchangedWith no bootstrap block anywhere, the chain is seeds then name expansion
and nothing else: the core's behaviour. With one, each group's chain is the
seeds first (when order names them), then the locators the properties ask
for in order, then the application's BeaconLocator beans (or the beans
first under bean-locators-first), with timeout, first-wins,
accept-group-id-from, and expand-names applied. A group's block overrides
the keys the fleet-wide block set. A misconfigured block (an unknown name in
order, accept-group-id-from naming a plain-DNS locator without
insecure-group-id: true) fails the context at start, naming the property.
Every step of "how this peer finds its group" is a bean the application may
define; the defaults are @ConditionalOnMissingBean. The core starter
collects the BeaconLocator, Transport, PeerNodeCustomizer, and
PeerNodeConfigurer beans; this module adds DnsResolver and
BootstrapChainFactory.
@Configuration
class FleetBootstrapConfig {
/** A locator for a registry the starter does not know. Applies to every group. */
@Bean @Order(10)
BeaconLocator cloudMapLocator(DnsResolver dns) {
return new SrvLocator("_aspace._tcp.agentspaces.corp.internal", "tls", dns);
}
/** A locator for one group only: override appliesTo. */
@Bean
BeaconLocator partnerDocument() {
UrlLocator partner = new UrlLocator(
List.of(URI.create("https://partner.example/.well-known/agentspaces/beacon.json")),
UrlLocator.Options.defaults(), null);
return new BeaconLocator() {
public String name() { return "partner"; }
public BeaconHint locate(BeaconRequest r) throws IOException { return partner.locate(r); }
public boolean appliesTo(String group) { return "partner-fleet".equals(group); }
};
}
/** Replace the resolver (DNS over HTTPS, DNSSEC): the agentspaces-bootstrap-dns project. */
@Bean
DnsResolver dnsResolver() { return new DnsJavaResolver("https://dns.corp.example/dns-query"); }
/** Touch the node builder without replacing the node (core starter seam). */
@Bean
PeerNodeCustomizer tenMinuteAds() { return b -> b.peerAdTtl(Duration.ofMinutes(10)); }
/** Change the composition policy for every group: the locators arrive as [seeds, beans...]. */
@Bean
BootstrapChainFactory firstWins() {
return (group, locators) -> BootstrapChain.builder().firstWins().addAll(locators).build();
}
}Rule of precedence: a BootstrapChainFactory bean wins over everything else;
otherwise the chain is the property order, with bean locators appended (or
prepended under bean-locators-first), then name expansion.
Beans in the module, by name and type:
| Bean | Type | Condition |
|---|---|---|
bootstrapDnsResolver |
ai.badmonkey.agentspaces.bootstrap.DnsResolver (JndiDnsResolver) |
no DnsResolver bean |
bootstrapChainFactory |
ai.badmonkey.agentspaces.spring.BootstrapChainFactory |
no BootstrapChainFactory bean |
agentspaces-ai.badmonkey...BootstrapProperties |
BootstrapProperties (@ConfigurationProperties("agentspaces")) |
always |
BootstrapAutoConfiguration runs before the core AgentSpacesAutoConfiguration
and is registered in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports;
META-INF/additional-spring-configuration-metadata.json describes every key
for the IDE.
The same keys bind through BootstrapFactory into a BootstrapTree (the
Micronaut environment's agentspaces subtree converted the way the core's
FleetConfiguration binds AgentSpacesProperties: kebab-case keys to the
POJO, durations in ISO-8601, milliseconds, or the 5m short form). The seams
are @Singleton beans with @Requires(missingBeans = ...): define a
DnsResolver or a BootstrapChainFactory bean and the default steps aside.
The core's AgentSpacesFactory collects BeaconLocator, Transport,
PeerNodeCustomizer, and PeerNodeConfigurer beans as List<T> injection
points (in @Order order) and takes the BootstrapChainFactory bean when
one exists. The factory is off under agentspaces.enabled: false, like the
core's.
@Factory
class FleetBootstrapConfig {
@Singleton
BeaconLocator cloudMapLocator(DnsResolver dns) {
return new SrvLocator("_aspace._tcp.agentspaces.corp.internal", "tls", dns);
}
}apiVersion: v1
kind: Service
metadata: { name: agentspaces-seeds, namespace: fleet }
spec:
clusterIP: None # headless: one A record per pod, SRV per named port
selector: { app: agentspaces-seed }
ports: [ { name: aspace, port: 7500 } ]
---
# seeds: a StatefulSet of 3 with
# agentspaces.bind: 0.0.0.0:7500
# agentspaces.advertise: ${POD_IP}:7500 (downward API: status.podIP)
# agentspaces.roles: [rendezvous] on ordinal 0
# workers: a Deployment with
# agentspaces.groups[0].bootstrap.k8s: { service: agentspaces-seeds, namespace: fleet, port-name: aspace }
# agentspaces.advertise: ${POD_IP}:7500 (or no bind: dial-only workers behind a RELAY seed)dig SRV _aspace._tcp.agentspaces-seeds.fleet.svc.cluster.local shows the
three pods; the k8s locator asks exactly that. Equivalent, with no
convenience key: bootstrap.dns-srv: _aspace._tcp.agentspaces-seeds.fleet.svc.cluster.local.
Compose's embedded DNS answers a service name with one A record per replica,
so a seed service scaled to three answers with three addresses and name
expansion (expand-names, on by default) dials all three:
services:
seeds:
image: my-fleet:latest
deploy: { replicas: 3 }
environment:
AGENTSPACES_BIND: 0.0.0.0:7500
AGENTSPACES_GROUPS_0_NAME: fleet
AGENTSPACES_GROUPS_0_FOUNDING: fleet-v1
workers:
image: my-fleet:latest
environment:
AGENTSPACES_GROUPS_0_NAME: fleet
AGENTSPACES_GROUPS_0_JOIN: aspace://z7f3a...
AGENTSPACES_GROUPS_0_SEEDS_0: seeds:7500 # one name, three addressesThe keys this project binds, fleet-wide under agentspaces.bootstrap.* and
per group under agentspaces.groups[].bootstrap.* (a group's set keys win).
| Key | Default | Meaning |
|---|---|---|
domain |
auto |
a list of domains, or auto for the host's search list, or empty for none |
order |
[seeds, dns-sd, dns-txt, dns-srv, k8s, url, document, multicast] |
chain order; a locator absent from the list is not asked (dns-srv, k8s, url, document only appear when their key is set) |
first-wins |
false |
stop at the first locator with endpoints |
timeout |
PT3S |
per locator |
refresh-min / refresh-max |
PT30S / PT10M |
re-ask pacing on an empty view (the chain's LocatePolicy; the node honours it) |
accept-group-id-from |
[url, document] |
locators whose GroupId claim is honoured when none is configured |
insecure-group-id |
false |
allow an unauthenticated locator (dns-*, k8s, multicast) in the list above |
beacon-peer |
empty | PeerId that must have signed any document |
url |
empty | beacon document URLs, comma or space separated |
url-allow-plain / url-allow-hosts / url-allow-private |
false / any / false |
URL locator guards |
expand-names |
true |
one endpoint per address of a seed name |
bean-locators-first |
false |
place bean locators before property locators |
require |
false |
fail startup, naming the group, when the first locate answers no endpoint and no founding |
trace |
false |
log every locate at INFO with each locator's evidence |
dns-srv |
empty | the SRV owner name (_aspace._tcp.seeds.example) |
dns-srv-scheme |
the node's default | the scheme SRV and Kubernetes endpoints take |
dns-sd-any-group |
false |
accept any DNS-SD instance, not only the group's own name |
document |
empty | a beacon document as is: a file path or a classpath: resource |
k8s.service / k8s.namespace / k8s.port-name / k8s.cluster-domain |
empty / the pod's / empty / cluster.local |
the headless service whose pods are the seeds |
Unchanged and bound by the core starter: agentspaces.groups[].seeds,
agentspaces.groups[].join, join-timeout-millis, agentspaces.advertise,
agentspaces.roles, agentspaces.multicast.*.
mvn clean install
Java 21. The tests start real peers on loopback ports; agentspaces-micronaut
0.4.0 (the core's Micronaut host) must be in the local repository for the
Micronaut module's end-to-end test.
The central-publish profile attaches the sources and javadoc jars, signs
every artifact with GPG (the passphrase from MAVEN_GPG_PASSPHRASE, or a GPG
agent), and uploads through the Sonatype Central Portal using the central
server id in settings.xml:
mvn -Pcentral-publish -DskipTests -Dgpg.skip=true clean package # dry run
mvn -Pcentral-publish clean deploy # releaseautoPublish is off, so the upload waits on the Portal as a validated bundle
until you publish it there. The bundle holds the four modules and their parent
POM, agentspaces-bootstrap-parent. Publish this project first:
agentspaces-bootstrap-dns and agentspaces-beacon depend on it.