Service Discovery¶
| Status | Implemented |
|---|---|
| Revision | 2026-10-27, skywire c18b044dc |
| Code | pkg/deployment/sd, pkg/services/sd, pkg/servicedisc |
Abstract¶
Service Discovery (SD) is a registry of the services that visors offer. A visor registers entries for the services it runs, such as a VPN server, a proxy, a public visor entry or a coin node, and clients query the registry by service type. SD also tracks visor uptime.
Overview¶
Visors register and refresh their own entries. Clients such as the VPN client, the proxy client and autoconnect list entries by type and choose among them. Entries are also available as a CXO feed, which visors prefer to polling the HTTP API (see Public Autoconnect).
Addressing¶
SD is served over dmsg HTTP at dmsg://<sd-pk>:80. A service entry is
addressed by servicedisc.SWAddr, the text form <public-key>:<port>. A
missing port reads as 0.
Data Formats¶
Service¶
servicedisc.Service, the request body of POST /api/services and the
response element of the read routes.
| JSON key | Type | Required | Meaning |
|---|---|---|---|
address |
string | yes | SWAddr of the service |
type |
string | yes | service type |
geo |
object | no | lat, lon, country, region (geo.LocationData) |
display_node_ip |
bool | no | whether local_ips is returned to readers |
version |
string | for vpn, skysocks |
visor version |
local_ips |
[]string | for visor |
addresses of the visor; must contain the observed source address |
info |
object | no | servicedisc.VPNInfo: latency, uptime, connections |
coin |
object | no | servicedisc.CoinInfo, for type coin |
Service.Check requires a non-null key and a non-zero port.
Service types¶
| Constant | Value |
|---|---|
servicedisc.ServiceTypeVisor |
visor |
servicedisc.ServiceTypeVPN |
vpn |
servicedisc.ServiceTypeSkysocks |
skysocks |
servicedisc.ServiceTypeProxy |
proxy, an alias of skysocks on the read routes |
servicedisc.ServiceTypeCoin |
coin |
Errors¶
servicedisc.HTTPError: {"code": <status>, "error": "<text>"}.
Uptimes¶
store.VisorSummary.
| JSON key | Type | Meaning |
|---|---|---|
pk |
string | visor key |
on |
bool | whether the visor currently has a live registration |
version |
string | from v=v2 and v=v3 |
daily |
object | day to uptime percentage, from v=v2 and v=v3 |
timeline |
object | day to a 288 character bitmap, from v=v3 |
Interfaces¶
HTTP API¶
The request body limit is 1 MiB and each request has a 30 s timeout
(httpTimeout). CORS allows any origin. Routes marked yes use
httpauth.MakeMiddleware when SD has a nonce store (see HTTP Authorization
Middleware). The route marked list accepts only keys in the deregistration
whitelist.
| Method and path | Auth | Purpose |
|---|---|---|
GET /api/services |
no | list entries of a type |
GET /api/services/{addr} |
no | get one entry |
POST /api/services |
yes | register or refresh an entry |
DELETE /api/services/{addr} |
yes | remove an entry |
DELETE /api/services/deregister/{type} |
list | remove entries of the listed keys |
GET /uptimes, POST /uptimes |
no | visor uptimes |
GET /health |
no | httputil.HealthCheckResponse, service name service-discovery |
GET / |
no | status page |
GET /security/nonces/{pk} |
no | next nonce, when a nonce store exists |
GET /api/services requires the query parameter type. Optional parameters
are version and country, which filter exactly, and quantity, which returns
a random sample of that many entries when more match. Local addresses are
removed from entries that do not set display_node_ip. Responses are 200 with
an array (empty when nothing matches), 400 when type is missing or quantity
is not an integer, and 500 on a store error.
GET /api/services/{addr}?type=<type> returns 200 with one entry, 400 when
type is missing or addr does not parse, 404 when the entry does not exist,
and 500 on a store error.
POST /api/services stores the entry and returns 200 with the stored entry.
The response status follows the order of checks.
- 400 when the body is not valid JSON.
- 500 for a
visorentry when no geo data is found for the observed address. Other types register without geo data. - 403 when the entry key differs from the authenticated key.
- 403 for
vpnandskysockswith an emptyversion. - 403 for
visorwhen the observed address is not inlocal_ips. - 500 on a store error.
DELETE /api/services/{addr}?type=<type> returns 200 with true, 400 when
type is missing or addr does not parse, 403 on a key mismatch, and 500 on a
store error.
DELETE /api/services/deregister/{type} accepts only keys in the deregistration whitelist. It
does not use the nonce middleware (see
02). The body is a JSON array of
public key strings, and each key is removed from the entries of {type} with
port 0. Responses are 200 with true, 400 for an unparsable key, signature or
body, 403 for a key outside the whitelist or a bad signature, and 500 on a
store error.
GET /uptimes returns an array of VisorSummary from a cache refreshed every
5 minutes (uptimesCacheDelay). The parameter v selects v2 or v3. The
parameter visors is a semicolon separated key list that filters the result. The
v3 form computes timelines on request. POST /uptimes takes
{"pks":[...],"v":"v2"} and answers the same way, with 400 for an invalid body.
CXO Feeds¶
| Feed | Port | Publisher | Constant |
|---|---|---|---|
| services | 53 | SD | skyenv.DmsgSDServicesCXOPort |
| visor registration | 72 | each visor | skyenv.DmsgVisorSDRegCXOPort |
Services feed. The leaf services/<type>/all holds all live entries of a
type. The body is a version byte (servicesBatchVersion, 1) and a gzip frame of
a JSON array of Service, sorted by key. An entry that is deregistered or has
expired is absent from the next version of its leaf. The tree is rebuilt from the
store every 60 s (servicesCXORepublishInterval) and on each registration or
removal.
Visor registration feed. A visor publishes its own entries as the leaf
services, in the same framing, and announces the feed to SD. SD subscribes
back (regcxo.Aggregator) and passes each entry to IngestServiceFromCXO. An
entry whose key is not the publisher's is dropped. When an entry exists, the
ingest refreshes its TTL and keeps the stored value. When none exists, entries
of type visor are not created, and others are created if vpn and skysocks
entries carry a version. The feed has no delete. An entry the visor stops
publishing expires.
Behaviour¶
Store and lifetime¶
Entries are Redis keys service:<type>:<pk> with a TTL equal to the service
entry_timeout setting, 5 minutes by default (cmd/svc/service-discovery).
A set service:<type> indexes the keys of a type, and a set
sd:visor-svc:<pk-hex> lists the types a visor has registered. Visors refresh
entries every 90 s (skyenv.ServiceDiscUpdateInterval). Index members whose key
has expired are removed when read and by a cleanup that runs every half TTL.
Each registration also records a heartbeat for the visor. The uptime counters have a 8 day expiry. A visor counts as online while it holds a live registration.
Security Considerations¶
Registration and removal are authenticated, and a visor can only register or
remove entries under its own key. Reads are open. A visor entry is accepted
only when the observed source address is in its local_ips. Entries expose
local_ips only when the registering visor sets display_node_ip.
References¶
- HTTP Authorization Middleware
- Public Visors
- Public Autoconnect