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

# Manage stack policies

> Attach a lifecycle policy to a stack: expire it after a duration with TTL, or keep it pinned to its declared state with auto-reconcile. Attach one inline, or define it once and reuse it across stacks.

A [policy](/documentation/concepts/policy) is a lifecycle rule you attach to a
stack. Where a forma declares what resources should exist, a policy declares how
formae manages the stack over time on its own, without you running `apply` again.
There are two:

* [TTL](/documentation/concepts/policies/ttl): destroy the stack after a duration, for ephemeral environments.
* [Auto-reconcile](/documentation/concepts/policies/auto-reconcile): re-apply the declared state on an interval, so drift never sticks.

You attach a policy one of two ways:

* **Inline**, declared directly on a single stack. It belongs to that stack and
  is deleted with it.
* **Reusable** (standalone), defined once with a label and referenced from any
  number of stacks. Edit it in one place and every stack that references it picks
  up the change.

The [Policy concept](/documentation/concepts/policy) covers the trade-offs in
full. This guide shows how to attach both kinds.

<Tabs>
  <Tab title="CLI">
    <Steps>
      <Step title="Attach an inline policy">
        Add a `policies` listing to the stack. A TTL policy expires it after a
        duration:

        ```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
        new formae.Stack {
          label = "dev-environment"
          description = "Ephemeral dev workspace"
          policies = new Listing {
            new formae.TTLPolicy {
              ttl = 4.h
              onDependents = "abort"
            }
          }
        }
        ```

        Or keep the stack pinned to its declared state with auto-reconcile:

        ```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
        policies = new Listing {
          new formae.AutoReconcilePolicy {
            interval = 5.min
          }
        }
        ```

        Declare the policy inside your stack's complete forma, alongside its
        resources. Reconcile compares the whole stack, so applying a forma that
        holds only the policy would remove the stack's resources.
      </Step>

      <Step title="Share one policy across stacks">
        To apply the same rule to several stacks, give the policy a `label` and
        define it once at the top of the forma, then reference it from each stack
        with `.res`:

        ```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
        local ephemeral = new formae.TTLPolicy {
          label = "ephemeral-24h"
          ttl = 24.h
          onDependents = "abort"
        }

        forma {
          ephemeral

          new formae.Stack {
            label = "dev-sandbox-1"
            description = "Sandbox for the payments team"
            policies = new Listing { ephemeral.res }
          }

          new formae.Stack {
            label = "dev-sandbox-2"
            description = "Sandbox for the checkout team"
            policies = new Listing { ephemeral.res }
          }
        }
        ```

        Change `ttl` in the one definition and both stacks pick it up on the next
        apply.
      </Step>

      <Step title="Apply">
        ```bash theme={"languages":{"custom":["/languages/pkl.json"]}}
        formae apply --mode reconcile your-infra.pkl
        ```

        <div className="fa-term">
          <div className="fa-term-body">  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(232,232,232)',fontWeight:'600'}}>formae</span> <span style={{fontWeight:'600'}} /><span style={{color:'rgb(255,133,51)',fontWeight:'600'}}>apply · reconcile</span>                                                                          your-infra.pkl<br /><span style={{color:'rgb(85,85,102)'}}>────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><br /><span style={{color:'rgb(85,85,102)'}} />  <span style={{color:'rgb(232,232,232)'}}>+</span> 3 <span style={{color:'rgb(232,232,232)'}}>create</span><br /><br />  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(255,133,51)',fontWeight:'600'}}>▌ Policies</span><br />  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(232,232,232)',fontWeight:'600'}} /><span style={{color:'rgb(232,232,232)',fontWeight:'600'}}>Operation ▲   </span><span style={{color:'rgb(170,170,170)'}}>Label                                   Type                Stack</span><br />  <span style={{color:'rgb(232,232,232)'}}>+ create      ephemeral-24h                           ttl                 </span><br />  <span style={{color:'rgb(170,170,170)'}}>+ create      </span><span style={{color:'rgb(129,209,219)'}}>ephemeral-24h                           </span><span style={{color:'rgb(170,170,170)'}}>ttl                 dev-sandbox-1</span><br />  <span style={{color:'rgb(170,170,170)'}}>+ create      </span><span style={{color:'rgb(129,209,219)'}}>ephemeral-24h                           </span><span style={{color:'rgb(170,170,170)'}}>ttl                 dev-sandbox-2</span><br /><br /><span style={{color:'rgb(85,85,102)'}}>────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><br /><span style={{color:'rgb(85,85,102)'}} />  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>↑↓</span><span style={{color:'rgb(153,153,153)'}}>: select</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>space</span><span style={{color:'rgb(153,153,153)'}}>: expand</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>→←</span><span style={{color:'rgb(153,153,153)'}}>: column</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>s</span><span style={{color:'rgb(153,153,153)'}}>: sort</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>y</span><span style={{color:'rgb(153,153,153)'}}>: confirm</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>q</span><span style={{color:'rgb(153,153,153)'}}>: abort</span> This operation will create 1 policy(ies).  Do<br />you want to continue? (y/N)  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>?</span><span style={{color:'rgb(153,153,153)'}}>: help</span></div>
        </div>

        The plan shows the policy being created and attached to each stack (for
        the reusable example above, `create policy ttl ephemeral-24h`, then
        `attach ephemeral-24h to dev-sandbox-1` and `attach ephemeral-24h to
                        dev-sandbox-2`). From then on formae enforces it: TTL destroys the stack
        when the duration elapses; auto-reconcile hard-reconciles on every
        interval.
      </Step>

      <Step title="Verify">
        Reusable policies and the stacks they are attached to show in:

        ```bash theme={"languages":{"custom":["/languages/pkl.json"]}}
        formae inventory policies
        ```

        <div className="fa-term">
          <div className="fa-term-body">  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(232,232,232)',fontWeight:'600'}}>formae</span> <span style={{fontWeight:'600'}} /><span style={{color:'rgb(255,133,51)',fontWeight:'600'}}>inventory</span><br /><span style={{color:'rgb(85,85,102)'}}>────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><br /><span style={{color:'rgb(85,85,102)'}} />                                         <span style={{color:'rgb(129,209,219)'}}>╭────────────╮</span><br /> <span style={{color:'rgb(170,170,170)'}}> 1 Resources </span>  <span style={{color:'rgb(170,170,170)'}}> 2 Targets </span>  <span style={{color:'rgb(170,170,170)'}}> 3 Stacks </span>  <span style={{color:'rgb(129,209,219)'}}>│</span><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}> 4 Policies </span><span style={{color:'rgb(129,209,219)'}}>│</span><br /><span style={{color:'rgb(129,209,219)'}}>─────────────────────────────────────────╯            ╰─────────────────────────────────────────────────────────────</span><br /><span style={{color:'rgb(129,209,219)'}} /><br /><span style={{color:'rgb(129,209,219)'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}} /><span style={{color:'rgb(232,232,232)',fontWeight:'600'}}>Label ▲             </span><span style={{color:'rgb(170,170,170)',fontWeight:'600'}}>Type                AttachedStacks                </span><br /><span style={{color:'rgb(85,85,102)'}}>──────────────────────────────────────────────────────────────────────</span><br /><span style={{color:'rgb(232,232,232)'}} /><span style={{color:'rgb(232,232,232)'}}>ephemeral-24h       ttl                 dev-sandbox-1, dev-sandbox-2  </span><br /><br />Showing 1 of 1 policies<br /><span style={{color:'rgb(85,85,102)'}}>────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><br /><span style={{color:'rgb(85,85,102)'}} />  <span style={{color:'rgb(153,153,153)'}}>/: query</span><br /><span style={{color:'rgb(85,85,102)'}}>────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><br /><span style={{color:'rgb(85,85,102)'}} />  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>↑↓/j/k</span><span style={{color:'rgb(153,153,153)'}}>: navigate</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>enter</span><span style={{color:'rgb(153,153,153)'}}>: detail</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>/</span><span style={{color:'rgb(153,153,153)'}}>: search</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>s</span><span style={{color:'rgb(153,153,153)'}}>: sort</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>r</span><span style={{color:'rgb(153,153,153)'}}>: refresh</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>1-4</span><span style={{color:'rgb(153,153,153)'}}>: tab</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>q</span><span style={{color:'rgb(153,153,153)'}}>: quit</span>                       <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>?</span><span style={{color:'rgb(153,153,153)'}}>: help</span></div>
        </div>

        Inline policies have no independent identity, so to see the policy on a
        given stack, check the stack itself:

        ```bash theme={"languages":{"custom":["/languages/pkl.json"]}}
        formae inventory stacks
        ```

        <div className="fa-term">
          <div className="fa-term-body">  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(232,232,232)',fontWeight:'600'}}>formae</span> <span style={{fontWeight:'600'}} /><span style={{color:'rgb(255,133,51)',fontWeight:'600'}}>inventory</span><br /><span style={{color:'rgb(85,85,102)'}}>────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><br /><span style={{color:'rgb(85,85,102)'}} />                             <span style={{color:'rgb(129,209,219)'}}>╭──────────╮</span><br /> <span style={{color:'rgb(170,170,170)'}}> 1 Resources </span>  <span style={{color:'rgb(170,170,170)'}}> 2 Targets </span>  <span style={{color:'rgb(129,209,219)'}}>│</span><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}> 3 Stacks </span><span style={{color:'rgb(129,209,219)'}}>│</span>  <span style={{color:'rgb(170,170,170)'}}> 4 Policies </span><br /><span style={{color:'rgb(129,209,219)'}}>─────────────────────────────╯          ╰───────────────────────────────────────────────────────────────────────────</span><br /><span style={{color:'rgb(129,209,219)'}} /><br /><span style={{color:'rgb(129,209,219)'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}} /><span style={{color:'rgb(232,232,232)',fontWeight:'600'}}>Label ▲                   </span><span style={{color:'rgb(170,170,170)',fontWeight:'600'}}>Description                                     Policies                            </span><br /><span style={{color:'rgb(85,85,102)'}}>──────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><br /><span style={{color:'rgb(232,232,232)'}} /><span style={{color:'rgb(232,232,232)'}}>dev-sandbox-1             Sandbox for the payments team                   TTL: 1d, expires in 23h58m (ephemer…</span><br /><span style={{color:'rgb(232,232,232)'}} /><span style={{color:'rgb(232,232,232)'}}>dev-sandbox-2             Sandbox for the checkout team                   TTL: 1d, expires in 23h58m (ephemer…</span><br /><br />Showing 2 of 2 stacks<br /><span style={{color:'rgb(85,85,102)'}}>────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><br /><span style={{color:'rgb(85,85,102)'}} />  <span style={{color:'rgb(153,153,153)'}}>/: query</span><br /><span style={{color:'rgb(85,85,102)'}}>────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><br /><span style={{color:'rgb(85,85,102)'}} />  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>↑↓/j/k</span><span style={{color:'rgb(153,153,153)'}}>: navigate</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>enter</span><span style={{color:'rgb(153,153,153)'}}>: detail</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>/</span><span style={{color:'rgb(153,153,153)'}}>: search</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>s</span><span style={{color:'rgb(153,153,153)'}}>: sort</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>r</span><span style={{color:'rgb(153,153,153)'}}>: refresh</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>1-4</span><span style={{color:'rgb(153,153,153)'}}>: tab</span>  <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>q</span><span style={{color:'rgb(153,153,153)'}}>: quit</span>                       <span style={{fontWeight:'600'}} /><span style={{color:'rgb(129,209,219)',fontWeight:'600'}}>?</span><span style={{color:'rgb(153,153,153)'}}>: help</span></div>
        </div>
      </Step>
    </Steps>
  </Tab>

  <Tab title="AI assistants">
    With the [formae MCP plugin](/documentation/guides/ai-coding-assistants)
    connected, describe the lifecycle you want and let the assistant define the
    reusable policy and attach it to each stack:

    <div className="fa-convo">
      <div className="fa-convo-head">Conversation</div>
      <div className="fa-turn fa-you"><div className="fa-tag">You</div><div className="fa-body">Create a reusable 24-hour TTL and attach it to both the dev-sandbox-1 and dev-sandbox-2 stacks.</div></div>
      <div className="fa-turn fa-bot"><div className="fa-tag">Assistant</div><div className="fa-body">I simulated it first. It creates one <code>ttl</code> policy, <code>ephemeral-24h</code> (24 hours, <code>onDependents = abort</code>), then attaches it to <code>dev-sandbox-1</code> and <code>dev-sandbox-2</code>. Each stack is destroyed 24 hours after the policy attaches, and editing the one policy updates both. Apply it?</div></div>
      <div className="fa-turn fa-you"><div className="fa-tag">You</div><div className="fa-body">Yes.</div></div>
      <div className="fa-turn fa-bot"><div className="fa-tag">Assistant</div><div className="fa-body">Done. <code>ephemeral-24h</code> now covers both sandboxes. Attach it to another stack any time by pointing that stack at the same policy.</div></div>
    </div>
  </Tab>
</Tabs>

<Warning>
  A TTL with `onDependents = "cascade"` deletes resources in other stacks if they
  depend on this one, anywhere the dependency chain leads. Use the default `abort`
  unless you are certain nothing outside the stack should survive it.
</Warning>

## See also

* [Policy](/documentation/concepts/policy): inline versus reusable policies.
* [TTL policy](/documentation/concepts/policies/ttl): automatic cleanup after a duration.
* [Auto-reconcile policy](/documentation/concepts/policies/auto-reconcile): automatic enforcement of declared state.
