> ## Documentation Index
> Fetch the complete documentation index at: https://docs.formae.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 04 - Plugin configuration

> Implement the RateLimit and LabelConfig methods every plugin needs, and see where DiscoveryFilters fits.

This section covers the configuration methods every plugin must implement: `RateLimit()` and `LabelConfig()`.

## Configuration methods

The `ResourcePlugin` interface requires three configuration methods:

| Method               | Required | Purpose                                                 |
| -------------------- | -------- | ------------------------------------------------------- |
| `RateLimit()`        | Yes      | Limits requests to protect the target system            |
| `LabelConfig()`      | Yes      | Extracts human-readable labels for discovered resources |
| `DiscoveryFilters()` | Optional | Excludes certain resources from discovery               |

We'll implement the first two now. `DiscoveryFilters()` is covered in [Discovery filters](/plugin-development/concepts/discovery-filters).

## RateLimit

SFTP servers typically limit concurrent connections. The `RateLimit()` method tells formae how fast it can send requests to your plugin.

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
func (p *Plugin) RateLimit() plugin.RateLimitConfig {
    return plugin.RateLimitConfig{
        Scope:                            plugin.RateLimitScopeNamespace,
        MaxRequestsPerSecondForNamespace: 5,
    }
}
```

### Configuration fields

| Field                              | Description                                           |
| ---------------------------------- | ----------------------------------------------------- |
| `Scope`                            | Currently only `RateLimitScopeNamespace` is supported |
| `MaxRequestsPerSecondForNamespace` | Maximum requests per second across all resource types |

### Choosing a rate limit

Consider your target system's limits:

* **SFTP servers**: Often allow 5-10 concurrent connections
* **Cloud APIs**: Check the provider's documentation (AWS: varies by service, typically 10-100 RPS)
* **SaaS APIs**: Usually documented in API docs (e.g., "100 requests per minute")

For our SFTP plugin, we'll use **5 requests per second** as a conservative default.

### Why rate limiting matters

formae runs multiple operations concurrently:

* User-triggered applies and destroys
* Background synchronization (periodic reads)
* Discovery scans

Without rate limiting, these could overwhelm your target system. The rate limiter ensures all operations share a fair quota.

## LabelConfig

When formae discovers resources, it needs a human-readable label to display. The `LabelConfig()` method tells formae how to extract labels from your resources.

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
func (p *Plugin) LabelConfig() plugin.LabelConfig {
    return plugin.LabelConfig{
        DefaultQuery: "$.path",
    }
}
```

### Configuration fields

| Field               | Description                                                                                 |
| ------------------- | ------------------------------------------------------------------------------------------- |
| `DefaultQuery`      | [JSONPath](https://www.rfc-editor.org/rfc/rfc9535.html) expression applied to all resources |
| `ResourceOverrides` | Per-resource-type overrides (optional)                                                      |

### JSONPath for labels

The `DefaultQuery` is a [JSONPath](https://www.rfc-editor.org/rfc/rfc9535.html) expression that extracts a label from the resource's JSON representation. For our File resource:

```json theme={"languages":{"custom":["/languages/pkl.json"]}}
{
  "path": "/data/config.txt",
  "content": "...",
  "permissions": "0644"
}
```

Using `$.path` extracts `/data/config.txt` as the label.

### Resource overrides

If your plugin has multiple resource types with different naming conventions, use `ResourceOverrides`:

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
func (p *Plugin) LabelConfig() plugin.LabelConfig {
    return plugin.LabelConfig{
        DefaultQuery: "$.path",
        ResourceOverrides: map[string]string{
            "SFTP::Config::Setting": "$.name",
        },
    }
}
```

For our simple SFTP plugin with only one resource type, we don't need overrides.

## DiscoveryFilters (optional)

The `DiscoveryFilters()` method excludes certain resources from discovery. For now, we return `nil` to discover all files:

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
func (p *Plugin) DiscoveryFilters() []plugin.MatchFilter {
    return nil
}
```

This is covered in detail in [Discovery filters](/plugin-development/concepts/discovery-filters).

## Update sftp.go

The template already has these methods. Update `RateLimit()` and `LabelConfig()` with our values:

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
// RateLimit returns the rate limiting configuration for this plugin.
// SFTP servers typically limit concurrent connections, so we use a
// conservative limit of 5 requests per second.
func (p *Plugin) RateLimit() plugin.RateLimitConfig {
    return plugin.RateLimitConfig{
        Scope:                            plugin.RateLimitScopeNamespace,
        MaxRequestsPerSecondForNamespace: 5,
    }
}

// DiscoveryFilters returns filters to exclude resources from discovery.
// We discover all files, so return nil.
func (p *Plugin) DiscoveryFilters() []plugin.MatchFilter {
    return nil
}

// LabelConfig returns the configuration for extracting labels from resources.
// For files, the path is the natural label.
func (p *Plugin) LabelConfig() plugin.LabelConfig {
    return plugin.LabelConfig{
        DefaultQuery: "$.path",
    }
}
```

## Verify

To verify that the configuration methods compile correctly:

```bash theme={"languages":{"custom":["/languages/pkl.json"]}}
cd formae-plugin-sftp
make build
```

## Summary

With configuration methods complete, your plugin now tells formae:

1. **Rate limit**: 5 requests per second to protect the SFTP server
2. **Labels**: Use the file path as the display name for discovered files
3. **Discovery filters**: Discover all files (no exclusions)

***

Next: [05 - Create](/plugin-development/tutorial/05-create)
