Declaring properties
A forma thatextends "@formae/forma.pkl" declares its properties as a plain
typed class:
- The member name is the CLI flag.
teambecomes--team,sizebecomes--size. Override this with@formae.Flagwhen the two should differ. - A member with a default is optional; a member without one is required.
Here
sizedefaults to"xs", andteammust be supplied. - The member’s type validates the input. A value that does not fit the declared type (or its constraints, below) is rejected before anything is applied.
Declaring properties as a typed class requires
extends "@formae/forma.pkl",
not amends. extends opens the forma module so property values can be injected
into it; amends keeps working for the legacy block form
below.Reading properties
Read a property directly by its member name, with the right static type:properties.team is the String; there is no .value to unwrap. Because the
shape is typed, editors resolve properties.team instead of flagging it as an
unresolved reference, so the property reads have full completion and type
checking.
Override the flag name
By default the member name is the flag, so a camelCase member likecertArn
would be exposed as --certArn. Annotate the member with @formae.Flag to
decouple the two, keeping the typed member name while choosing the flag a
consumer passes:
--cert-arn (and -p cert-arn=...), while the forma still
reads the member as properties.certArn, with full editor resolution:
--help and the property manifest show the overridden flag (cert-arn); the
member name (certArn) is unchanged.
Constraining the properties
Constrain a member with a Pkl type so a bad input fails before anything is applied rather than mid-deploy:team is a closed enumeration: only "team-a" or "team-b" type-check. size
takes a pattern constraint, useful when you want to validate shape rather than
enumerate every legal value. port constrains a number: --port -5 fails the
Int(this > 0) constraint at evaluation time. Either way, an input that doesn’t
satisfy the constraint stops the apply before any cloud call.
Setting the properties
A consumer, whether a developer or a CI/CD job, only needs to supply properties when runningapply. They don’t need to understand the forma itself.
To see the properties a forma exposes, pass it to --help:
Legacy properties block
A forma thatamends "@formae/forma.pkl" declares properties as a properties {}
block of formae.Prop objects, read through .value. This form still works and
needs no migration:
flag becomes a --flag option, and a property without a
default is required, exactly as in the typed form. The difference is
ergonomics: the block form reads values with .value and is not statically
typed, so editors cannot resolve properties.team and constraints live in
separate typealias/function definitions rather than on the member itself.
Prefer the typed extends form for new formae.
See also
- Write your first forma: a hands-on walkthrough that adds a property to a real bucket.
- Build self-service infrastructure: expose a parameterized forma as a developer-facing interface.
- Apply modes: reconcile vs. patch, and how properties interact with each.

