# `gno.land/p/moul/pausable` The switch a realm checks before it acts: a parsed pause state, the rule for combining two of them, and the asserts that stop a call. It holds no state and knows nothing about where a pause setting is stored. [`r/moul/config`](../../../r/moul/config) is the realm that stores one and wires this to it; this package is the part worth getting right once. ```go st := pausable.MustParse(raw) // a stored value, failing closed st.AllowsRead() // false only when fully paused st.AllowsWrite() // false unless running st.AssertWritable() // aborts, with the reason if there is one st.Notice() // the markdown banner, or "" while running pausable.Strictest(a, b) // combine a global and a per-realm state ``` ## Three levels, because "paused" is usually too blunt Taking a realm fully offline hides the thing people came to read. Most incidents only need the writes stopped: a board keeps rendering, an exchange keeps quoting, nothing new lands while the fix is prepared. | level | value | reads | writes | |---|---|---|---| | Running | `""` or `running` | yes | yes | | ReadOnly | `readonly` | yes | no | | Paused | `paused` | no | no | Each optionally followed by `: `, which shows up in the banner and in the abort message: `paused: migrating storage, back in an hour`. So a realm guards its mutating functions and leaves `Render` alone: ```go func Post(cur realm, body string) { config.AssertWritable() // aborts while ReadOnly or Paused ... } func Render(path string) string { return config.TopBlock() + body // the banner explains itself } ``` `AssertReadable` exists but most realms should not put it in `Render`. A page that aborts tells a reader nothing; the banner tells them what happened and when to come back. Reach for it only where serving stale data is itself the harm. ## It fails closed, and that is why the writer must validate `MustParse` turns a value it does not recognise into **Paused**, not Running. A pause switch that a typo silently disables is not a pause switch: the failure would be invisible until the incident it was meant to cover. The cost is that a typo takes a realm offline, so `Parse` reports `ok=false` and the storing realm is expected to refuse the write. **Validate on the way in, fail closed on the way out.** `r/moul/config` does exactly that, so in practice the closed path is never reached. The values this refuses are the ones someone would plausibly type: `yes`, `true`, `1`, `on`, `stop`, `read-only`. None of them may read as Running. ## The stricter of two states wins A global pause and a per-realm pause combine with `Strictest`, so a per-realm entry left behind from last month can never re-open a realm during a global halt. The cost is that exempting one realm from a global pause is not expressible. Clear the global and set the others instead. That is the right trade for an emergency brake: the failure mode of the alternative is a stale exemption nobody remembers, discovered during the incident. The reason travels with the level that won, and on a tie the first argument does, so calling `Strictest(global, scoped)` keeps the global explanation when both say the same thing. ## Notice is one markdown block The reason is text a manager typed, and it lands inside a blockquote where a newline would end the quote and let the rest render as page content. `Notice` folds it to a single line. --- Part of **[moul/gno-contracts](https://github.com/moul/gno-contracts)** — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage. > ⚠️ **Disclaimer:** provided as-is, without warranty; not security-audited. Full disclaimer: [DISCLAIMER](https://github.com/moul/gno-contracts/blob/main/DISCLAIMER.md).