Release notes

0.86.0

New features and improvements

Relabeling resources

Extract

  • formae extract handles per-version schema layouts. Plugins that ship schemas split by API version (for example the Kubernetes plugin's @k8s/v<X.Y>/ subtrees) can now be extracted and re-evaluated without the alias collisions and identifier errors that earlier 0.85.x releases produced when a forma referenced multiple per-version files. This unblocks the Kubernetes plugin 0.1.3 (K8s 1.35 and 1.36 support); see Kubernetes plugin release notes.

PklProject auto-resolve

  • formae auto-resolves PklProject dependencies. Previously, running an apply in a directory with an unresolved PklProject failed with NoSuchFileException and forced a manual pkl project resolve. formae now runs the resolve automatically when PklProject.deps.json is missing.

Cascading updates

  • Mutable parent revisions no longer destroy their dependents. Previously, when a parent resource was replaced, every dependent was cascade-replaced (torn down and recreated) regardless of whether the dependent's referring field could actually accept the new parent value via a provider-native update. The canonical case is an AWS::ECS::Service consuming an AWS::ECS::TaskDefinition: every routine TaskDefinition revision (an image deploy, an env-var bump) tore the Service down and stood it back up, typically two to four minutes of outage per deploy, even though the Service can absorb a new TaskDefinition revision via a rolling deploy without ever stopping traffic.

Query syntax

  • Same-field OR matching. Repeating a field in a --query now matches any of the listed values. formae inventory resources --query='stack:web stack:api' returns resources in either stack; the exclusion form -stack:scratch -stack:tmp excludes any of them. Cross-field terms continue to AND together as before, so type:AWS::S3::Bucket stack:prod stack:staging still means "S3 buckets in (prod or staging)".

  • Wildcard matching. Every string-valued field (stack, label, type, target) accepts * as a prefix or suffix wildcard. --query='label:prod-*' matches every resource whose label starts with prod-; label:*-prod matches every label that ends with -prod. Wildcards work across the colons in type names too: --query='type:AWS::S3::*' matches every S3 resource type. Only * is supported (no ?), and a bare * is rejected: wildcards must be anchored to at least one literal character.

  • target: filter on resource queries. Resource queries now accept target:<label> to scope results to a specific cloud target, matching the behaviour that already existed for stack, type, and label. Previously this filter was silently ignored.

Datastore

  • Microsoft SQL Server datastore backend. The agent can now persist its state to Microsoft SQL Server, including Azure SQL Database, alongside the existing SQLite, PostgreSQL, and Aurora Data API options. Set datastoreType = "mssql" in the agent configuration; this is the natural choice for Azure deployments. See Configuration → Datastore for the connection settings.

CLI

  • Replace simulations show the full diff. A formae apply simulation of a replace operation now surfaces any non-immutable changes (mutable property edits, label rename) in a follow-up and by doing the following: block alongside the existing because these immutable properties changed: block. Operators see the full picture of what an apply will do, not just the immutable trigger.

Documentation

  • Published roadmap. The docs now carry a rolling, three-release roadmap so you can see where formae is heading over the next few releases, alongside the release notes that record what has already shipped.

Bug fixes

  • FIXED: Stack apply and destroy could get stuck on certain resource ordering dependencies. Some cloud resources hold their parent alive through a network attachment or rule that the provider requires you to remove first: an EFS file system's mount targets hold the file system open through an ENI; a security group's rules hold live workloads on the wire. formae treated these as ordinary parent-child references and could try to delete the parent in parallel with the dependents, leaving orphaned resources behind and the apply command itself wedged waiting for the provider to confirm a delete it was never going to confirm.

The canonical case is a stack with an AWS::EFS::FileSystem and an AWS::ECS::TaskDefinition that mounts it. Before this release the destroy looked like:

Time ──→
┌─ destroy TaskDefinition
├─ destroy MountTarget
└─ destroy FileSystem ✗   (MountTarget still attached → provider rejects)
   orphaned MountTarget and FileSystem; apply hangs.

The same shape bites on create. The TaskDefinition would start before its MountTargets were operational, the ECS task would fail to mount EFS, and the apply would burn retry cycles until the mount targets caught up.

Plugins can now declare which fields create ordering dependencies, and formae uses that information to order the destroy and create graphs correctly. With the AWS plugin ≥ 0.1.11:

Destroy order:                    Create order:
──────────────                    ─────────────
1. destroy TaskDefinition         1. create FileSystem
2. destroy MountTarget            2. create MountTarget
3. destroy FileSystem ✓           3. create TaskDefinition ✓

This also fixes the symmetric destroy case for SecurityGroupIngress / Egress rules, previously torn down in the first destroy wave, now sequenced after the workloads they protect. See AWS plugin 0.1.11 release notes for the resource-type-specific list.

  • FIXED: A failed Read during a background sync no longer leaves the agent unresponsive to subsequent apply, destroy, or sync commands until restart.

  • FIXED: Optional collection fields (a Tags list, a metadata map) absent in the forma no longer produce spurious "remove" patch ops when the provider returned [] or {}, which previously caused some resource types (e.g. AWS::ECS::TaskDefinition.Tags) to fail apply with "Member must have length greater than or equal to 1" errors.

  • FIXED: Resources deleted and re-created out-of-band with the same provider identifier (common for some Azure resource types) are now re-discovered.

  • FIXED: Plugin error messages now surface on Update, Delete, and resume failures instead of being dropped in favour of a bare UnforeseenError.

  • FIXED: formae update list refreshes package metadata before listing, so the output reflects what's actually available on the Hub instead of stale local cache.

  • FIXED: Intermittent 5-second "timed out" errors at the start of plugin-side resource operations are resolved.

0.85.0

Breaking changes

  • Plugin distribution moves to the public platform.engineering Hub. Plugins are no longer bundled with the formae binary. They are installed on demand from the community orbital repo on hub.platform.engineering via formae plugin install <name>. The container image and setup.sh have been updated to install the curated default set (formae-plugin-standard) automatically, so a fresh install behaves the same as 0.84. Existing 0.84 installs that relied on bundled plugins must install the plugins they were using after upgrading — see the upgrade notes below.
  • artifacts configuration block reshaped. The new artifacts.repositories listing replaces the flat artifacts.url / artifacts.username / artifacts.password fields. Defaults already include both the pel (binary) and community (formae-plugin) repos, so users on the default config don't need to change anything. Users who set any of the deprecated fields will see deprecation warnings on agent startup — see Upgrading from 0.84 for the migration snippet, and Configuration → Artifact repositories for the new shape.

New features and improvements

The Public Hub

The platform.engineering Hub at hub.platform.engineering is now the single source of truth for distributing the formae binary, plugins, and tooling. It hosts:

  • The pel repo — the existing binary repository, where the formae binary, pkl, and other system tooling live. This is what setup.sh and formae update already pull from.
  • The community repo — the new repository for formae plugins. Resolves formae plugin install <name> requests, signed by the publisher and verified against the platform.engineering root CA on every install.

Both repos are reachable on every supported platform (linux-x86_64, linux-arm64, macos-x86_64, macos-arm64). The trust chain ties every published artifact back to a publisher cert chained to a platform.engineering root, so install-time verification works offline once the metadata cache is warm.

Plugins

  • formae plugin install / uninstall / update / search / info / list commands. Plugin lifecycle is now fully managed through the CLI. install and update accept optional @version pins. search filters by category, type, and channel. In 0.85, install, uninstall, and update operate locally. Run them on the agent host (typically sudo formae plugin install <name>) for resource plugins; run them on every host that runs the CLI or the agent for auth plugins. formae plugin list shows the agent and CLI views together with version-mismatch warnings. Container deployments are the natural single-step case: bake the install into the Dockerfile and the running container stays immutable. See formae plugin for the full reference.
  • The agent runs unprivileged. As part of the move to the new plugin distribution, /opt/pel is owned by root and the formae agent process runs as the pel user. formae plugin install and friends re-exec themselves under sudo to write into /opt/pel. A future release will restore the single-command "install on every host" UX for auth plugins.
  • Standard plugin metapackage. The new formae-plugin-standard metapackage pulls in the curated default plugin set: aws, azure, gcp, oci, ovh, and auth-basic. Container builds and the install script use it so a fresh install ships with the same plugins as 0.84 had bundled.
  • Centralized plugin builds. Every platform.engineering plugin is now built and signed in a single, locked-down workflow (formae-actions/plugin-build.yml). Plugin repos no longer hold signing material — they only carry a thin dispatch wrapper. Tag-only refs, single-SHA pinning, and a static repo allowlist guard the pipeline. The publisher signs every artifact with an intermediate cert chained to the platform.engineering root CA, and the community repo verifies that chain on install.

CLI

  • formae extract reuses your existing PklProject. When the target directory already has a PklProject, extract reads its declared dep versions and uses them for the generated .pkl instead of the latest installed schemas. The extracted output evaluates cleanly against your existing project — no version skew between extraction and apply. When extracting into a fresh directory (no PklProject), the existing behavior is preserved: a new PklProject is generated using the locally installed plugin versions.
  • formae project init --plugin-dir. New flag on project init for plugin developers iterating on @local schemas. Defaults to ~/.pel/formae/plugins so the typical workflow keeps working.

Kubernetes plugin

  • New plugin. Typed Pkl schemas per K8s minor (v1.21 through v1.34) — field mismatches fail at pkl eval time, not against your live cluster. Six Auth classes: KubeconfigAuth, InClusterAuth, EKSAuth, AKSAuth, GKEAuth, OCIAuth. See Kubernetes plugin.

Helm integration

  • Helm integration. Deploy upstream Helm charts as part of your forma. Reference a chart by name and version, set values inline.

.tfvar integration

  • .tfvars files can now be used directly in formae

Upgrading from 0.84

The 0.84 → 0.85 update preserves bundled plugins. Inside the 0.85 install, ops install formae will pull in the standard metapackage on first start, so you'll have the same plugin set you had before. If you need a non-standard plugin (compose, grafana, sftp), install it explicitly:

formae plugin install grafana sftp

After upgrading, you can verify what's installed with:

formae plugin list

If your 0.84 install was running a version of auth-basic, that plugin is already present on both the agent and the CLI hosts. In 0.85 the upgrade is local-only, so run sudo formae plugin upgrade auth-basic on each host where the plugin lives (one command on a single-host deployment, one per host on a split deployment).

Artifact configuration

If your formae.conf.pkl does not override artifacts, no action is needed — the default config now includes both the pel (binary) and community (formae-plugin) repositories. Plugin install just works.

If your formae.conf.pkl set any of artifacts.url, artifacts.username, or artifacts.password, you'll see deprecation warnings on agent startup. Migrate to artifacts.repositories:

// 0.84 (still works in 0.85, with deprecation warnings):
artifacts {
    url = "https://hub.platform.engineering/repos/platform.engineering/pel#stable"
}

// 0.85:
artifacts {
    repositories {
        new {
            uri = "https://hub.platform.engineering/repos/platform.engineering/pel#stable"
            type = "binary"
        }
        new {
            uri = "https://hub.platform.engineering/repos/platform.engineering/community#stable"
            type = "formae-plugin"
        }
    }
}

The username and password fields are deprecated without a like-for-like replacement in 0.85; per-repository credentials will be reintroduced in a later release. Operators relying on basic auth against the hub should reach out before upgrading.


0.84.0

Breaking changes

IMPORTANT — Manual installation required for this release. formae now uses the orbital package manager for installation and updates, so the formae upgrade command from previous versions cannot upgrade to 0.84.0. Install 0.84.0 using the one-line installer:

bash -c "$(curl -fsSL https://hub.platform.engineering/get/setup.sh)" -- install formae

After this one-time installation, future updates use formae update. See Installation for details.

  • upgrade command replaced by update: The formae upgrade command has been removed. Use formae update instead. The new command uses the orbital package manager and supports version pinning and channel selection. See update for details.
  • MessagePack wire protocol (plugin developers): The internal communication protocol between the agent and plugins has been replaced from Ergo's native EDF struct encoding to MessagePack with zstd compression. This is a foundational change that enables backwards-compatible schema evolution — future SDK releases can add new fields without requiring all plugins to be recompiled simultaneously. All existing plugins must be recompiled against Plugin SDK v0.2.1 for this release; plugins built against older SDK versions are detected as incompatible and skipped at startup with a clear warning message. See Plugin SDK v0.2.1 for migration details.

New features and improvements

Configuration

  • Per-plugin configuration: Resource plugin behavior can now be tuned per plugin instead of globally. In the new agent.resourcePlugins block you can enable or disable individual plugins, set per-plugin rate limits, declare which resource types each plugin discovers, override retry behavior, and customize how discovered resources are labeled. Plugins can also ship their own typed configuration schema (schema/Config.pkl) to expose plugin-specific settings. The global agent.retry, agent.discovery.resourceTypesToDiscover, and agent.discovery.labelTagKeys settings are deprecated in favor of these per-plugin overrides. See Configuration for the full reference.
  • Configuration blocks relocated: Authentication and network settings move from the plugins { ... } block to purpose-specific locations. Agent-side auth goes in agent.auth, CLI-side auth in cli.auth, and network config at top-level network. The previous plugins { authentication { ... } } and plugins { network { ... } } blocks are still supported with deprecation warnings. See Configuration and Security and networking.

Targets

  • Per-field config mutability: Target config fields can now be annotated as mutable or immutable by plugin authors. Changing a mutable field (like an AWS profile) produces an in-place target update without recreating resources. Changing an immutable field (like a region) triggers a full target replace as before. This is backwards compatible — targets without annotations treat all config changes as immutable. See Per-field config mutability for details.
  • Config change visibility: The simulate output now shows which target config fields changed and how (added, changed, removed), consistent with how resource property changes are displayed.

Resolvables

  • Reference individual items in collections: You can now pick a specific value out of a collection property instead of getting the entire collection. Use .at("key") for maps and .at(index) for lists. For example, reference a specific compose stack endpoint directly in your Grafana target:

    config = new grafana.Config {
        url = lgtmStack.res.endpoints.at("lgtm:3000")
    }

    This replaces the previous Endpoints/EndpointKey workaround. See Collection Resolvables.

Plugins

  • Datadog plugin: New official plugin for managing Datadog resources — monitors, SLOs, downtimes, log indexes, pipelines, archives, dashboards, teams, roles, and synthetics tests. See the Datadog plugin documentation.
  • GitHub Actions plugin: New official plugin for managing GitHub Actions resources — secrets, variables, environments, branch policies, workflow files, OIDC claims, runner groups, and org-level permissions. See the GitHub Actions plugin documentation.
  • GitLab plugin: New official plugin for managing GitLab project-scoped CI/CD resources — variables, environments, repository files, and typed .gitlab-ci.yml pipelines. See the GitLab plugin documentation.
  • Plugin compatibility checks: The agent now verifies plugin compatibility at startup and skips incompatible plugins with clear, actionable warnings instead of crashing. Checks work in both directions — plugins requiring a newer agent and plugins built against an older SDK are both detected. Plugins also verify agent compatibility at startup as a fallback.

Bug fixes

  • FIXED: Destroying a target with discovered (unmanaged) resources left those resources orphaned in the database, causing the sync cycle to crash or produce repeated errors. Discovered resources are now unregistered correctly when their target is destroyed, and a related registration leak that could permanently block discovered resources from future sync cycles is resolved.
  • FIXED: Patch-mode apply could produce spurious diffs when a resource contained nested objects whose children had all been stripped as provider defaults (e.g. Lambda DestinationConfig.OnSuccess/OnFailure). The now-empty parent object was left in the desired state, causing CloudControl to reject the update with "required fields missing".
  • FIXED: Patch-mode applies could show spurious diffs on resources with fields that are set at creation and never returned by the provider afterwards (typically secrets, tokens, or credentials). Repeated applies are now idempotent for these resources — the unreadable field is excluded from comparison instead of appearing as a phantom change.
  • FIXED: Non-portable target replace errors now return HTTP 409 (Conflict) with a clear message instead of HTTP 500 with a raw error string.
  • FIXED: Target create errors during crash recovery are logged at debug level instead of error, since the idempotent retry handles them gracefully.
  • FIXED: CLI help output previously generated documentation deep links that returned 404s. Help text now links to the docs root.
  • FIXED: Reduced log noise from the underlying state machine framework by demoting internal timeout messages from info to debug level.
  • FIXED: Default values filled in by the cloud provider on nested resource properties (for example fields AWS sets on ECS container definitions when you leave them unset) weren't recognised as such. On repeated applies of an unchanged forma, the agent saw these as real changes and could choose to recreate the resource instead of treating the apply as a no-op. Nested provider defaults are now ignored during comparison, so reapplies are idempotent.
  • FIXED: Targets whose config references resolvables no longer report false changes on repeated applies. Previously, applying the same forma twice in a row would flag the target as modified because the stored config carried the resolved value alongside the reference. Same forma → same apply outcome.
  • FIXED: Evaluating formae PKL files no longer fails with confusing Cannot find property allAnnotations errors when an older pkl binary happens to be first on your PATH. The CLI now uses the pkl binary shipped alongside the formae executable, so you don't need to manage your PATH manually.
  • FIXED: Simulate output now renders composite values in remove operations as JSON instead of Go's map[k:v] format, making add/remove diffs visually comparable.
  • FIXED: PKL schema hints for fields nested three or more levels deep inside sub-resources were silently dropped when the intermediate sub-resource had no @FieldHint annotation of its own. This could cause provider defaults deep in the schema to not be stripped, leading to spurious replacements on reapply.
  • FIXED: Resources with nested lists whose element order differed between stored state and desired state (for example, ECS Environment variables or PortMappings) could trigger spurious replacements. List comparison now ignores element order recursively.
  • FIXED: formae extract failed on resources with Float properties (for example, GKE Container Cluster's batchPercentage). These resources can now be extracted correctly.

0.83.2

Bug fixes

  • FIXED: Docker image: the .config directory was created as root during the plugin migration build step, preventing the pel user from writing to it at runtime. It is now chowned to pel alongside .pel.

0.83.1

Bug fixes

  • FIXED: Apply and destroy commands could get permanently stuck at InProgress after an agent restart. Target update states (e.g. target creation completing successfully) were only held in the agent's in-memory cache and never written to the database. If the agent restarted before the command completed, the target state reverted to NotStarted, causing overallCommandState to return InProgress indefinitely.
  • FIXED: A single failed discovery sync read (e.g. an AWS resource that CloudControl cannot read) could cascade-fail all other reads in the same sync command. Discovery sync commands were incorrectly creating dependency edges between independent read operations, so one failure would propagate to unrelated resources.

0.83.0

New features and improvements

Plugins

  • Grafana plugin: New official plugin for managing Grafana resources — dashboards, data sources, folders, alerting rules, teams, and service accounts. Works with both self-hosted Grafana and Grafana Cloud. See the Grafana plugin documentation.
  • Docker Compose plugin: New official plugin for managing Docker Compose stacks as infrastructure. Exposes resolvable endpoints that other plugins can reference — for example, a Grafana target can resolve its connection URL from a compose stack. See the Docker Compose plugin documentation.

Targets

  • Target replace: When a target's configuration changes (e.g. switching AWS region), formae now replaces the target and all managed resources in the correct order — delete resources, delete target, create target, recreate resources. This is only allowed when all resources on the target are marked as portable in their schema, ensuring safe recreation. In reconcile mode, stacks not present in the forma are preserved; in patch mode, everything managed on the target is replaced.
  • Resolvable target configuration: Target configuration fields now support resolvable references to resource properties. This enables cross-provider workflows where one provider's output feeds another provider's configuration — for example, a Kubernetes target whose cluster endpoint is resolved from an EKS resource. Targets with resolvable config are automatically ordered in the execution DAG to wait for their dependencies. See Target resolvables for examples.
  • Cascade target deletes: When destroying resources that a target depends on (via resolvable references), formae detects the dependency and either aborts (default) or cascade-deletes the dependent target and its resources. Use --on-dependents=cascade to proceed. Plain targets (e.g., cloud region targets) without resolvable dependencies survive destroy.

Operations

  • Azure cloud installer: One-command deployment of the formae agent to Azure Container Instances with managed identity authentication. See Install on Azure.
  • GCP cloud installer: One-command deployment of the formae agent to Google Cloud Run with Workload Identity Federation. See Install on GCP.

Schema

  • Atomic field updates: Fields annotated with updateMethod = "Atomic" are now compared as opaque values and produce a single replace operation instead of recursive sub-field diffs. This is needed for cloud provider fields like IAM policy documents where the API expects the entire value as a single update. See Collection Semantics for details.

Bug fixes

  • FIXED: Synchronization and updates could silently fail for resources with complex schemas (e.g. Kubernetes Pod, Deployment, AWS ECS TaskDefinition). Large messages between the agent and plugins were being dropped due to an internal transport limit. All plugin messages are now compressed, eliminating this size restriction.
  • FIXED: Agent could fail to recover after a crash, leaving commands stuck in an incomplete state.
  • FIXED: Concurrent apply commands targeting the same resource could create duplicates.
  • FIXED: Commands could reach terminal state before resource properties were fully persisted, causing stale inventory data.
  • FIXED: Concurrent stack deletion could cause panics.
  • FIXED: Recreating a resource after deletion could produce stale internal references.
  • FIXED: A background sync could interfere with an in-progress apply, causing unexpected failures.
  • FIXED: An update on a resource with resolvable references could get stuck if resolving the reference failed.
  • FIXED: formae apply could generate unnecessary resource replacements when createOnly fields hadn't actually changed.
  • FIXED: formae apply could generate spurious patch operations from empty arrays and maps produced by the PKL schema, leading to unnecessary replacements or validation errors.
  • FIXED: Provider-populated default values inside nested sub-resources (e.g. container CPU defaults in ECS TaskDefinitions) were incorrectly flagged as drift.
  • FIXED: Provider-populated default values in key-based collections (e.g. AWS LoadBalancer attributes) could cause patches to exceed API limits.
  • FIXED: Resources with required fields inside array properties (e.g. ClusterRole rules) could be silently rejected during validation.
  • FIXED: References nested inside array elements were not resolved during patch generation.
  • FIXED: formae extract produced invalid PKL in some cases involving stack references.
  • FIXED: Discovery could skip resources between cycles.
  • FIXED: Plugin error messages were not included in failure responses, making debugging difficult from the CLI.

Breaking changes

Plugin SDK update required. This release changes the internal message format between the formae agent and plugins. All plugins must be recompiled against pkg/plugin v0.1.21 to remain compatible. Plugins built against older SDK versions will fail with message decoding errors during synchronization and resource operations.


0.82.3

New features and improvements

  • Pkl extraction: Shared types are now imported during formae extract resolving issues where extracted Pkl code referenced types that weren't in scope.

Bug fixes

  • FIXED: Container image did not include cloud provider plugin binaries due to a missing dependency in the build process. All resource plugins (AWS, Azure, GCP, OCI, OVH) are now correctly packaged.
  • FIXED: formae apply in certain cases incorrectly rejected changes when modifications were already absorbed in the forma (e.g. via extract).
  • FIXED: Empty arrays [] and maps {} were incorrectly treated as missing values during patch comparison. This caused commands that remove all remaining items from a collection to silently leave items behind, and prevented drift detection from catching out-of-band additions to fields not present in the user's Pkl.
  • FIXED: CommandStatus API endpoint was not reading the command ID from the path parameter.
  • FIXED: API returned incorrect HTTP status codes for no-op and simulate commands.
  • FIXED: Agent could fail to start after a forced termination (SIGKILL, crash, or system reboot) because the stale PID file was not cleaned up. The agent now verifies the recorded PID is a live process and removes stale or corrupt PID files automatically.
  • FIXED: Resolvables referencing nonexistent resources were silently skipped during translation, only surfacing at runtime. Simulate would show a clean plan while the actual apply would fail. These are now caught early and reported as a clear error.
  • FIXED: formae extract accepted a directory path as target, failing later with a confusing error. It now validates the target path upfront and returns a clear message.

0.82.2

Bug fixes

  • FIXED: Simulation output now shows the actual value being removed from arrays instead of displaying "(empty)".
  • FIXED: Several Pkl union types with nullable alternatives were incorrectly declared.

Documentation

  • Installation guide restructured: Installation documentation is now split into dedicated pages for local, Docker, and Helm deployments.
  • Collection handling: New section in Apply Modes explaining how reconcile and patch modes differ when updating collections.

Plugin release notes

Official plugin release notes have moved to dedicated pages under each plugin's documentation. See AWS, Azure, GCP, OCI, and OVH.


0.82.1

Bug fixes

  • FIXED: Running formae extract on discovered resources could crash when all results were unmanaged or when results included a mix of managed and unmanaged resources. The $unmanaged stack doesn't exist in the database, so it was silently dropped during serialization, leaving the PKL generator with missing stack data. Both cases are now handled correctly.
  • FIXED: Conformance tests could fail for resources that have Resolvable references nested inside SubResource maps. The test runner was comparing these maps as flat strings instead of recognizing the nested references. The framework now recursively walks maps and correctly compares Resolvable fields at any depth.

0.82.0

New features and improvements

  • Policies (Beta): formae now supports policies—configurable behaviors that you attach to stacks, that allow you to choose different automation strategies for different sets of resources and use cases. This release introduces TTL policies for automatic cleanup after a duration, and auto-reconcile policies for automatic reversal of out-of-band and incremental changes at regular intervals. Policies can be inline (owned by a stack) or reusable (shared across stacks via .res reference). See the Policy documentation for details.
  • AI assistant integration (Beta): formae now ships with an MCP server and 13 skills that teach AI coding assistants how to manage infrastructure and open the way to partly or fully agentic, safe, IaC-centric workflows. Deploy, query, detect drift in your own codebase, and build plugins — all through natural conversation. Available as a Claude Code plugin via the formae marketplace. See the AI coding assistants documentation for details.
  • New formae inventory policies command to list all reusable policies. See the CLI documentation for details.
  • The formae inventory stacks command now shows attached policies for each stack.
  • HasProviderDefault field hint: New hasProviderDefault annotation for fields where cloud providers assign default values. When a field with this hint exists in actual state but is not specified by the user, formae accepts the provider's default instead of generating a "remove" operation. This prevents oscillation during reconcile for fields like S3 bucket encryption or SQS visibility timeout. See the schema reference for details.
  • How-to guides: documentation now includes a growing number of concrete guides how to use formae in different situations and for various use cases.

0.81.0

New features and improvements

  • Stack improvements: Stack descriptions are now correctly stored and updated when reapplying. Stacks are automatically deleted when their last resource is removed.
  • New formae inventory stacks command to query and list all stacks managed by formae. See the CLI documentation for details.
  • Aurora Data API datastore: New datastore option for AWS deployments using AWS RDS Data API. See the configuration documentation for details.
  • Empty targets can now be deleted using formae destroy. See the target documentation for details.
  • Non-interactive plugin init: The formae plugin init command now supports non-interactive mode for automation, CI pipelines, and LLM-assisted workflows. Use the --no-input flag with CLI flags to provide all required values. See the plugin tutorial for details.
  • Plugin dashboard: A new Grafana dashboard for monitoring plugin metrics is available in the formae-grafana-dashboards repository.
  • Plugin log messages now show the plugin name instead of the namespace.

Bug fixes

  • FIXED: Resources with fields that are arrays of resolvables are now handled properly.
  • FIXED: Plugin shutdown no longer produces spurious error messages.
  • FIXED: Improved import declaration generation when using formae extract.

0.80.1

Bug fixes

  • FIXED: The extract command would fail for plugins where the plugin name differed from the namespace.

0.80.0

Breaking Changes

  • Plugin SDK: Complete overhaul of the ResourcePlugin interface, the formae Pkl schema and plugin distribution. See the plugin SDK documentation for details.
  • Formae Pkl schema: nonprovisionable and persist resource annotations have been removed. Tags are no longer a first-class citizen in formae and will be superseded by plugin-specific implementations. For AWS, formae.Tag will be supported for three more releases before being removed. Please update tag definitions in your AWS Pkl files to use aws.Tag instead of formae.Tag.

New features and improvements


0.76.5

Bug fixes

  • FIXED: In some cases updates for AWS Tags were generated incorrectly, causing commands to fail.
  • FIXED: If there were unfinished commands on agent startup, the agent would some times try to resume those commands before the plugins were fully initialized.
  • FIXED: Commands would fail when a read operation failed with a recoverable error (like a rate limit).
  • FIXED: AWS::EC2::TaskSet was incorrectly marked as discoverable causing noise in the logs during discovery.

0.76.0

Breaking Changes

  • Plugin Architecture Migration: Plugins must now be built as standalone executables instead of shared libraries (.so files). Existing custom plugins need to be updated with a main() function that calls plugin.Run(). See the AWS plugin implementation for reference.

New features and improvements

  • Plugin Architecture Overhaul: Plugins now run as separate executables in their own processes rather than as shared libraries, enabling independent dependency graphs and multi-host deployment.
  • Major Performance Improvements: Significantly improved command processing performance. Previously limited to around 500 resources, the agent now handles up to 20,000 resources. See the agent sizing recommendations for resource requirements.
  • Enhanced Observability: The agent now exports comprehensive telemetry via OpenTelemetry including metrics, structured logs with trace correlation, and distributed traces for API requests, resource operations, and database queries.
  • Grafana Dashboards: Pre-built Grafana dashboards for monitoring formae are now available in the new formae-grafana-dashboards repository.
  • New temporality configuration option for OTLP metrics export, supporting both delta (OTel-native) and cumulative (Prometheus/Mimir) temporality.

Bug fixes


0.75.5

New features and improvements

  • Shell auto-completion is now supported for formae commands in bash and zsh. The installer can now install the necessary completion scripts
  • Sync commands that did not pull in any changes are now automatically removed from the database

Bug fixes


0.75.4

Bug fixes


0.75.3

Bug fixes


0.75.2

New features and improvements

  • Watch output prints useful hints at the end of the output
  • Targets are now discoverable by default
  • When creating a target, discovery is automatically initiated
  • Discovery now traverses multuple levels of parent-child relationships in resource hierarchies
  • Discovery can now handle multiple namespaces
  • Discovery creates resolvables to parent properties in child resources
  • Destroy queries no longer accept "managed" as a field parameter, as it does not apply to destroy operations

Bug fixes


0.75.1

New features and improvements

  • Implemented database migration for future changes
  • Discovery is enabled now by default in the configuration
  • Targets are now discoverable by default
  • Default discovery interval is now at 10 minutes instead of previously 1 hour

Bug fixes


0.75.0

New features and improvements

  • New formae cancel command that allows to cancel any apply or destroy command already being executed by the agent.
  • New formae inventory targets command to query and list targets with filtering by label, namespace, and discoverability.
  • Targets now support a discoverable field for granular control over which targets participate in discovery scans.
  • Pkl: Nested fields are now being parsed.
  • The lifeline example now uses more idiomatic PKL code.

Breaking changes

  • We are deprecating configuration of discovery targets through the agent config. Instead, targets need to be configured through forma application now.

Bug fixes


0.74.0

Public launch.