# How frp's Feature Gate System Enables Experimental Features: A Deep Dive

> Learn how frp's thread-safe feature gate system enables runtime experimental features via configuration maps. Keep alpha code inaccessible until explicitly activated.

- Repository: [fatedier/frp](https://github.com/fatedier/frp)
- Tags: deep-dive
- Published: 2026-02-26

---

**frp implements a thread-safe feature gate mechanism that lets operators toggle experimental functionality at runtime through configuration maps, ensuring alpha-stage code remains inaccessible unless explicitly enabled.**

The fatedier/frp repository uses a centralized policy layer to safely ship unstable capabilities without risking production stability. The entire system resides in [`pkg/policy/featuregate/feature_gate.go`](https://github.com/fatedier/frp/blob/main/pkg/policy/featuregate/feature_gate.go) and provides a declarative API for registering, configuring, and querying feature states across the client and server.

## Core Architecture of the Feature Gate System

At the heart of frp's implementation are four core abstractions that define how experimental capabilities are staged and controlled.

### Feature Identifiers and Maturity Stages

Each capability is identified by a **Feature** string constant and assigned a **FeatureStage** representing its maturity level:

- **ALPHA**: Disabled by default, requires explicit opt-in
- **BETA**: May be enabled by default depending on the specification  
- **GA** (General Availability): Always enabled and hidden from the gate list

The **FeatureSpec** struct captures this metadata along with the default boolean state and whether the gate is locked to that default. According to the source code, the global singleton `DefaultFeatureGates` manages these definitions through a map called `defaultFeatures` located at lines 56-59 of [`feature_gate.go`](https://github.com/fatedier/frp/blob/main/feature_gate.go).

For example, the experimental *VirtualNet* capability is declared as:

```go
VirtualNet = Feature("VirtualNet")

defaultFeatures = map[Feature]FeatureSpec{
    VirtualNet: {Default: false, Stage: Alpha},
}

```

Because VirtualNet is marked as **Alpha** with a default of `false`, the system blocks its usage until an administrator explicitly overrides the setting.

### The MutableFeatureGate Interface

The system exposes **MutableFeatureGate**, which extends the base `FeatureGate` interface with two critical methods:

- **`SetFromMap(map[string]bool)`**: Applies user-provided configuration to override default states (implemented at lines 106-138)
- **`Add(Feature, FeatureSpec)`**: Registers new gates during initialization

The `SetFromMap` implementation performs strict validation: it rejects unrecognized feature names with an "unrecognized feature gate" error and prevents modification of locked gates. Once validated, values are stored in an atomic map (`f.enabled`) to ensure thread-safe access during concurrent operations.

## Enabling Experimental Features at Runtime

frp activates feature gates during the client initialization sequence, translating static configuration into runtime policy.

### Configuration-Driven Activation

When **frpc** (the client) starts, the entry point in [`cmd/frpc/sub/root.go`](https://github.com/fatedier/frp/blob/main/cmd/frpc/sub/root.go) (lines 32-35) checks the configuration structure for a `FeatureGates` map:

```go
if len(cfg.FeatureGates) > 0 {
    if err := featuregate.SetFromMap(cfg.FeatureGates); err != nil {
        return err
    }
}

```

This code forwards the user-defined map to the global `DefaultFeatureGates` singleton. The `SetFromMap` method iterates through each entry, validates the gate exists, and updates the internal atomic state. Because this happens during startup, the system prevents late modifications that could destabilize running components.

### Validation and Safety Checks

The gate system enforces immutability constraints after initialization. Once `Close()` is called on the feature gate, subsequent calls to `Add` fail, preventing plugins or dynamic extensions from registering new gates after the system has stabilized. This design ensures that the set of experimental features remains fixed throughout the process lifetime.

## Guarding Code with Runtime Checks

Experimental code paths remain dormant unless explicitly enabled through the feature gate policy layer.

### Validation Layer Integration

Configuration validators query the gate state before accepting experimental fields. In [`pkg/config/v1/validation/client.go`](https://github.com/fatedier/frp/blob/main/pkg/config/v1/validation/client.go) (lines 54-56), the VirtualNet address validator demonstrates this pattern:

```go
if c.VirtualNet.Address != "" {
    if !featuregate.Enabled(featuregate.VirtualNet) {
        return nil, fmt.Errorf("VirtualNet feature is not enabled; enable it by setting the appropriate feature gate flag")
    }
}

```

This check prevents users from accidentally deploying configurations that depend on unstable networking features.

### Conditional Execution Logic

Any component can query the gate state through the `Enabled(Feature)` method. This method first checks the override map populated by `SetFromMap`; if no override exists, it falls back to the `FeatureSpec.Default` value. For GA features, this always returns `true`, while Alpha features return `false` unless explicitly toggled.

## Practical Configuration Examples

### Enable an Experimental Gate in Client Configuration

Define the feature gate map in your TOML, YAML, or JSON configuration file:

```toml

# client.toml

[common]
  featureGates = { VirtualNet = true }

```

When the client parses this configuration, the map `{"VirtualNet": true}` is passed to `featuregate.SetFromMap`, enabling the VirtualNet capability for that session.

### Programmatic Gate Activation

For custom builds or testing scenarios, enable gates programmatically before starting the client:

```go
import "github.com/fatedier/frp/pkg/policy/featuregate"

func enableExperimental() error {
    return featuregate.SetFromMap(map[string]bool{
        string(featuregate.VirtualNet): true,
    })
}

```

This approach overrides only the specified gate while preserving defaults for all other experimental features.

### Guarding Experimental Code Paths

Implement conditional logic to execute code only when the corresponding gate is active:

```go
if featuregate.Enabled(featuregate.VirtualNet) {
    // Initialize experimental virtual networking
    startVirtualNetwork()
}

```

If the gate is disabled, the system skips the experimental block and continues along the stable code path.

## Summary

- frp's feature gate system lives in [`pkg/policy/featuregate/feature_gate.go`](https://github.com/fatedier/frp/blob/main/pkg/policy/featuregate/feature_gate.go) and provides a thread-safe API for managing experimental capabilities.
- Features progress through **Alpha**, **Beta**, and **GA** stages, with Alpha gates disabled by default and requiring explicit configuration.
- The `SetFromMap` method in [`cmd/frpc/sub/root.go`](https://github.com/fatedier/frp/blob/main/cmd/frpc/sub/root.go) applies user configuration during client startup, validating each gate against the `defaultFeatures` registry.
- Runtime guards using `featuregate.Enabled()` prevent unstable code execution unless the corresponding gate is toggled, as demonstrated in the validation logic of [`pkg/config/v1/validation/client.go`](https://github.com/fatedier/frp/blob/main/pkg/config/v1/validation/client.go).
- The system locks the gate set after initialization to prevent runtime modifications that could compromise stability.

## Frequently Asked Questions

### How do I enable an experimental feature in frp?

Add a `featureGates` map to your client configuration file (TOML, YAML, or JSON) with the feature name set to `true`. For example, set `featureGates = { VirtualNet = true }` in your client.toml. When frpc starts, it passes this map to `featuregate.SetFromMap`, which activates the gate for that process.

### What happens if I try to enable a feature gate that doesn't exist?

The `SetFromMap` method returns an error with the message "unrecognized feature gate" and prevents the client from starting. This validation ensures typos or deprecated feature names don't cause undefined behavior.

### What's the difference between Alpha, Beta, and GA stages in frp feature gates?

**Alpha** features default to disabled and must be explicitly enabled; **Beta** features may default to enabled depending on the specification; **GA** (General Availability) features are always enabled and hidden from the gate list. Only Alpha and Beta gates appear in configuration documentation.

### Can I enable feature gates programmatically in custom frp builds?

Yes. Import `github.com/fatedier/frp/pkg/policy/featuregate` and call `featuregate.SetFromMap()` with a map containing the feature names and boolean values. This must be done before the configuration validation phase to ensure the gates are active when the system checks `featuregate.Enabled()`.