> ## 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.

# ResourcePlugin interface

> Every method of the ResourcePlugin interface: parameters, return types, and behavioral contracts.

The `ResourcePlugin` interface is the core contract between your plugin and the formae agent. This page documents all methods, their parameters, return types, and behavioral contracts.

## Interface definition

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type ResourcePlugin interface {
    // Configuration
    RateLimit() RateLimitConfig
    DiscoveryFilters() []MatchFilter
    LabelConfig() LabelConfig

    // CRUD Operations
    Create(ctx context.Context, req *resource.CreateRequest) (*resource.CreateResult, error)
    Read(ctx context.Context, req *resource.ReadRequest) (*resource.ReadResult, error)
    Update(ctx context.Context, req *resource.UpdateRequest) (*resource.UpdateResult, error)
    Delete(ctx context.Context, req *resource.DeleteRequest) (*resource.DeleteResult, error)
    Status(ctx context.Context, req *resource.StatusRequest) (*resource.StatusResult, error)
    List(ctx context.Context, req *resource.ListRequest) (*resource.ListResult, error)
}
```

***

## Configuration methods

### RateLimit

Returns rate limiting configuration for your plugin.

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

**Returns:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type RateLimitConfig struct {
    Scope                            RateLimitScope
    MaxRequestsPerSecondForNamespace int
}

type RateLimitScope string

const (
    RateLimitScopeNamespace RateLimitScope = "Namespace"
)
```

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

**Example:**

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

***

### DiscoveryFilters

Returns filters to exclude resources from discovery.

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

**Returns:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type MatchFilter struct {
    ResourceTypes []string
    Conditions    []FilterCondition
}

type FilterCondition struct {
    PropertyPath  string  // JSONPath expression
    PropertyValue string  // Expected value (empty = existence check)
}
```

Resources matching **all conditions** in a filter are excluded from discovery.

**Example:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
func (p *Plugin) DiscoveryFilters() []plugin.MatchFilter {
    return []plugin.MatchFilter{
        {
            ResourceTypes: []string{"MYCLOUD::Compute::Instance"},
            Conditions: []plugin.FilterCondition{
                {
                    PropertyPath:  "$.Tags[?(@.Key=='formae:skip')].Value",
                    PropertyValue: "true",
                },
            },
        },
    }
}
```

Return `nil` to discover all resources.

***

### LabelConfig

Returns configuration for extracting human-readable labels from discovered resources.

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

**Returns:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type LabelConfig struct {
    DefaultQuery      string
    ResourceOverrides map[string]string
}
```

| Field               | Description                                  |
| ------------------- | -------------------------------------------- |
| `DefaultQuery`      | JSONPath expression applied to all resources |
| `ResourceOverrides` | Per-resource-type JSONPath overrides         |

**Example:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
func (p *Plugin) LabelConfig() plugin.LabelConfig {
    return plugin.LabelConfig{
        DefaultQuery: "$.Tags[?(@.Key=='Name')].Value",
        ResourceOverrides: map[string]string{
            "MYCLOUD::IAM::Policy": "$.PolicyName",
            "MYCLOUD::IAM::Role":   "$.RoleName",
        },
    }
}
```

***

## CRUD methods

### Create

Provisions a new resource.

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
func (p *Plugin) Create(ctx context.Context, req *resource.CreateRequest) (*resource.CreateResult, error)
```

**Request:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type CreateRequest struct {
    ResourceType string          // e.g., "MYCLOUD::Compute::Instance"
    Properties   json.RawMessage // Desired resource configuration
    TargetConfig json.RawMessage // Credentials and endpoint config
}
```

**Result:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type CreateResult struct {
    ProgressResult *ProgressResult
}
```

**Contract:**

* On success: Set `OperationStatus` to `Success`, provide `NativeID` and `ResourceProperties`
* On async: Set `OperationStatus` to `InProgress`, provide `RequestID` for polling
* On failure: Set `OperationStatus` to `Failure`, provide `ErrorCode` and `StatusMessage`
* Return Go error only for unexpected or unrecoverable errors

***

### Read

Fetches current state of an existing resource.

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
func (p *Plugin) Read(ctx context.Context, req *resource.ReadRequest) (*resource.ReadResult, error)
```

**Request:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type ReadRequest struct {
    ResourceType    string
    NativeID        string
    TargetConfig    json.RawMessage
    RedactSensitive bool            // Strip sensitive properties from the result
    PriorProperties json.RawMessage // Caller's last-known model; empty if unknown
}
```

**Result:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type ReadResult struct {
    ResourceType       string
    NativeID           string
    ResourceProperties json.RawMessage
    ErrorCode          OperationErrorCode
}
```

**Contract:**

* On success: Provide `ResourceProperties` with current state
* If not found: Set `ErrorCode` to `OperationErrorCodeNotFound`
* Never return Go error for "not found"
* `PriorProperties` (optional) carries the caller's last-known model. When set, a `Read` may tailor which fields it reports to match what the caller manages (for example, embedding a child collection only when the prior model declares it inline). It is empty when the caller has no prior state, such as a create/status read-back or discovery; treat empty as unknown and fall back to default behaviour. Populated by the agent from formae 0.87.1.

***

### Update

Modifies an existing resource.

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
func (p *Plugin) Update(ctx context.Context, req *resource.UpdateRequest) (*resource.UpdateResult, error)
```

**Request:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type UpdateRequest struct {
    ResourceType      string
    NativeID          string
    PriorProperties   json.RawMessage // State before update
    DesiredProperties json.RawMessage // Requested new state
    PatchDocument     json.RawMessage // JSON Patch of changes
    TargetConfig      json.RawMessage
}
```

**Result:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type UpdateResult struct {
    ProgressResult *ProgressResult
}
```

**Contract:**

* Use `DesiredProperties` for full replacement or `PatchDocument` for partial update
* On success: Provide updated `ResourceProperties`
* On async: Return `InProgress` with `RequestID`

***

### Delete

Removes a resource.

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
func (p *Plugin) Delete(ctx context.Context, req *resource.DeleteRequest) (*resource.DeleteResult, error)
```

**Request:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type DeleteRequest struct {
    ResourceType string
    NativeID     string
    TargetConfig json.RawMessage
}
```

**Result:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type DeleteResult struct {
    ProgressResult *ProgressResult
}
```

**Contract:**

* Must be idempotent: deleting a non-existent resource should succeed
* On success: Set `OperationStatus` to `Success`
* On async: Return `InProgress` with `RequestID`

***

### Status

Polls for completion of an async operation.

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
func (p *Plugin) Status(ctx context.Context, req *resource.StatusRequest) (*resource.StatusResult, error)
```

**Request:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type StatusRequest struct {
    ResourceType string
    RequestID    string // From the original InProgress response
    TargetConfig json.RawMessage
}
```

**Result:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type StatusResult struct {
    ProgressResult *ProgressResult
}
```

**Contract:**

* Called when Create/Update/Delete returns `InProgress`
* Return `InProgress` to continue polling
* Return `Success` or `Failure` when complete
* Provide `NativeID` and `ResourceProperties` on success

***

### List

Returns all resource identifiers of a given type for discovery.

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
func (p *Plugin) List(ctx context.Context, req *resource.ListRequest) (*resource.ListResult, error)
```

**Request:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type ListRequest struct {
    ResourceType string
    TargetConfig json.RawMessage
    PageToken    *string // For pagination
    PageSize     int     // Suggested page size
}
```

**Result:**

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type ListResult struct {
    NativeIDs     []string
    NextPageToken *string
}
```

**Contract:**

* Return all native IDs for resources of the given type
* Use pagination for large result sets
* Set `NextPageToken` if more pages available

***

## ProgressResult

Common result structure for operations:

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type ProgressResult struct {
    Operation          Operation
    OperationStatus    OperationStatus
    RequestID          string
    NativeID           string
    ResourceProperties json.RawMessage
    ErrorCode          OperationErrorCode
    StatusMessage      string
}
```

### Operation

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type Operation string

const (
    OperationCreate      Operation = "CREATE"
    OperationRead        Operation = "READ"
    OperationUpdate      Operation = "UPDATE"
    OperationDelete      Operation = "DELETE"
    OperationCheckStatus Operation = "CHECK_STATUS"
)
```

### OperationStatus

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type OperationStatus string

const (
    OperationStatusSuccess    OperationStatus = "SUCCESS"
    OperationStatusFailure    OperationStatus = "FAILURE"
    OperationStatusInProgress OperationStatus = "IN_PROGRESS"
    OperationStatusPending    OperationStatus = "PENDING"
)
```

### OperationErrorCode

```go theme={"languages":{"custom":["/languages/pkl.json"]}}
type OperationErrorCode string

const (
    OperationErrorCodeNotFound           OperationErrorCode = "NOT_FOUND"
    OperationErrorCodeAlreadyExists      OperationErrorCode = "ALREADY_EXISTS"
    OperationErrorCodeInvalidRequest     OperationErrorCode = "INVALID_REQUEST"
    OperationErrorCodeAccessDenied       OperationErrorCode = "ACCESS_DENIED"
    OperationErrorCodeThrottling         OperationErrorCode = "THROTTLING"
    OperationErrorCodeServiceUnavailable OperationErrorCode = "SERVICE_UNAVAILABLE"
    OperationErrorCodeInternalFailure    OperationErrorCode = "INTERNAL_FAILURE"
    OperationErrorCodeNotStabilized      OperationErrorCode = "NOT_STABILIZED"
)
```

| Error Code            | Retriable | Description                      |
| --------------------- | --------- | -------------------------------- |
| `NOT_FOUND`           | No        | Resource does not exist          |
| `ALREADY_EXISTS`      | No        | Resource already exists (Create) |
| `INVALID_REQUEST`     | No        | Invalid parameters               |
| `ACCESS_DENIED`       | No        | Permission denied                |
| `THROTTLING`          | Yes       | Rate limited by provider         |
| `SERVICE_UNAVAILABLE` | Yes       | Temporary provider outage        |
| `INTERNAL_FAILURE`    | Maybe     | Unexpected error                 |
| `NOT_STABILIZED`      | Yes       | Resource not yet ready           |

***

## See also

* [Tutorial: Create](/plugin-development/tutorial/05-create) - Implementing Create, Read, Update, Delete
* [Tutorial: List](/plugin-development/tutorial/09-list) - Implementing List for discovery
