We're rebuilding how Greentic apps get deployed: instead of "stuff that runs on AWS", a deployment is now a named Environment that owns its config, its secrets, its bundles, its revisions, and how traffic is split between them.
Greentic apps already follow a layered model: small WASM Components are wired into
Flows, packaged into Packs, composed into Bundles,
and run by a Runner. The redesign adds an explicit Environment layer
between Bundle and Runner — so the same bundle can be deployed to local, staging,
or customer-X-prod without rewriting it.
Why this matters: today "deploy" silently means "AWS, with config baked into secrets, revisions you can't roll back". After this redesign, every deploy is a named environment with explicit revisions, traffic splits, and one-step rollback — and the same machinery works for a laptop demo, a customer pilot, and a production cluster.
Four targeted security gates landed before the redesign started, so the rewrite doesn't sit on a leaky foundation:
New crate greentic-deploy-spec owns the types every other crate now agrees on:
Environment, Revision, TrafficSplit,
BundleDeployment, Credentials. JSON-schema-derivable.
70 unit tests green.
EnvironmentStore trait + LocalFsStore implementation. Per-env file lock,
atomic writes, local backups, ULID generation. The whole read-modify-write cycle now runs inside
transact() so two parallel op commands can't clobber each other.
113 tests green across the surface.
op CLI surface done 2026-05-19The actual command set users type. Eight nouns, each with their own verbs:
| Noun | Verbs | What it manages |
|---|---|---|
env | create · update · list · show · doctor · destroy | environments themselves |
env-packs | add · update · remove · rollback · list | which packs (deployer/secrets/state/…) provide which capability |
bundles | add · update · remove · list | app bundles deployed into an env |
revisions | stage · warm · drain · archive · list | versions of a bundle, with a real lifecycle |
traffic | set · show · rollback | weighted traffic between revisions; one-step rollback |
config | show · set | host config + per-slot answer presence |
credentials | requirements · bootstrap · rotate | preconditions enforced; heavy logic deferred to A5/Phase D |
secrets | list · put · get · rotate | preconditions enforced; backend dispatch deferred to A9 |
Every output goes through a single JSON envelope — {op, noun, result} on success,
{op, noun, error: {kind, message}} on failure — so scripts get a stable contract
regardless of which binary they invoked it through (gtc op …,
greentic-operator op …, or greentic-deployer op …).
local environment auto-create next
First gtc setup on a clean machine will write a default local env with
sensible bindings (local-process deployer, dev-store secrets,
stdout telemetry, in-memory sessions + state).
After this, gtc start ./my-bundle.gtbundle just works.
gtc-dev / greentic-deployer-dev). Your locally
installed gtc 1.0.x is the stable lane and doesn't have it yet. To play with A3,
install the dev binary alongside.
gtc 1.0.x)What you already had:
gtc setup — bundle setup wizardgtc start — local runner (legacy orchestration)gtc admin … — admin verbs (now require explicit --target aws|azure|gcp instead of silently defaulting to aws)gtc-dev 1.1.0-dev.*)What A3 just unlocked:
op surface — all 8 nouns above--schema flag on every verb (machine-readable input schemas)--answers file.json for non-interactive runstraffic set with replay protectiontraffic rollback back to single-revision — all without a running runtime.
--answers payloads. Use --schema to dump the input
schema of any verb and feed it to LLMs / form-generators / docs.
op commands in shell or CI without parsing free-form text.
op revisions warm
flips a state field; it doesn't launch a process. That's gated on A5 (lifecycle execution) and
Phase D (deployer env-packs). For "I want my bundle running", keep using gtc start on
the stable lane until A4 lands.
cargo binstall greentic-deployer-dev
# or, for the full gtc with `gtc op …` passthrough:
cargo binstall gtc-dev
greentic-deployer op --help
greentic-deployer op env --help
greentic-deployer op revisions --help
STORE=$(mktemp -d)
greentic-deployer op env create \
--store-root "$STORE" \
--answers <(echo '{"name":"sandbox","description":"a play env"}')
greentic-deployer op env list --store-root "$STORE"
greentic-deployer op env show --store-root "$STORE" --answers <(echo '{"name":"sandbox"}')
# success → stdout has {op, noun, result}
greentic-deployer op env list --store-root "$STORE" | jq .
# failure → stderr has {op, noun, error:{kind,message}}, exit non-zero
greentic-deployer op env show --store-root "$STORE" \
--answers <(echo '{"name":"does-not-exist"}') 2>&1 1>/dev/null | jq .
greentic-deployer op env-packs add --schema | jq .
greentic-deployer op traffic set --schema | jq .
gtc-dev, the same surface via passthroughgtc op --help # shells out to the operator binary
gtc op env list
gtc op revisions list sandbox
| Gate | What it adds | When you'll feel it |
|---|---|---|
A4 | auto-create local env on first setup | gtc setup just works on a clean machine |
A5 | real revision lifecycle execution | op revisions warm actually warms a runner |
A7 | audit-event log for every mutation | compliance / post-incident timelines |
A8 | remote EnvironmentStore + CAS | multi-operator setups stop clobbering each other |
A9 | env-pack registry + secrets backends | op secrets put/get/rotate hits real vaults |
A10 | wizards become env-scoped | same wizard, different secrets per env |
Phase B | multi-bundle envs, in-process traffic splitter | two revisions of one customer side-by-side |
Phase C | credentials channel, runtime config channel | secrets and config stop sharing one pipe |
Phase D | AWS + K8s env-packs ship | "deploy to AWS" without bespoke scripts |