8.2 Definitions under change control
Included from Basic tier
After this you can: export and import definitions as backup and change control — one trigger or workflow, or the whole estate — build the zero-secrets support bundle, and treat the OpenAPI document as your integration contract.
Before you start — tick each one:
- Lesson 3.1 — Your first change (
opschain learn 3.1)
In the GUI: the project page's Triggers and Workflows tabs are the definitions this lesson exports; the Diagnostics panel in the Operations area downloads the same redacted support bundle.
Lesson 8.1 kept the server's data recoverable. This lesson keeps its definitions reviewable: in git, diffed between releases, restorable onto a fresh server — plus the two documents that keep a support case and an integration honest.
1. One definition at a time
Your triggers (lesson 5.2) and workflows (lesson 4.1) live in the server's database — that stays the single source of truth. But their definitions belong in a git repository too. The export/import verbs are that bridge:
mkdir -p backup/triggers backup/workflows
opschain triggers export acme/promote_on_success --out backup/triggers/promote_on_success.yaml
opschain workflows export acme/env_start --out backup/workflows/env_start.yaml
exported trigger acme/promote_on_success to backup/triggers/promote_on_success.yaml
exported workflow acme/env_start to backup/workflows/env_start.yaml
Exports are deterministic — stable key order, trailing newline — so a
re-export of an unchanged resource is byte-identical and your backup
repo's git diff shows real changes only. Restoring goes through the
same authorised create/update path a GUI edit uses:
opschain triggers import acme -f backup/triggers/promote_on_success.yaml
Error: trigger acme/promote_on_success already exists — pass --update to replace its definition from backup/triggers/promote_on_success.yaml
opschain triggers import acme -f backup/triggers/promote_on_success.yaml --update
opschain workflows import acme -f backup/workflows/env_start.yaml --update
updated trigger acme/promote_on_success from backup/triggers/promote_on_success.yaml
- Without
--update, importing an existing code is a named error and writes nothing — safe to run against a server whose state you are unsure of. - With
--update, a trigger's definition is replaced in place (the document fully replaces the stored definition, defaults included), and a workflow gains its next version — history is never rewritten. - Prove a backup round-trips: export → import
--update→ export again must be byte-identical.
The loop doubles as promotion between servers: export from staging, review the diff, import into production.
2. The whole estate's definitions
The system-scope cousin moves every definition between estates —
each live project subtree plus the records a subtree cannot carry:
local users and groups, authorisation rules and policies, the global
settings document and scheduled maintenance windows. Secret values are
always excluded (git remote credentials, secured settings values,
password hashes never travel; secret-vault:// references travel as
references). The export needs the source quiesced — the archive must
describe a still estate — and the import needs the target not
quiesced, because the barrier would refuse the import's own writes:
# on the source
opschain admin platform quiesce --reason "estate export"
opschain admin database export --out estate.zip
opschain admin platform resume
exported the estate to estate.zip (3 project(s))
excluded: git remote credentials, secured settings values, password hashes
# on the target
opschain admin database import estate.zip --into-empty
imported the estate archive into this server:
TYPE CREATED SKIPPED
users 4 0
groups 2 0
authorisation_rules 6 0
policies 3 0
projects 3 0
maintenance_windows 1 0
secrets never travel: re-enter git remote credentials, secured settings values and local user passwords
local users were created with generated passwords — reset each with `opschain admin users reset-password`
Import is ordinary authorised creation on the target: it skips — never
overwrites — anything that already exists, and --into-empty asserts
the target holds no projects yet and applies the archive's global
settings too. Run history, events and artifacts do not travel with an
export — they belong to the source estate's backup (lesson 8.1), not to
interchange.
3. The support bundle
When you need help, collect one archive instead of twenty screenshots:
opschain support bundle --server https://opschain.example.com --out bundle.tar.gz
wrote bundle.tar.gz (14 files; secrets redacted, key material excluded)
Your own opschain login session is the credential (then --auth-token
/ OPSCHAIN_ADMIN_TOKEN / the stored admin token, for automation). The
bundle carries the server configuration with every secret value
replaced by [REDACTED], a note of which keys exist (names only,
never values), the CLI build identity, and live diagnostics: health and
readiness, the admin status and queue views, the licence usage report,
the runner fleet, recent events and the metrics exposition. The
zero-secrets property is test-enforced — no credential, token or key
material can reach the bundle. Without --server it degrades
gracefully to an offline config-only bundle, and any unfetchable item is
recorded as an error file rather than failing the collection.
4. The OpenAPI document: your integration contract
Everything the CLI has done across these lessons went over the server's HTTP API, and the API describes itself (lesson 0.3):
opschain api spec --out openapi.yaml # the targeted server's document
opschain api spec --local # the copy embedded in this binary
opschain api routes --contains triggers # the route table, filtered
wrote the OpenAPI document to openapi.yaml
Build integrations on the served document, and treat it as the contract
it is: the spec is generated from the same route table the server
mounts — a route cannot exist without appearing in the document.
Against a server, the document is stamped with that server's version
(v5.7.0) and commit, so "which API am I actually
talking to" is always answerable; --local describes this binary
instead, for offline work.
Check yourself
- What does a definitions export leave out, and what does the support bundle never contain?
- Which side of an estate export must be quiesced, and which must not?
What you learned
- Trigger and workflow exports are deterministic YAML; import without
--updatenever overwrites. - The estate export carries every definition and no secret; import creates, skips the existing, and leaves history behind.
- The support bundle is redacted by construction and degrades to config-only offline.
- The OpenAPI document is generated from the route table and stamped with the server's version.
Next: lesson 8.3 — Decommission (opschain learn 8.3), or back to your track (opschain learn).