Configuration as code
Export an environment as one JSON document and apply it to another, atomically: the v2 format, revisions, preview, merge semantics and the diff.
GET /v1/config/export?schema=orishare.config/v2 returns the whole configuration of the
environment as one portable document: resources (currencies, attribute and event definitions,
segments, counters), programs with their versions, seasons, rules with their versions,
catalogues, offers, inventories, code_pools, assets, presentation, trust_policy and
the project context. Entries refer to each other by key, so the document applies to another
environment. No secrets are exported.
Always ask for orishare.config/v2. Without schema the export is the older
orishare.config/v1, which has no revision protection and is refused with
409 config_format_required in any environment that has native programs, seasons, catalogues,
currency capabilities or v2 rules. (legacy_only=true exports just the v1 part, for tools that
still read v1.)
Revisions
The v2 export carries the environment's configuration revision in the Configuration-Revision
response header (also the ETag, and Configuration-Environment names the environment). It is
not part of the document, so read it from the raw response. It identifies the state you read.
POST /v1/config/apply takes { "document", "mode", "expected_revision" }:
mode: "preview"plans the change and writes nothing. Withexpected_revisionit also refuses with409 stale_writeif the target changed since that revision.mode: "apply"requiresexpected_revision(409 stale_writewithout it, or when the target moved) and runs everything in one transaction. Unknown references, invalid rules and immutable changes (currency precision, counter shape, a held tier level) fail withvalidation_failednaming the path, and nothing is applied.
Both answers carry revision: the revision the request was checked against, not the
revision after the apply. To learn the new revision, export again. Applying the same document
twice is a no-op (changes: 0). Publishing rules in a production environment requires the owner or admin role
(or a secret key with rules:publish).
Apply is a merge
Apply creates what the document has and the target lacks, and updates what both have and that
differs. It never deletes: a resource, program or rule the target has and the document omits is
left as it is. To retire something, keep it in the document with its archived status (for
example "status": "archived" on a program or rule). The usual flow is to export, edit the
exported document and apply it whole.
Version digests are server-computed. Each program, season and rule row carries
versions[].digest and a head ({ version, digest }) that must name one of its versions. The
digest is a hash the server computes over the normalized definition, its bindings and its
dependencies; it is checked on apply (validation_failed when it does not match) and is not a
documented algorithm to reproduce. Copy versions and heads unchanged from an export (of this or
another environment), and change other fields around them. To add a new rule, do not write a rule
row by hand: propose a rule.create change set, then rule.publish_draft with key set to the
created rule's id (outcome.result.id of the first commit) and expected_version set to
outcome.result.version (Author a mechanic). The rule then appears in
the next export with its digests.
The answer
| Field | Meaning |
|---|---|
mode, applied | What ran; applied is true only for mode: "apply". |
diff | Per section (resources, programs, rules, ...), the entries under create, update and unchanged. |
changes | The number of create plus update entries. |
published_rules | The ids of the document's rules whose status is published, changed or not; empty with publish: false. |
notes | Plain-language notes about the plan. |
revision | The revision the request was checked against. |
A diff entry is a JSON-encoded tuple [kind, catalogue, key, version], with null for the parts
a section does not use: "[\"event_definition\",null,\"article_shared\",null]" is the event
definition article_shared, "[\"streak\",null,\"daily_lessons\",null]" the streak program
daily_lessons. Parse it with JSON.parse. Single-object sections (presentation,
trust_policy, context) use the section name as the entry. There is no delete list, because
apply never deletes.
Over HTTP and with the CLI
# Export, keeping the revision.
curl -sD headers.txt "$API/v1/config/export?schema=orishare.config/v2" \
-H "Authorization: Bearer $ORISHARE_SECRET_KEY" > config.json
# Preview, then apply exactly what was reviewed.
curl -s "$API/v1/config/apply" -H "Authorization: Bearer $ORISHARE_SECRET_KEY" \
-H 'Content-Type: application/json' \
-d "{\"document\": $(cat config.json), \"mode\": \"preview\", \"expected_revision\": \"$REVISION\"}"The orishare CLI wraps the same calls (config pull --schema v2, config push); it is a
workspace package of the Orishare repository, not published to npm. See
Promote between environments.