Skip to content

An environment has a mode

shipped 1.0.0

Every environment in cwp.yml carries a mode, and it has three values.

environments:
  prod:
    cloudron_app: example.com
    mode: protected      # refuses upward writes without --force
  staging:
    cloudron_app: staging.example.com
    mode: managed        # the default — absent means this
  legacy:
    cloudron_app: old.example.com
    mode: source         # read-only, always

The mode is one key rather than a switch beside a flag. A switch and a flag would give four combinations, and two of them mean nothing: a read-only environment has no use for a second key saying whether cwp refuses writes to it.

managed: the environment this project works with

The default, and the one everything else in the manual assumes. You can adopt a managed environment, read down from it and write up to it, subject to the guards: the restore point and the confirmation.

protected: the same, and cwp refuses every upward write

A partner you write to deliberately rather than routinely. Every crossing out of the tree stops:

✗ "prod" is a protected environment and refuses upward writes
  → pass --force if you really mean to write to example.com

--force passes it, and the run says so. The ledger records protectedEnvironment: "passed-with-force", so a --json consumer can see that a guard was overridden rather than absent.

protected is a rung of the guard ladder. That separates it from the value below.

source: an environment you read and never write

A source environment is one you take something from: a site in migration, an old install whose content you want, a colleague’s staging you have a login to. Reading is all cwp does with it. No flag says so: the mode is a property of the environment, declared once and true for every command.

cwp refuses an upward crossing against it before it considers anything else:

✗ this environment is `mode: source`, which is read-only and never a push target

And cwp refuses every remote write, not only the ones with push in the name. A downward crossing against a source goes through: read-only is not unreadable, and reading is the direction the mode exists for. But reading content mints an identity onto a post that has none, and minting is a write. The guard ladder refuses that too, and no flag opens it:

✗ "legacy" is `mode: source`, and this run would write to it
  → a source environment is read-only in every direction and no flag opens it —
    read it down instead, or change its `mode` in cwp.yml if this project owns it

A source capture therefore carries the items and not the identities; the section below says what that costs.

It is not protected with extra steps. protected refuses an upward write until you pass --force: the environment is one you own and sometimes write to deliberately. source has no --force. cwp never writes to a site this project does not own, so no flag opens one. The refusal comes before the ladder rather than being a rung of it.

Adoption does not apply

Adoption is the mutual record that says this project manages this environment: a marker on the site, a ref in the tree. A source environment gets neither, because both halves of that sentence are false. Its adoption state reads source (read-only), and cwp adopt declines it rather than pretending.

What a source capture does not carry

Reading from a managed environment gives every item a uid: an identity the site and the tree both hold. With it cwp recognises the same page again next week, on another environment, after an id changes. Minting that uid means writing to the site, and a source environment is one cwp does not write to.

So a source capture gives you the items and not the relationship:

  • Items arrive keyed by what they looked like, not by an identity both sides share. Read the same site twice after somebody renamed a page and cwp cannot tell you it is the same page.
  • There is no baseline. base records what a transfer agreed on. No transfer to a source environment can happen, so drift against it is not a question with an answer.
  • The crossing is one-way and one-time in spirit. A source capture is how content enters the project. From there it lives in the tree, and the tree is what has history.

Where cwp reads the mode

cwp status prints it beside each environment and carries it in --json. cwp adopt declines a source. Every upward crossing checks it first. Changing it means editing cwp.yml: deliberate, reviewable and visible in the diff, like everything else the tree declares.

Moving an adopted environment to source

Release it first:

cwp adopt prod --release

Adoption is a record on the site as well as in the tree, and changing the mode does not undo it. Editing cwp.yml alone leaves the site carrying a marker that says this project manages it. The new mode says cwp may not write to the site, so cwp cannot take the marker off.

cwp refuses rather than choosing which of the two to believe:

✗ "prod" is `mode: source`, which is read-only
  → it still carries an adoption record — take it off with
    `cwp adopt prod --release`, or change its `mode` in cwp.yml if the mode is
    what is wrong

cwp refuses a read too. A crossing writes a baseline into the ref. The ref already holds an adoption this mode says cannot exist. Reading past that would build on the contradiction and make it older. --release is exempt from the refusal, so the state has exactly one exit.