Discovery

Discovery

Service discovery and registry with multiple backends and FARP integration

Overview

github.com/xraph/forge/extensions/discovery provides service discovery and registration for Forge applications. It registers a Service in the DI container that manages service instances across pluggable backends (memory, Consul, etcd, mDNS) and optionally exposes FARP endpoints for schema-based API discovery.

What It Registers

ServiceDI KeyType
Discovery servicediscovery*Service

The service is registered with Vessel and supports the legacy alias discovery.Service.

Quick Start

package main

import (
    "context"
    "fmt"

    "github.com/xraph/forge"
    "github.com/xraph/forge/extensions/discovery"
)

func main() {
    app := forge.NewApp(forge.AppConfig{Name: "user-service", Version: "1.0.0"})

    app.RegisterExtension(discovery.NewExtension(
        discovery.WithEnabled(true),
        discovery.WithBackend("consul"),
        discovery.WithService(discovery.ServiceConfig{
            Name:    "user-service",
            Version: "1.0.0",
            Port:    8080,
            Tags:    []string{"api", "v1"},
            Metadata: map[string]string{"team": "platform"},
        }),
        discovery.WithConsul(discovery.ConsulConfig{
            Address: "localhost:8500",
        }),
    ))

    ctx := context.Background()
    app.Start(ctx)  // auto-registers with Consul
    defer app.Stop(ctx)  // auto-deregisters

    svc, _ := forge.InjectType[*discovery.Service](app.Container())

    // Discover other services
    instances, _ := svc.Discover(ctx, "order-service")
    for _, inst := range instances {
        fmt.Printf("Found: %s at %s\n", inst.ID, inst.URL("http"))
    }

    // Discover healthy instances only
    healthy, _ := svc.DiscoverHealthy(ctx, "order-service")

    // Select one with load balancing
    selected, _ := svc.SelectInstance(ctx, "order-service", "round-robin")
    fmt.Printf("Selected: %s\n", selected.URL("http"))

    // Watch for changes
    svc.Watch(ctx, "order-service", func(instances []discovery.ServiceInstance) {
        fmt.Printf("Service updated: %d instances\n", len(instances))
    })
}

FARP Integration

FARP (Forge API Registration Protocol) lets your service publish API schema metadata so the gateway can automatically discover and proxy traffic to it. When FARP is enabled, the extension registers two HTTP endpoints (/_farp/manifest and /_farp/discovery) and injects FARP metadata into the service's discovery registration (mDNS TXT records, Consul metadata, etcd keys, etc.).

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",
    }),
)
// Exposes: GET /_farp/manifest and GET /_farp/discovery
// mDNS TXT records now include farp.enabled=true, farp.manifest=..., farp.openapi=...

FARP with mDNS (Zero-Config Local Development)

The most common local-development setup uses mDNS with FARP. No external infrastructure is needed -- services and gateways discover each other over the local network automatically:

package main

import (
    "context"

    "github.com/xraph/forge"
    "github.com/xraph/forge/extensions/discovery"
)

func main() {
    app := forge.NewApp(forge.AppConfig{Name: "order-service", Version: "1.0.0"})

    app.RegisterExtension(discovery.NewExtension(
        discovery.WithEnabled(true),
        discovery.WithBackend("mdns"),
        discovery.WithServiceName("order-service"),

        // Enable FARP so the gateway can auto-discover this service.
        discovery.WithFARPEnabled(true),
        discovery.WithFARPAutoRegister(true),
        discovery.WithFARPEndpoints(discovery.FARPEndpointsConfig{
            OpenAPI: "/openapi.json",
            Health:  "/health",
        }),
        discovery.WithFARPCapabilities("http", "rest"),
    ))

    ctx := context.Background()
    app.Start(ctx)
    defer app.Stop(ctx)
}

When this service starts it:

  1. Registers itself via mDNS on the local network (service type _order-service._tcp).
  2. Includes FARP metadata in mDNS TXT records (farp.enabled=true, farp.manifest=http://.../_farp/manifest, farp.openapi=http://.../openapi.json).
  3. Exposes GET /_farp/manifest returning the service's API schema manifest.
  4. The gateway extension on another Forge app discovers this service, reads the manifest, and auto-generates proxy routes.

FARP with Consul (Production)

For production deployments, use Consul as the backend. FARP metadata is stored in Consul service metadata:

discovery.NewExtension(
    discovery.WithEnabled(true),
    discovery.WithConsul("consul.internal:8500", ""),
    discovery.WithServiceName("payment-service"),
    discovery.WithServiceTags("api", "v2"),
    discovery.WithFARPEnabled(true),
    discovery.WithFARPAutoRegister(true),
    discovery.WithFARPSchemas(
        discovery.FARPSchemaConfig{
            Type:        "openapi",
            SpecVersion: "3.1",
            Location: discovery.FARPLocationConfig{
                Type: "url",
                URL:  "/openapi.json",
            },
            ContentType: "application/json",
        },
    ),
    discovery.WithFARPEndpoints(discovery.FARPEndpointsConfig{
        Health:  "/health",
        Metrics: "/metrics",
        OpenAPI: "/openapi.json",
    }),
    discovery.WithFARPCapabilities("http", "rest", "webhooks"),
)

Key Concepts

  • Backends -- choose between memory (default), consul, etcd, mdns, kubernetes, or eureka.
  • Service registration -- register the current service with the backend on startup.
  • Service discovery -- discover healthy instances by name or tags with load balancing.
  • Health checks -- periodic checks with configurable interval and timeout.
  • Watching -- watch for service changes in real-time.
  • FARP -- expose /_farp/manifest and /_farp/discovery for API schema-based discovery.
  • mDNS + FARP -- zero-config local development where FARP metadata is carried in mDNS TXT records.

Important Runtime Notes

  • The memory backend is local-only and does not share state across nodes.
  • Consul and etcd backends require running infrastructure.
  • FARP endpoints enable the gateway extension to auto-discover and proxy routes.
  • When using mDNS, the service name determines the mDNS service type (_<name>._tcp). Use WithServiceName or WithMDNSServiceType to control it.

Detailed Pages

How is this guide?

On this page