sudoX · environments

From a connected cloud to a watched one.

Real accounts arrived — AWS and GCP — and with them the question every operations tool eventually meets: production and staging are not the same place, so nothing about them should be governed the same way. This is how sudoX names the parts of an estate, what changes once it has, and five of those changes as things to operate.

The model

Environment → connections → assignments

An environment

The name a team already uses for part of its estate — prod, staging — made into something policies can match on. It carries a kind and, optionally, its own agent mode. Names are lowercase letters, digits and hyphens, because they are matched by glob in policy rules.

A connection

A cloud account or a database, per organisation, its credential sealed server-side and tested before it is stored. Every connection belongs to an environment — or to none, and the console counts the unassigned ones in front of you, because "prod needs two approvers" is only true if nothing that is production sits outside it.

An assignment

A credential's role inside one environment: an approver in staging, a viewer in prod. It can only narrow the organisation role. No assignments means the organisation role applies everywhere — restriction is opted into. Try the check.

The environment is deliberately not part of a resource's rendered scope. Six functions render provider/account/region as a string and one of them is a finding's fingerprint; folding the environment in would make every existing finding look new the day it shipped. It is a separate clause everywhere it appears — on a plan step, an alert, a finding, an audit line — and a rule that names an environment does not match a thing that has none.

in the console Environments · Connections · Access (assignments)

Try it

A more specific setting narrows, never widens.

The deployment has a lifecycle mode. An environment can have its own. Here is the one rule for how they combine — and why it is the only rule that keeps the org-wide switch a working emergency brake.

effective mode for one environment

deployment

∧

environment: prod

=

effective in prod

shadow

the worker
the autonomous runner

in the console Settings (deployment mode) · Environments (per-environment mode) · pkg/agent/mode.go

Both of an operator's expectations hold at once. Naming an environment works: "the org runs detect, but production watches in shadow for its first weeks" is the narrower setting winning for the thing it names. And the org-wide switch is still a brake: dropping the deployment to training stops production too, even though production was armed independently. If an environment could widen, that lever would silently not be a lever — and it is the one an operator reaches for when something is going wrong.

The same word decides both halves. The worker reads it before raising anything; the autonomous runner reads it before changing anything. There is exactly one function that answers "may the platform act here", because two copies of that comparison is how they stop agreeing — and an operator who dialled production down to shadow, stopped being paged about it, and found it being repaired unattended anyway would be meeting the most dangerous split the product could have.

Try it

The gate, step by step.

Build a plan. Change a step's risk, its environment, whether it can be undone. The verdict is the policy's — with the two floors no rule reaches applied first.

approval policy

the plan needs

in the console Approval policy · Actions (the verdict on each plan) · pkg/approval

Pick "everything → autonomous" and make a step destructive. It still needs two people. Uncheck rollback on a write. It still needs one. Those are the floors: an admin can make almost anything autonomous, and these two are not reachable from the policy at all — which is why they are stated in the policy editor above the rules rather than below them. "Two" is counted by credential, not by display name, and the approver's role is checked in each environment the plan touches at the moment they approve.

Try it

What to watch, derived from what is there.

Discovery draws the estate. A reviewed catalog turns what it found into log sources and golden-signal metrics — one query per kind of resource, so it keeps covering the estate as it changes. Nothing is enabled by this; you review a list and turn things off. Choose what discovery found.

proposal

in the console Watching (the proposal, with on/off and "follow the catalog" per row) · pkg/derive

Two things a shorter list would not say. What was not proposed, and why — "no Cloud SQL found" tells you the read worked and the estate has none; an empty list tells you nothing and looks broken. And the asymmetry between clouds is deliberate: a Google project has one log stream that a filter narrows, so one source covers it and starts on. CloudWatch has a group per producer whose name is a convention nobody is obliged to follow — so the groups are read, never guessed, and every one is proposed off, because each enabled one is a billed call per tick.

Every metric name in the catalog was checked against the provider's own documentation. A model that invents a metric name produces a signal that returns nothing forever, and nothing about that is distinguishable from a healthy, idle service. A proposal the operator switched on that then fails to build — a credential that expired, a permission that was never granted — is shown as "on, but not running" with the error, not as on.

Try it

The first look, once.

Connecting a cloud used to do nothing visible: an operator had to find the estate page and click Discover, then the assessment page, then cost, then decide what to watch — four pages, in an order nobody tells you, before the platform had an opinion about the thing you just gave it the keys to. Now the moment a connection is stored, four phases run in the cheapest order, once per scope. Connect one.

on connection

1 · read

Discovery

2 · free

Derivation

3 · budget

Assessment

4 · billed

Cost baseline

in the console Connections (the wizard) · Environments → an environment's page (what we found) · Infrastructure · Cost

What to notice. The order is by cost. Discovery is read-only and everything depends on it. Derivation is free and turns the read into something that watches, so a new cloud is being watched before anything expensive is attempted. Assessment spends model budget, and when it cannot run it is skipped with a reason and recorded as not assessed yet — never as clean. The cost baseline is a billed call, but a cheap one, and a baseline taken the day a cloud was connected is the only number there will ever be from before sudoX touched anything. Thirty days, not seven.

Connect the same scope again and nothing runs: "once" means "there is no snapshot for it yet", which makes the first look idempotent against a reconcile that fires on every connection edit — a wizard saves, then tests, then the page refreshes — so it cannot double-spend. Re-running is a deliberate act on the estate page. And on GCP with no billing export named, cost is not measured; a bill that reads as free is worse than one that reads as unknown.

Try it

Where it is armed, it acts.

Approved plans the policy cleared for autonomy sit in a queue. Every minute a runner takes up to three, oldest first, and for each one asks the same questions in the same order: are all approvals in, does the policy still say autonomous now, and is the platform armed in every environment the plan touches. Arm environments and run a pass.

deployment · detect

in the console Actions · Audit (actor autonomous-runner, environment named, result success / failure / denied)

What to notice. With production in shadow, the oldest plan in the queue is skipped with the exact sentence the audit trail records: not armed in prod — it is in shadow. Arm production and it runs. The plan whose policy changed since it was approved — its rollback step was removed — is not run on the strength of an old yes; it is re-gated and goes back to pending, because the policy is evaluated at the moment of execution, not at the moment of approval. The plan waiting for a second approver is skipped: the runner never counts itself as one.

The dev plan runs and fails. It is recorded as failure, in the same audit line format as a success, with the step that timed out. It is not recorded as refused — a refusal is a policy saying no, a failure is the estate saying no, and a trail that spelled them the same would be answering "who changed this?" with a lie.

Where it lands

Everything downstream reads the environment.

Approval policy

"Prod needs two approvers" written as the word a team already uses, not an account id the next reader has to recognise. A rule naming an environment does not match a step with none — an unassigned connection is not secretly production.

Alert routing

"Staging goes to a channel, production pages on-call" as a rule rather than a naming convention nobody enforces. The environment travels from the signal source onto the alert, so it is also seen. Switch the rules.

The assessment sweep

Narrowed to an environment, and reporting by one: "prod 1, staging 3" instead of "4". Four findings in a sandbox and four in production are the same number and not the same morning.

The audit trail

Every unattended execution and scheduled verification is recorded with the environment named — "who changed this?" answered with "nobody, it was cleared to run" is only acceptable if the record says where.

The knowledge graph

The environment is a node, and everything a provider can act on is linked into it, so "what depends on this database" answers with which estate it is about. Click a database.

Postmortems

Lead with the environment. Read by people who were not on the call, "prod (aws/…)" tells them where in four characters, where an account id asks them to recognise a number.

← Who can do what Deploy →