---
title: Features
description: Discovery extension capabilities
---

## Multiple Backends

All backends implement the same `Backend` interface, making it easy to switch between providers without changing application code:

```go
type Backend interface {
    Name() string
    Initialize(ctx context.Context) error
    Register(ctx context.Context, instance *ServiceInstance) error
    Deregister(ctx context.Context, serviceID string) error
    Discover(ctx context.Context, serviceName string) ([]*ServiceInstance, error)
    DiscoverWithTags(ctx context.Context, serviceName string, tags []string) ([]*ServiceInstance, error)
    Watch(ctx context.Context, serviceName string, onChange func([]*ServiceInstance)) error
    ListServices(ctx context.Context) ([]string, error)
    Health(ctx context.Context) error
    Close() error
}
```

### Backend Comparison

| Backend | Infrastructure | Multi-Node | Watch | Health Checks | Best For |
|---|---|---|---|---|---|
| **Memory** | None | No | No | No | Development, testing |
| **Consul** | Consul cluster | Yes | Yes | Yes | Production, full-featured |
| **etcd** | etcd cluster | Yes | Yes | No | Kubernetes environments |
| **mDNS** | None (LAN) | Yes (LAN) | Yes | No | Local development, zero-config |
| **Kubernetes** | K8s cluster | Yes | Yes | Yes | Kubernetes-native deployments |
| **Eureka** | Eureka server | Yes | No | Yes | Spring/Netflix ecosystems |

## Self-Registration

The discovery service automatically registers the current application as a service instance on startup and deregisters it on shutdown:

```go
svc := discovery.MustGet(app.Container())

// Manual registration (automatic if SelfRegister is enabled in config)
err := svc.Register(ctx, &discovery.ServiceInstance{
    ID:      "my-service-1",
    Name:    "my-service",
    Version: "1.0.0",
    Address: "10.0.0.5",
    Port:    8080,
    Tags:    []string{"http", "v1"},
    Metadata: map[string]string{
        "region": "us-east-1",
        "env":    "production",
    },
})
```

The `ServiceInstance` struct provides utility methods:

```go
instance.URL("http")         // "http://10.0.0.5:8080"
instance.HasTag("http")      // true
instance.HasAllTags([]string{"http", "v1"}) // true
instance.IsHealthy()         // true if Status == HealthPassing
instance.GetMetadata("region") // ("us-east-1", true)
```

## Service Discovery

Find service instances by name or tags:

```go
svc := discovery.MustGet(app.Container())

// Discover all instances of a service
instances, err := svc.Discover(ctx, "user-service")

// Filter by tags
instances, err := svc.DiscoverWithTags(ctx, "user-service", []string{"http", "v2"})

// Get only healthy instances
healthy, err := svc.DiscoverHealthy(ctx, "user-service")

// List all registered service names
services, err := svc.ListServices(ctx)
```

## Instance Selection

Built-in load balancing selects a single instance from the discovered set:

```go
// Select using a load balancing strategy
instance, err := svc.SelectInstance(ctx, "user-service", discovery.StrategyRoundRobin)

// Get a ready-to-use URL for a service
url, err := svc.GetServiceURL(ctx, "user-service", "http", discovery.StrategyLeastConnections)
// Returns: "http://10.0.0.5:8080"
```

## Service Watching

Subscribe to real-time service change events. When instances are added, removed, or their health changes, your callback is invoked:

```go
err := svc.Watch(ctx, "payment-service", func(instances []*discovery.ServiceInstance) {
    log.Printf("payment-service instances changed: %d available", len(instances))
    for _, inst := range instances {
        log.Printf("  - %s (%s:%d) healthy=%v", inst.ID, inst.Address, inst.Port, inst.IsHealthy())
    }
})
```

Watching is supported by Consul, etcd, mDNS, and Kubernetes backends.

## Health Checks

The discovery extension supports health checking of registered services:

- **Active probes** -- periodic HTTP health checks at configurable intervals (default 10s) with timeout (default 5s).
- **Failure threshold** -- mark as unhealthy after consecutive failures.
- **Critical deregister** -- automatically deregister services that remain unhealthy beyond the critical threshold (default 1m).
- **Service status** -- `HealthPassing`, `HealthWarning`, `HealthCritical`.

Health check behavior varies by backend. Consul provides the most comprehensive health check integration.

## FARP Protocol

The Forge API Registration Protocol (FARP) enables schema-driven API discovery. When enabled, the
extension exposes two HTTP endpoints and injects FARP metadata into the service's backend registration
(mDNS TXT records, Consul metadata, etcd keys). The API gateway reads this metadata to auto-generate
proxy routes.

### How FARP Works

```
┌─────────────────────┐       ┌──────────────────────┐       ┌───────────────────────┐
│   Upstream Service  │       │   Discovery Backend  │       │      API Gateway      │
│   (discovery ext)   │       │  (mDNS/Consul/etcd)  │       │    (gateway ext)      │
├─────────────────────┤       ├──────────────────────┤       ├───────────────────────┤
│ 1. Start            │──────▶│ 2. Register service  │       │                       │
│ 2. Expose /_farp/*  │       │    + FARP metadata   │       │                       │
│                     │       │    (farp.enabled,     │       │                       │
│                     │       │     farp.manifest,    │◀──────│ 3. Discover services  │
│                     │       │     farp.openapi)     │       │ 4. Read FARP metadata │
│                     │◀──────│                       │       │ 5. Fetch manifest     │
│ 5. Return manifest  │──────▶│                       │──────▶│ 6. Generate routes    │
└─────────────────────┘       └──────────────────────┘       └───────────────────────┘
```

### `GET /_farp/manifest`

Returns the service's API manifest including metadata and schemas:

```json
{
  "name": "user-service",
  "version": "1.0.0",
  "schemas": [
    {
      "type": "openapi",
      "specVersion": "3.0",
      "location": {
        "type": "url",
        "url": "/openapi.json"
      },
      "contentType": "application/json"
    }
  ],
  "capabilities": ["http", "websocket"]
}
```

### `GET /_farp/discovery`

Returns discovery information including instance address and available endpoints.

### FARP Metadata in Discovery

When a service registers with a backend, FARP metadata is injected automatically. The key metadata fields are:

| Key | Value | Description |
|---|---|---|
| `farp.enabled` | `"true"` | Signals that FARP is active on this service |
| `farp.manifest` | `"http://host:port/_farp/manifest"` | URL to fetch the full API manifest |
| `farp.openapi` | `"http://host:port/openapi.json"` | OpenAPI spec endpoint |
| `farp.asyncapi` | `"http://host:port/asyncapi.json"` | AsyncAPI spec endpoint (if configured) |
| `farp.graphql` | `"http://host:port/graphql"` | GraphQL endpoint (if configured) |
| `farp.strategy` | `"push"` | Registration strategy |
| `farp.capabilities` | `"[http rest]"` | Advertised capabilities |

The gateway extension checks for `farp.enabled=true` in discovered service metadata and uses
these URLs to generate proxy routes for each protocol.

### Schema Publishing

Publish API schemas (OpenAPI, AsyncAPI, GraphQL, gRPC) via FARP manifests for the gateway and other services to discover:

```go
discovery.NewExtension(
    discovery.WithFARPEnabled(true),
    discovery.WithFARPSchemas(
        discovery.FARPSchemaConfig{
            Type:        "openapi",
            SpecVersion: "3.0",
            Location: discovery.FARPLocationConfig{
                Type: "url",
                URL:  "/openapi.json",
            },
            ContentType: "application/json",
        },
        discovery.FARPSchemaConfig{
            Type:        "graphql",
            SpecVersion: "2023",
            Location: discovery.FARPLocationConfig{
                Type: "url",
                URL:  "/graphql",
            },
        },
    ),
)
```

### Auto-Register with FARP

When `AutoRegister` is enabled (the default), the FARP manifest is automatically registered with the discovery backend, allowing the API gateway to discover and proxy requests without manual configuration:

```go
discovery.NewExtension(
    discovery.WithFARPEnabled(true),
    discovery.WithFARPAutoRegister(true),  // default: true
)
```

### FARP Strategies

| Strategy | Behavior |
|---|---|
| `push` | Service pushes schema registration on startup (default) |
| `pull` | Gateway pulls schema from service on demand |
| `hybrid` | Push on startup with pull-based refresh |

## mDNS Discovery

Zero-configuration service discovery on local networks using multicast DNS. Ideal for local
development environments where no external infrastructure (Consul, etcd) is available.

### Basic mDNS Setup

```go
discovery.NewExtension(
    discovery.WithBackend("mdns"),
    discovery.WithServiceName("my-service"),
)
```

### mDNS with FARP

mDNS is the recommended backend for local development with FARP. FARP metadata is stored in
mDNS TXT records, so the gateway can discover services and their API schemas over the local
network without any infrastructure:

```go
// Service app (e.g. user-service)
discovery.NewExtension(
    discovery.WithEnabled(true),
    discovery.WithBackend("mdns"),
    discovery.WithServiceName("user-service"),
    discovery.WithFARPEnabled(true),
    discovery.WithFARPAutoRegister(true),
    discovery.WithFARPEndpoints(discovery.FARPEndpointsConfig{
        OpenAPI: "/openapi.json",
        Health:  "/health",
    }),
)
```

When the service registers via mDNS, the TXT records include:

```
version=1.0.0
id=user-service-abc123
farp.enabled=true
farp.manifest=http://192.168.1.10:8080/_farp/manifest
farp.openapi=http://192.168.1.10:8080/openapi.json
```

The gateway extension browsing for mDNS services reads these TXT records, detects
`farp.enabled=true`, and auto-generates proxy routes.

### mDNS Service Types

By default the service type is derived from the service name: `_<name>._tcp`. You can override
this or configure which service types to browse for:

```go
discovery.NewExtension(
    discovery.WithBackend("mdns"),
    discovery.WithServiceName("user-service"),
    // Custom service type for registration
    discovery.WithMDNSServiceType("_myapp._tcp"),
    // Browse for multiple service types during discovery
    discovery.WithMDNSServiceTypes("_myapp._tcp", "_api._tcp"),
)
```

### mDNS Configuration Options

| Option | Description |
|---|---|
| `WithMDNSServiceType(t)` | Set the mDNS service type for registration |
| `WithMDNSServiceTypes(t...)` | Set mDNS service types to browse during discovery |
| `WithMDNSBrowseTimeout(d)` | Timeout for browse operations (default: 3s) |
| `WithMDNSTTL(ttl)` | DNS record TTL |
| `WithMDNSWatchInterval(d)` | Watch polling interval |
| `WithMDNSInterface(iface)` | Bind to a specific network interface |
| `WithMDNSIPv6(enabled)` | Enable IPv6 support |
