Skip to content

Schema Annotations

This page documents all PKL annotations used in formae resource schemas.

Overview

Annotations provide metadata about resources and their fields. The SDK reads these annotations to:

  • Generate resource descriptors
  • Validate user input
  • Control CRUD behavior
  • Enable discovery and extraction

ResourceHint

Applied to resource classes to define type metadata.

@formae.ResourceHint {
    type = "MYCLOUD::Compute::Instance"
    identifier = "$.InstanceId"
}
class Instance extends formae.Resource {
    // ...
}

Fields

Field Type Required Default Description
type String Yes Full resource type name
identifier String Yes JSONPath to native ID in API response
parent String No null Parent resource type for nested resources
listParam ListProperty No null Parent property required for List operations
discoverable Boolean No true If false, excluded from discovery

type

Full resource type name following the pattern NAMESPACE::SERVICE::RESOURCE:

type = "AWS::EC2::Instance"
type = "AZURE::Compute::VirtualMachine"
type = "MYCLOUD::Storage::Bucket"

Naming conventions:

  • NAMESPACE - Your plugin's namespace (uppercase)
  • SERVICE - Logical service grouping (e.g., EC2, S3, IAM)
  • RESOURCE - Resource name (PascalCase)

identifier

JSONPath expression to extract the native ID from your cloud API's response:

identifier = "$.InstanceId"         // Top-level field
identifier = "$.Resource.Id"        // Nested field
identifier = "$.Arn"                // ARN as identifier
identifier = "$.Metadata.Guid"      // Deep nested

The native ID must:

  • Be unique within the resource type
  • Be stable (not change over the resource's lifetime)
  • Be returned by your Create operation

parent and listParam

For child resources, use parent to declare the parent resource type and listParam to specify which parent properties are needed for the List operation:

@formae.ResourceHint {
    type = "MYCLOUD::Storage::Object"
    identifier = "$.ObjectKey"
    parent = "MYCLOUD::Storage::Bucket"
    listParam = new formae.ListProperty {
        parentProperty = "BucketName"  // Property on the parent resource
        listParameter = "Bucket"        // Parameter name the List API expects
    }
}
class Object extends formae.Resource {
    // ...
}

This enables:

  • Scoped discovery (list objects within a specific bucket)
  • Correct ordering (discover parents before children)
  • Automatic resolvable injection from child to parent

For resources requiring multiple parent properties:

listParam = List(
    new formae.ListProperty {
        parentProperty = "TableName"
        listParameter = "TableName"
    },
    new formae.ListProperty {
        parentProperty = "IndexName"
        listParameter = "IndexName"
    }
)

During discovery, formae reads the parent's properties and passes them to your List operation via ListRequest.ListParameters.

discoverable

Set to false to exclude from automatic discovery:

@formae.ResourceHint {
    type = "MYCLOUD::Internal::TempResource"
    identifier = "$.Id"
    discoverable = false  // Don't show in discovery
}

Use for:

  • Internal/system resources users shouldn't manage
  • Resources that would flood discovery results
  • Temporary or ephemeral resources

FieldHint

Applied to resource properties to define field metadata.

@formae.FieldHint {
    createOnly = true
    description = "The image ID to launch from"
}
imageId: String

Fields

Field Type Default Description
createOnly Boolean false Cannot be changed after creation
writeOnly Boolean false Can be written but never returned by Read
readOnly Boolean false Computed by the provider
description String null Human-readable description
sensitive Boolean false Contains sensitive data
immutable Boolean false Synonym for createOnly

createOnly

Fields that cannot be modified after the resource is created:

@formae.FieldHint { createOnly = true }
region: String

@formae.FieldHint { createOnly = true }
encryptionKeyId: String

Attempting to change a createOnly field triggers a replace operation (delete + create).

writeOnly

Fields that can be written but are never returned by the cloud provider's Read operation:

@formae.FieldHint { writeOnly = true }
password: String

@formae.FieldHint { writeOnly = true }
secretKey: String

WriteOnly fields:

  • Are stored by formae but never returned by the provider
  • Are always included in update patches (even if unchanged)
  • Common for passwords, secrets, and sensitive configuration

This is essential for cloud APIs (like AWS CloudControl) that require certain fields in every update but never return them on read.

readOnly

Fields computed by the provider that users cannot set:

@formae.FieldHint { readOnly = true }
createdAt: String

@formae.FieldHint { readOnly = true }
arn: String

@formae.FieldHint { readOnly = true }
status: String

Read-only fields:

  • Are ignored in Create/Update requests
  • Are populated from Read responses
  • Are included in inventory output

description

Human-readable documentation for the field:

@formae.FieldHint {
    description = "The display name of the instance. Must be unique within the region."
}
name: String

Descriptions appear in:

  • Generated documentation
  • IDE hover tooltips
  • Error messages

sensitive

Fields containing secrets or sensitive data:

@formae.FieldHint { sensitive = true }
password: String

@formae.FieldHint { sensitive = true }
apiKey: String

Sensitive fields:

  • Are masked in logs and output
  • Are excluded from diffs by default
  • Trigger warnings if committed to version control

Type Examples

Required vs Optional

// Required - must be provided
@formae.FieldHint {}
name: String

// Optional - can be omitted
@formae.FieldHint {}
description: String?

With Defaults

@formae.FieldHint {}
instanceType: String = "small"

@formae.FieldHint {}
enabled: Boolean = true

@formae.FieldHint {}
maxRetries: Int = 3

Collections

// Map of strings
@formae.FieldHint {}
tags: Mapping<String, String>

// List of strings
@formae.FieldHint {}
securityGroups: Listing<String>

// Optional list
@formae.FieldHint {}
additionalIps: Listing<String>?

Nested Types

@formae.FieldHint {}
networkConfig: NetworkConfig?

class NetworkConfig extends formae.SubResource {
    @formae.FieldHint {}
    vpcId: String

    @formae.FieldHint {}
    subnetIds: Listing<String>

    @formae.FieldHint {}
    assignPublicIp: Boolean = false
}

Complete Example

module mycloud

import "@formae/formae.pkl"

/// A compute instance in MyCloud.
@formae.ResourceHint {
    type = "MYCLOUD::Compute::Instance"
    identifier = "$.InstanceId"
}
class Instance extends formae.Resource {
    fixed hidden type: String = "MYCLOUD::Compute::Instance"

    /// Display name for the instance.
    @formae.FieldHint {
        description = "Human-readable name for the instance"
    }
    name: String

    /// Machine type determining CPU and memory.
    @formae.FieldHint {
        description = "Instance size: small, medium, or large"
    }
    instanceType: String = "small"

    /// Image to launch from. Cannot be changed after creation.
    @formae.FieldHint {
        createOnly = true
        description = "AMI or image ID"
    }
    imageId: String

    /// Region where the instance runs.
    @formae.FieldHint {
        createOnly = true
        description = "Cloud region (e.g., us-east-1)"
    }
    region: String

    /// Instance ARN. Computed by the provider.
    @formae.FieldHint { readOnly = true }
    arn: String?

    /// Current instance state.
    @formae.FieldHint { readOnly = true }
    status: String?

    /// Tags for organization.
    @formae.FieldHint {}
    tags: Mapping<String, String>?

    /// SSH key for access.
    @formae.FieldHint { sensitive = true }
    sshPrivateKey: String?
}

See Also