Configuration
llms.txtlists every page for an agent
plywood reads the nearest plywood.yml (or plywood.yaml) in the working directory or a directory above it, stopping at the repository root. plywood config check checks it and prints the settings in force, and plywood.schema.json in the package is its JSON schema.
stacks:
shop/dev: { dir: infra }
shop/prod-us: { dir: infra }
gate: on-destructive
apply: { when: merge }
locks: { when: preview }
policy: { rego: policy/, packs: [policy/aws-guard] }
drift: { schedule: "0 6 * * *" }| Key | Default | Meaning |
|---|---|---|
stacks |
found by init |
each stack as project/stack, with dir, paths, gate, backend and actions |
stacks.<stack>.actions |
none | the stack’s day-2 actions, each a name, the urn of a command:local:Command or command:remote:Command resource in the stack, and a description; config check refuses a URN of another type, on another stack, and a name used twice |
waves |
one wave per environment, dev before staging before prod | a list of waves, each a list of stacks, the first wave the canary; or a mapping with canary, the stacks that update first, and after, each stack to the stacks it updates after |
engine |
a plunge+ backend picks plunge, else Pulumi |
pulumi or plunge |
backend |
PULUMI_BACKEND_URL, then Pulumi.yaml’s backend.url |
the backend URL, for every stack or per stack under stacks |
gate |
on-destructive |
always, on-destructive or never: when a wave waits for an approval |
approval |
ledger |
ledger, sealed or pr-review |
preview |
false |
true makes plywood init set approvals.preview in the waves spec, so a merged wave applies only the plans its pull request previewed, or exits 4. Off by default since 0.3.1 because of two limits: the second of two merges that change different resources of one stack is refused, and a pull request that creates an autonamed resource is refused at merge |
approval_rules |
none | per stack glob, role or roles from .chant/trust.json and a quorum (default 1); read at base |
apply.when |
merge |
merge, or pull-request for /plywood up before merge |
apply.requires |
all four | with when: pull-request, the conditions an open pull request meets before /plywood up updates it: approved, mergeable, undiverged, checks |
locks.when |
preview |
preview, from the first preview, or up, from an update before merge or a /plywood lock comment |
locks.grain |
resource on plunge, stack on stock Pulumi |
resource or stack |
policy.rego |
none | the directory of Rego rules: deny_* denies, hold_* waits for a person, warn_* is a note line |
policy.engine |
conftest |
conftest or opa, the result shape the rules’ engine prints; a hold needs opa |
policy.namespace |
every package (conftest), main (opa) |
the Rego package to read |
policy.command |
the engine’s own | the argv to run in place of the engine’s; {policy} is the rules’ directory at base, {input} the change set |
policy.source |
none | a shared rule set from another repository: repo, ref and path (default policy); packs are then paths inside it |
policy.providers |
none | the provider packages a stack may use; plywood init writes them as deny_provider_package in policy.rego and as the CrossGuard pack policy/plywood-providers |
policy.packs |
none | CrossGuard packs the preview and the update run with --policy-pack; mandatory violations deny, advisory ones are note lines |
identity.preview, identity.up |
the job’s own credentials | each job’s identity per cloud: aws a role ARN, gcp a service account, azure a client id, pulumi a Pulumi Cloud token (team:<name>, user:<login>, organization or organization:admin) |
identity.stacks |
none | stack glob to its own preview and up identities; the first glob that matches wins |
identity.aws, identity.gcp, identity.azure |
none | each cloud’s trade settings: region, audience, endpoint; provider, audience, sts_endpoint, iam_endpoint; tenant_id, subscription_id, audience, authority |
pulumi_cloud.org |
none | the Pulumi Cloud organization the stacks’ state is in |
pulumi_cloud.api_url |
https://api.pulumi.com |
the API of a self-hosted Pulumi Cloud |
pulumi_cloud.audience |
urn:pulumi:org:<org> |
the OIDC token’s audience |
review_stacks.template |
off | the stack a review stack’s config starts from, as project/stack |
review_stacks.name |
pr-{number} |
the review stack’s name, pr-{number} or pr-{number}-<suffix>; only stacks named so are destroyed |
review_stacks.ttl |
chant’s | how long a review stack lives after its last update, such as 72h |
notify.slack.webhook, notify.teams.webhook |
off | the variable holding the incoming webhook’s URL |
notify.webhook.url, notify.webhook.key |
off | the variables holding a generic webhook’s URL and its signing key |
notify.on |
every event | the events to send |
notify.relay |
off | the variable holding the relay key, so a waiting wave’s Slack and Teams messages carry an approval plywood notify relay records |
telemetry.endpoint |
off | the OTLP/HTTP base URL; /v1/traces is added |
telemetry.headers_env |
none | the variable holding the OTLP headers |
telemetry.service_name |
plywood |
the trace’s service.name |
telemetry.traces |
true |
false turns traces off |
reports.url |
none | where each run’s report is kept: s3://, gs://, az:// or file:// |
drift.schedule |
off | a five-field cron line the drift job runs on |
drift.stacks |
every stack | the stacks the drift job reads, by name or project/* glob |
respond.drift |
issue |
issue, pull-request (the issue and the drift agent’s pull request), up (the issue and a gated update) or none |
review |
off | true, or a mapping with instructions (default .chant/review.md), command, key_env (default ANTHROPIC_API_KEY) and timeout |
agent.comment |
false |
answer /plywood agent <ask> on a pull request |
agent.drift |
false |
open a pull request that fixes a drifted stack |
agent.command, agent.key_env, agent.timeout |
as for review |
the model command, its key’s variable and its minutes |
agent.max_runs |
none | the most agent runs per day |
agent.token_env |
the job token on GitHub and Forgejo, CHANT_FORGE_TOKEN on GitLab |
the secret holding the token the agent’s push job pushes with; a GitHub push with the job token starts no workflow, so name a user’s or app’s token here for the agent’s commit to get a preview |
branches |
none | branch name to stack globs; a stack a glob matches updates from that branch alone |
labels |
none | pull request label to stack globs; a labelled pull request previews only the stacks its labels match |
forge.kind, forge.url |
read from the repository’s remote | github, gitlab or forgejo, and the base URL of a self-hosted forge |
pulumi, plunge |
the image’s release | the engine release the jobs run: one for every stack, or a map of stack glob to release |
image |
plywood’s image for each stack’s engine | an image pinned by digest, or a mapping with name and the plugins it carries |
runners |
chant’s default runner | a label, a list of labels or a GitHub runner group; or default, preview, up and drift each mapped to one |
env |
none | variable to value, set in the preview, up and drift jobs; values are written into the workflow, so a secret goes under pass |
pass |
none | secrets the jobs read as variables, by name: a list of secret names, or a mapping of variable to secret; the workflow names the secret and never holds its value |
parallelism |
a preview runs a wave’s stacks at once, an update one at a time | the most stacks a preview or an update runs at once in a wave |
environments |
none | stack glob to the forge deployment environment its up jobs run in, a name or a mapping with name and url; the first glob that matches wins, a wave whose stacks are in different environments runs one job per environment, and plywood forge check checks each up job against it (GitHub and GitLab) |
control |
off | the control repo this repository tells after each update on merge: a project key or URL, or a mapping with repo, op (default pulumi-up), member, forge, token_secret, read_token_secret, ref and api_base |
plywood turns each key into chant’s workspace settings, so plywood.yml holds no chant key. A ci key there is refused with the name of the plywood key that sets it.