Skip to main content

EnvironmentMutations

Trait EnvironmentMutations 

Source
pub trait EnvironmentMutations: Send + Sync {
Show 32 methods // Required methods fn create_environment( &self, env_id: &EnvId, name: String, host_config: EnvironmentHostConfig, ) -> Result<Environment, StoreError>; fn update_environment( &self, env_id: &EnvId, patch: UpdateEnvironmentPayload, ) -> Result<Environment, StoreError>; fn load_environment( &self, env_id: &EnvId, ) -> Result<Environment, StoreError>; fn trust_root_is_seeded(&self, env_id: &EnvId) -> Result<bool, StoreError>; fn migrate_merge_bindings( &self, target_env_id: &EnvId, payload: MigrateMergePayload, ) -> Result<(Vec<String>, Vec<String>), StoreError>; fn stage_revision( &self, env_id: &EnvId, payload: StageRevisionPayload, idempotency_key: IdempotencyKey, ) -> Result<Revision, StoreError>; fn warm_revision( &self, env_id: &EnvId, payload: WarmRevisionPayload, idempotency_key: IdempotencyKey, ) -> Result<RevisionTransitionOutcome, StoreError>; fn drain_revision( &self, env_id: &EnvId, revision_id: RevisionId, idempotency_key: IdempotencyKey, ) -> Result<RevisionTransitionOutcome, StoreError>; fn archive_revision( &self, env_id: &EnvId, revision_id: RevisionId, idempotency_key: IdempotencyKey, ) -> Result<RevisionTransitionOutcome, StoreError>; fn add_bundle( &self, env_id: &EnvId, payload: AddBundlePayload, idempotency_key: IdempotencyKey, ) -> Result<BundleDeployment, StoreError>; fn update_bundle( &self, env_id: &EnvId, payload: UpdateBundlePayload, idempotency_key: IdempotencyKey, ) -> Result<BundleDeployment, StoreError>; fn remove_bundle( &self, env_id: &EnvId, deployment_id: DeploymentId, idempotency_key: IdempotencyKey, ) -> Result<RemoveBundleOutcome, StoreError>; fn add_pack_binding( &self, env_id: &EnvId, binding: EnvPackBinding, idempotency_key: IdempotencyKey, ) -> Result<EnvPackBinding, StoreError>; fn update_pack_binding( &self, env_id: &EnvId, slot: CapabilitySlot, binding: EnvPackBinding, idempotency_key: IdempotencyKey, ) -> Result<(EnvPackBinding, u64), StoreError>; fn remove_pack_binding( &self, env_id: &EnvId, slot: CapabilitySlot, idempotency_key: IdempotencyKey, ) -> Result<(EnvPackBinding, u64), StoreError>; fn rollback_pack_binding( &self, env_id: &EnvId, slot: CapabilitySlot, idempotency_key: IdempotencyKey, ) -> Result<(EnvPackBinding, u64), StoreError>; fn add_extension_binding( &self, env_id: &EnvId, binding: ExtensionBinding, idempotency_key: IdempotencyKey, ) -> Result<ExtensionBinding, StoreError>; fn update_extension_binding( &self, env_id: &EnvId, key: ExtensionKey, binding: ExtensionBinding, idempotency_key: IdempotencyKey, ) -> Result<(ExtensionBinding, u64), StoreError>; fn remove_extension_binding( &self, env_id: &EnvId, key: ExtensionKey, idempotency_key: IdempotencyKey, ) -> Result<(ExtensionBinding, u64), StoreError>; fn rollback_extension_binding( &self, env_id: &EnvId, key: ExtensionKey, idempotency_key: IdempotencyKey, ) -> Result<(ExtensionBinding, u64), StoreError>; fn set_traffic_split( &self, env_id: &EnvId, payload: SetTrafficSplitPayload, idempotency_key: IdempotencyKey, ) -> Result<ApplyTrafficSplitOutcome, StoreError>; fn rollback_traffic_split( &self, env_id: &EnvId, deployment_id: DeploymentId, idempotency_key: IdempotencyKey, ) -> Result<RollbackTrafficSplitOutcome, StoreError>; fn add_messaging_endpoint( &self, env_id: &EnvId, payload: AddMessagingEndpointPayload, idempotency_key: IdempotencyKey, ) -> Result<MessagingEndpoint, StoreError>; fn link_messaging_bundle( &self, env_id: &EnvId, endpoint_id: MessagingEndpointId, bundle_id: BundleId, updated_by: String, idempotency_key: IdempotencyKey, ) -> Result<MessagingEndpoint, StoreError>; fn unlink_messaging_bundle( &self, env_id: &EnvId, endpoint_id: MessagingEndpointId, bundle_id: BundleId, updated_by: String, idempotency_key: IdempotencyKey, ) -> Result<MessagingEndpoint, StoreError>; fn set_messaging_welcome_flow( &self, env_id: &EnvId, payload: SetMessagingWelcomeFlowPayload, idempotency_key: IdempotencyKey, ) -> Result<MessagingEndpoint, StoreError>; fn remove_messaging_endpoint( &self, env_id: &EnvId, endpoint_id: MessagingEndpointId, ) -> Result<MessagingEndpointId, StoreError>; fn rotate_messaging_webhook_secret( &self, env_id: &EnvId, endpoint_id: MessagingEndpointId, updated_by: String, idempotency_key: IdempotencyKey, ) -> Result<MessagingEndpoint, StoreError>; fn bootstrap_trust_root( &self, env_id: &EnvId, ) -> Result<TrustRootSeed, StoreError>; fn seed_trust_root_if_absent( &self, env_id: &EnvId, ) -> Result<Option<TrustRootSeed>, StoreError>; fn add_trusted_key( &self, env_id: &EnvId, key_id: String, public_key_pem: String, idempotency_key: IdempotencyKey, ) -> Result<TrustRootAddOutcome, StoreError>; fn remove_trusted_key( &self, env_id: &EnvId, key_id: String, idempotency_key: IdempotencyKey, ) -> Result<TrustRootRemoveOutcome, StoreError>;
}
Expand description

The typed-verb persistence operations a Greentic environment store performs in response to op … CLI verbs.

Replaces the closure-based LocalFsStore::transact pattern with one method per logical verb. All methods take &self — concurrency is the impl’s responsibility (flock for LocalFsStore, optimistic CAS via If-Match for HttpEnvironmentStore against an A8-compliant server).

Methods that need idempotency replay (per A8 §2) take an IdempotencyKey; methods that are intrinsically idempotent (e.g. seed_trust_root_if_absent) do not.

Errors: all methods return StoreError. Impls may map their transport-specific errors into the existing variants; new variants land alongside the impl that needs them.

Required Methods§

Source

fn create_environment( &self, env_id: &EnvId, name: String, host_config: EnvironmentHostConfig, ) -> Result<Environment, StoreError>

Create a fresh environment with empty bundles/revisions/packs. Rejects if the env already exists.

Source

fn update_environment( &self, env_id: &EnvId, patch: UpdateEnvironmentPayload, ) -> Result<Environment, StoreError>

Patch the named scalar fields on an existing environment. FieldUpdate::Keep fields are skipped, FieldUpdate::Set writes the new value, FieldUpdate::Clear resets optional fields to None. The full updated Environment is returned. Covers what was previously split across update_environment (no listen_addr), set_public_url (single field), and set_config (host-level fields including listen_addr). One verb, one HTTP endpoint, one impl body per backend.

Source

fn load_environment(&self, env_id: &EnvId) -> Result<Environment, StoreError>

Read the current environment state. This is the single READ verb on an otherwise mutation-only trait: the remote dispatch needs it to evaluate client-side preconditions — e.g. the warm health-gate’s expected_lifecycle, which the local closure-based path reads inline under its flock. Maps to GET /environments/{env_id} on the HTTP backend and delegates to EnvironmentStore::load on LocalFsStore.

Source

fn trust_root_is_seeded(&self, env_id: &EnvId) -> Result<bool, StoreError>

True when the env’s trust root holds at least one operator key. Maps to GET /environments/{env_id}/trust-root on the HTTP backend and to a trust-root file read on LocalFsStore. A read-only probe (like Self::load_environment) — remote env apply --check uses it to diff a declared trust_root instead of assuming it is seeded (the env document does not carry trust-root state).

Source

fn migrate_merge_bindings( &self, target_env_id: &EnvId, payload: MigrateMergePayload, ) -> Result<(Vec<String>, Vec<String>), StoreError>

Merge pack bindings and extension bindings into target_env_id. Skips slots / extension keys already bound in the target. Returns the list of newly-merged slots + extension key strings (in the form "<kind_path>::<instance_id>").

payload.seed_if_missing is the optional seed-on-create-target branch used by op env migrate-dev to migrate a legacy dev env into a fresh local target atomically — load, fallback-to-seed, merge, save all happen under one lock.

Source

fn stage_revision( &self, env_id: &EnvId, payload: StageRevisionPayload, idempotency_key: IdempotencyKey, ) -> Result<Revision, StoreError>

Stage a fresh revision under deployment_id. The caller supplies the pinned artifact pointers; LocalFsStore’s CLI helper resolves them from a local .gtbundle upstream of this call so the trait stays storage-only.

idempotency_key: A8 §2 — same-key replay returns the originally staged Revision without re-minting the ULID or advancing the sequence. Local impl accepts and ignores; HTTP backends cache it.

Source

fn warm_revision( &self, env_id: &EnvId, payload: WarmRevisionPayload, idempotency_key: IdempotencyKey, ) -> Result<RevisionTransitionOutcome, StoreError>

Transition a revision through its warm lifecycle chain, applying the client-evaluated health-gate outcome. The deployer CLI runs the runner-side health checks locally and ships the result in WarmRevisionPayload::health_gate: Ok(()) advances the revision to Ready; Err(failure) flips it to Failed atomically. This replaces the closure-based gate so the operation can cross the A8 HTTP wire contract.

Source

fn drain_revision( &self, env_id: &EnvId, revision_id: RevisionId, idempotency_key: IdempotencyKey, ) -> Result<RevisionTransitionOutcome, StoreError>

Drain a Ready revision (graceful step-down → Drained). idempotency_key is required for A8 mutation consistency even though drain is logically idempotent — the key enables audit-event replay.

Source

fn archive_revision( &self, env_id: &EnvId, revision_id: RevisionId, idempotency_key: IdempotencyKey, ) -> Result<RevisionTransitionOutcome, StoreError>

Archive a Drained / Failed revision (terminal). idempotency_key is required for A8 mutation consistency even though archive is logically idempotent — the key enables audit-event replay.

Source

fn add_bundle( &self, env_id: &EnvId, payload: AddBundlePayload, idempotency_key: IdempotencyKey, ) -> Result<BundleDeployment, StoreError>

idempotency_key is required for A8 §2 mutation replay; the local impl accepts and ignores, the HTTP backend caches the original outcome.

Source

fn update_bundle( &self, env_id: &EnvId, payload: UpdateBundlePayload, idempotency_key: IdempotencyKey, ) -> Result<BundleDeployment, StoreError>

idempotency_key is required for A8 §2 mutation replay; the local impl accepts and ignores, the HTTP backend caches the original outcome.

Source

fn remove_bundle( &self, env_id: &EnvId, deployment_id: DeploymentId, idempotency_key: IdempotencyKey, ) -> Result<RemoveBundleOutcome, StoreError>

Remove a bundle deployment from the env. Refuses if the deployment still has live traffic splits or non-archived revisions — callers must op traffic clear and archive revisions first.

Also drops archived revisions for the same deployment_id so the env stays compact. The pruned IDs are surfaced on the outcome so the destructive side effect is explicit on the contract — HTTP backends can apply a separate authorization check against the prune set, the CLI logs the IDs in the audit target.

idempotency_key is required for A8 §2 mutation replay; the local impl accepts and ignores, the HTTP backend caches the original outcome.

Source

fn add_pack_binding( &self, env_id: &EnvId, binding: EnvPackBinding, idempotency_key: IdempotencyKey, ) -> Result<EnvPackBinding, StoreError>

Source

fn update_pack_binding( &self, env_id: &EnvId, slot: CapabilitySlot, binding: EnvPackBinding, idempotency_key: IdempotencyKey, ) -> Result<(EnvPackBinding, u64), StoreError>

Returns (new_binding, new_generation) — the bumped audit generation is surfaced for downstream observability.

Source

fn remove_pack_binding( &self, env_id: &EnvId, slot: CapabilitySlot, idempotency_key: IdempotencyKey, ) -> Result<(EnvPackBinding, u64), StoreError>

Source

fn rollback_pack_binding( &self, env_id: &EnvId, slot: CapabilitySlot, idempotency_key: IdempotencyKey, ) -> Result<(EnvPackBinding, u64), StoreError>

Source

fn add_extension_binding( &self, env_id: &EnvId, binding: ExtensionBinding, idempotency_key: IdempotencyKey, ) -> Result<ExtensionBinding, StoreError>

Source

fn update_extension_binding( &self, env_id: &EnvId, key: ExtensionKey, binding: ExtensionBinding, idempotency_key: IdempotencyKey, ) -> Result<(ExtensionBinding, u64), StoreError>

Source

fn remove_extension_binding( &self, env_id: &EnvId, key: ExtensionKey, idempotency_key: IdempotencyKey, ) -> Result<(ExtensionBinding, u64), StoreError>

Source

fn rollback_extension_binding( &self, env_id: &EnvId, key: ExtensionKey, idempotency_key: IdempotencyKey, ) -> Result<(ExtensionBinding, u64), StoreError>

Source

fn set_traffic_split( &self, env_id: &EnvId, payload: SetTrafficSplitPayload, idempotency_key: IdempotencyKey, ) -> Result<ApplyTrafficSplitOutcome, StoreError>

Source

fn rollback_traffic_split( &self, env_id: &EnvId, deployment_id: DeploymentId, idempotency_key: IdempotencyKey, ) -> Result<RollbackTrafficSplitOutcome, StoreError>

Source

fn add_messaging_endpoint( &self, env_id: &EnvId, payload: AddMessagingEndpointPayload, idempotency_key: IdempotencyKey, ) -> Result<MessagingEndpoint, StoreError>

idempotency_key is both A8 §2 transport metadata AND domain state in this group: the key is embedded in the endpoint’s updated_by stamp and drives same-key replay detection (see greentic_deploy_spec::engine::messaging).

Source

fn set_messaging_welcome_flow( &self, env_id: &EnvId, payload: SetMessagingWelcomeFlowPayload, idempotency_key: IdempotencyKey, ) -> Result<MessagingEndpoint, StoreError>

idempotency_key semantics as on Self::add_messaging_endpoint — transport metadata that doubles as the replay-detection stamp.

Source

fn remove_messaging_endpoint( &self, env_id: &EnvId, endpoint_id: MessagingEndpointId, ) -> Result<MessagingEndpointId, StoreError>

Source

fn rotate_messaging_webhook_secret( &self, env_id: &EnvId, endpoint_id: MessagingEndpointId, updated_by: String, idempotency_key: IdempotencyKey, ) -> Result<MessagingEndpoint, StoreError>

Source

fn bootstrap_trust_root( &self, env_id: &EnvId, ) -> Result<TrustRootSeed, StoreError>

Load (or generate) the backend’s operator signing key and add it to the env trust root. Idempotent re-grant: a second call with the same operator key is a no-op on the key set (case-insensitive key_id dedup), and it never rejects on an existing trust root.

Source

fn seed_trust_root_if_absent( &self, env_id: &EnvId, ) -> Result<Option<TrustRootSeed>, StoreError>

Idempotent variant called from op env init: returns Some(seed) when a key was minted, None when a trust root already existed.

Source

fn add_trusted_key( &self, env_id: &EnvId, key_id: String, public_key_pem: String, idempotency_key: IdempotencyKey, ) -> Result<TrustRootAddOutcome, StoreError>

idempotency_key is required for A8 mutation consistency even though add_trusted_key is intrinsically idempotent on key_id collision — the key enables HTTP-backend audit-event replay. Local-FS impls accept and ignore it; HTTP impls cache it for replay.

Source

fn remove_trusted_key( &self, env_id: &EnvId, key_id: String, idempotency_key: IdempotencyKey, ) -> Result<TrustRootRemoveOutcome, StoreError>

idempotency_key is required for A8 mutation consistency. remove is logically idempotent on the trust-root state, but the wire-shape removed_public_key_pem field would be null on retry (the original PEM is gone) — so a retry without replay loses recovery material AND audit fidelity. The key enables HTTP-backend replay of the original response/audit; local-FS impls accept and ignore it.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§