# How to Use IP Utilities for Network Address Processing in go-pantheon/fabrica-kit

> Learn to process network addresses using IP utilities in go-pantheon/fabrica-kit. Detect internal IPs, resolve endpoints, extract client IPs, and validate ranges with pure Go functions.

- Repository: [Pantheon/fabrica-kit](https://github.com/go-pantheon/fabrica-kit)
- Tags: how-to-guide
- Published: 2026-03-02

---

**The `ip` package in go-pantheon/fabrica-kit provides pure Go functions to detect internal IPv4 addresses, resolve service advertisement endpoints, extract client IPs from Kratos contexts, and validate private network ranges without external dependencies.**

The **go-pantheon/fabrica-kit** repository offers a lightweight networking toolkit designed for microservices that need reliable address detection across containerized and bare-metal environments. This guide examines the IP utilities for network address processing implemented in [`ip/ip.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/ip/ip.go), demonstrating how to leverage `InternalIP()`, `Extract()`, and `GetClientIP()` for robust service discovery and client identification.

## Core IP Utility Functions in fabrica-kit

The `ip` package exposes five primary functions that handle distinct network address processing scenarios. Each utility operates without network I/O beyond local interface enumeration, making them safe for high-frequency calls during service startup and request handling.

### InternalIP: Detecting Private IPv4 Addresses

The `InternalIP()` function scans the host's network interfaces to return the first available private IPv4 address. It iterates through `net.Interfaces()`, skips downed or loopback devices, and extracts the IPv4 component from each address using `ipNet.IP.To4()`.

This function is essential when services must auto-detect their LAN address for registration with service discovery systems or health check endpoints. If no suitable interface exists, it returns an empty string rather than an error, allowing callers to implement fallback logic.

### Extract: Resolving Advertised Service Addresses

The `Extract(hostPort string, lis net.Listener)` function builds a resolvable `host:port` string suitable for service advertisement. It implements a four-step resolution strategy:

1. **Parse** the input using `net.SplitHostPort`, returning `ErrInvalidHostPort` on malformed input.
2. **Override Port** with the listener's actual port if available via the `Port()` helper.
3. **Explicit Address** preservation when the host is not a wildcard (`0.0.0.0`, `[::]`, `::`).
4. **Private IP Fallback** that enumerates interfaces and returns the first address satisfying `isPrivateIP`, or returns `ErrNoPrivateIPFound` if none exist.

This function solves the common microservice problem of binding to `0.0.0.0:0` (all interfaces, random port) while still advertising a concrete, reachable address to registries.

### Port: Retrieving Dynamic Listener Ports

The `Port(lis net.Listener)` function extracts the concrete TCP port from a listener. It type-asserts `lis.Addr()` to `*net.TCPAddr` and returns the `Port` field.

This utility is critical when starting servers with `net.Listen("tcp", ":0")`, where the operating system assigns an available ephemeral port. The returned port enables dynamic service registration without pre-configuration.

### isPrivateIP: Validating Private Address Ranges

The `isPrivateIP(addr string)` function validates whether an IP address falls within standard private ranges. It implements RFC1918 checks for IPv4:

- `10.0.0.0/8` (10.x.x.x)
- `172.16.0.0/12` (172.16.x.x - 172.31.x.x)
- `192.168.0.0/16` (192.168.x.x)

For IPv6, it checks the unique local address prefix `FC00::/7` by verifying `(ip[0] & 0xfe) == 0xfc`.

The function returns `false` for public IPs, loopback addresses, and malformed strings, making it suitable for security filtering and network segmentation logic.

### GetClientIP: Extracting Client Addresses in Kratos

The `GetClientIP(ctx context.Context)` function extracts the originating client IP from a Kratos server context. It retrieves the transport layer via `transport.FromServerContext(ctx)` and inspects request headers in priority order:

1. **X-Forwarded-For**: Returns the first comma-separated entry if present (handles proxy chains).
2. **X-Real-IP**: Returns the value if `X-Forwarded-For` is absent.

This function is essential for microservices running behind reverse proxies or load balancers, where the transport connection's `RemoteAddr` reflects the proxy rather than the end user. It returns an empty string when the context lacks transport information or headers.

## Working with Network Address Errors

The `ip` package defines exported sentinel errors that enable precise error handling without string parsing. These constants reside in [`ip/ip.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/ip/ip.go) and cover the primary failure modes:

- **`ErrInvalidHostPort`**: Returned when `Extract` receives a malformed `host:port` string that fails `net.SplitHostPort`.
- **`ErrNoPrivateIPFound`**: Returned when `Extract` cannot locate a suitable private IP address on the host after checking all network interfaces.
- **`ErrInterfaceLookup`**: Returned when the underlying call to `net.Interfaces()` fails, preventing address enumeration.
- **`ErrInvalidIPFormat`**: Returned when `isPrivateIP` receives a string that cannot be parsed as an IP address.
- **`ErrIPComponentOutOfRange`**: Returned when parsing IP components reveals values outside valid byte ranges.

Using these errors allows applications to implement differentiated retry logic, fallback to configuration values, or fail fast with clear telemetry.

## Practical Implementation Examples

The following examples demonstrate common patterns for using fabrica-kit's IP utilities in production Go applications.

### Example 1: Obtaining the Host's Private IP

Use `InternalIP()` when your service needs to register its LAN address with a service discovery registry.

```go
package main

import (
	"fmt"
	"github.com/go-pantheon/fabrica-kit/ip"
)

func main() {
	ifaceIP := ip.InternalIP()
	if ifaceIP == "" {
		fmt.Println("No internal IP found")
		return
	}
	fmt.Println("Detected internal IP:", ifaceIP)
}

```

### Example 2: Resolving Service Advertisement Addresses

Use `Extract()` when binding to wildcard addresses but advertising a concrete private IP to clients.

```go
package main

import (
	"fmt"
	"net"
	"github.com/go-pantheon/fabrica-kit/ip"
)

func main() {
	// Bind to all interfaces with OS-assigned port
	ln, err := net.Listen("tcp", "0.0.0.0:0")
	if err != nil {
		panic(err)
	}
	defer ln.Close()

	// Resolve to private IP for service registration
	addr, err := ip.Extract("0.0.0.0:0", ln)
	if err != nil {
		// Handle ErrNoPrivateIPFound or ErrInvalidHostPort
		panic(err)
	}
	fmt.Println("Service should be advertised as:", addr)
}

```

### Example 3: Handling Dynamic Ports

Use `Port()` to retrieve the actual port when using `:0` for dynamic allocation.

```go
ln, _ := net.Listen("tcp", ":0")
if port, ok := ip.Port(ln); ok {
    fmt.Printf("Listener is on port %d\n", port)
}

```

### Example 4: Validating Client IPs in Kratos Handlers

Use `GetClientIP()` inside Kratos handlers to identify real client addresses behind proxies.

```go
import (
	"context"
	"github.com/go-kratos/kratos/v2/transport"
	"github.com/go-pantheon/fabrica-kit/ip"
	pb "myapp/api"
)

func MyHandler(ctx context.Context, req *pb.MyRequest) (*pb.MyResponse, error) {
	clientIP := ip.GetClientIP(ctx)
	if clientIP != "" {
		// Log or validate the originating IP
		fmt.Printf("Request from %s\n", clientIP)
	}
	
	// ... business logic ...
	return &pb.MyResponse{}, nil
}

```

## Summary

- The **go-pantheon/fabrica-kit** `ip` package provides pure Go utilities for network address processing without external dependencies.
- **`InternalIP()`** auto-detects the host's first private IPv4 address for service registration.
- **`Extract()`** resolves wildcard bind addresses (`0.0.0.0:0`) into concrete private IPs suitable for service discovery.
- **`Port()`** retrieves runtime-assigned ports from listeners created with `:0`.
- **`isPrivateIP()`** validates RFC1918 IPv4 and unique local IPv6 addresses for security filtering.
- **`GetClientIP()`** extracts real client IPs from Kratos contexts, respecting `X-Forwarded-For` and `X-Real-IP` headers.
- All functions return exported sentinel errors (`ErrNoPrivateIPFound`, `ErrInvalidHostPort`, etc.) for robust error handling.

## Frequently Asked Questions

### How does fabrica-kit determine if an IP is private?

The `isPrivateIP` function in [`ip/ip.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/ip/ip.go) implements RFC1918 checks for IPv4 addresses by verifying if the IP falls within the `10.0.0.0/8`, `172.16.0.0/12`, or `192.168.0.0/16` ranges using byte-wise comparison. For IPv6, it checks the unique local address prefix `FC00::/7` by validating that the first byte matches `(ip[0] & 0xfe) == 0xfc`.

### What error should I handle when no private IP is available?

When calling `Extract()` or `InternalIP()` on hosts without private network interfaces (such as public cloud instances with only public IPs), handle `ErrNoPrivateIPFound`. This sentinel error defined in [`ip/ip.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/ip/ip.go) indicates that the function successfully scanned all network interfaces but failed to locate any address satisfying the private IP criteria, allowing your application to fall back to configuration values or public IP detection.

### Can I use these utilities without the Kratos framework?

Yes, all IP utilities except `GetClientIP` function independently of the Kratos framework. `InternalIP()`, `Extract()`, `Port()`, and `isPrivateIP()` rely solely on Go's standard library (`net` package) and require no external dependencies. Only `GetClientIP` depends on Kratos's transport package to extract headers from the context, but the core network address processing capabilities remain framework-agnostic.

### How do I handle dynamic port allocation with fabrica-kit?

When binding to port `0` to allow the OS to assign an available ephemeral port, use the `Port()` function to retrieve the actual assigned port number. First create the listener with `net.Listen("tcp", ":0")`, then pass that listener to `ip.Port(lis)` which type-asserts the address to `*net.TCPAddr` and returns the concrete port integer. Combine this with `Extract()` to build the complete `host:port` string for service registration.