# AI agent configuration edits: safe remote change controls

A remote configuration edit is an operational change, even when the agent alters one line. The file may control who can reach a service, which identity it accepts, where requests go, or whether the operator can still get back into the host. Treating such work as ordinary text generation creates outages that are tedious to diagnose and, in the worst cases, quietly expands access.

The safe pattern is plain: preserve the current state before writing, validate the exact candidate, apply it in a recoverable way, prove the intended behavior, and put a person in the path when the edit changes network access. An agent can do much of the mechanical work. It should not decide on its own that a new port exposure, CIDR range, or administrative route is acceptable.

## A remote edit needs transaction boundaries

A configuration change needs a beginning, a point of commitment, and a defined rollback path. Shell access alone supplies none of those. An agent that receives a request to "open the service to the build network" can search files, change an allow list, and reload a daemon. If the request was ambiguous, if the file contains generated sections, or if another deployment writes the same file minutes later, the action has already escaped the requester's intent.

Define the unit of change before you authorize it. For a reverse proxy, that may be one virtual-host file plus any included access-control file. For SSH, it may be the primary daemon configuration and a directory of snippets. For a cloud firewall, it may be a rule set represented by an API resource rather than a file. A backup of one file does little when the live behavior depends on five related files.

The sequence should have explicit states:

1. Read and fingerprint the active configuration and its related inputs.
2. Build a candidate outside the live path and produce a readable diff.
3. Run native parsing and focused checks against that candidate.
4. Take an independent rollback copy immediately before the commit.
5. Install, reload or apply, then run a behavioral probe.

A failed check must stop the sequence before the commit. Do not let an agent "fix forward" by trying variants against a production service. That approach turns a controlled operation into a series of unreviewed experiments, and it also destroys the evidence needed to understand the first failure.

Separate preparation from authority. An agent can draft a proposed patch and assemble test commands without permission to alter the host. A narrow executor can perform only an approved change shape. The executor should reject arbitrary shell fragments, arbitrary destination paths, and commands that do not belong to the declared service. This feels restrictive until someone asks an agent to repair an unrelated symptom and it edits the first plausible file it finds.

## A backup must restore the state that actually ran

A backup works only if it captures the active bytes before the change and survives the failure that prompts rollback. Copying a proposed file into a nearby `.bak` file is weak protection. A later command can overwrite it, cleanup can remove it, and a confused rollback can restore a file that was never live.

Create the rollback copy immediately before the commit, after validation but before installation. Preserve permissions, ownership, timestamps when useful for diagnosis, and extended attributes where the operating system uses them. Put it in a location that the service does not load through wildcard includes. For an access-control service, include all files that contribute rules, not only the file an agent happened to edit.

Record a content hash alongside the copy. The hash has a practical purpose: during an incident, it tells an operator whether the backup is the pre-change version they intended to restore. It also catches a surprising number of mistakes where two operators believe they are discussing the same revision.

For example, a backup record for an Nginx configuration can contain a timestamped copy and a SHA-256 digest:

```sh
backup_dir=/var/backups/config-changes/nginx
stamp=$(date -u +%Y%m%dT%H%M%SZ)
mkdir -p "$backup_dir"
cp -a /etc/nginx/nginx.conf "$backup_dir/nginx.conf.$stamp"
sha256sum /etc/nginx/nginx.conf > "$backup_dir/nginx.conf.$stamp.sha256"
```

This command protects only the main file. If `nginx.conf` includes `/etc/nginx/conf.d/*.conf`, the change record needs copies of the included files involved in the edit as well. The right scope comes from the service's include graph, not from the filename in an agent prompt.

Version control is useful but it is not a rollback mechanism by itself. A repository can tell you what someone intended to deploy. It cannot restore a manually changed certificate permission, a generated include, or a cloud-side object whose current state differs from the last commit. Keep both: versioned desired configuration and an operational recovery copy of the state immediately before application.

Also test restoration. Pick a disposable host, restore a backup using the documented command, validate it, and reload the service. Teams often discover at that point that their backup account cannot write the destination, that the service reads a different include directory, or that a deployment agent overwrites the restored file. Those are not documentation defects. They are rollback defects.

## Parser success is necessary and insufficient

A parser check catches malformed syntax, missing directives, and many unsafe file references. It does not establish that the resulting service behaves as the request requires. People routinely blur those claims, then describe a bad deployment as "validated" because a command returned zero.

Nginx documents `nginx -t` as a test of configuration syntax and an attempt to open files referenced by that configuration. Its familiar output has this shape:

```text
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
```

That is a worthwhile gate. It can catch a misplaced semicolon, an unreadable certificate, or a bad include path before a reload. It cannot tell you whether an upstream target answers requests, whether a location block now bypasses authentication, or whether a newly added host name resolves as intended.

Use the native command for the component you actually changed. OpenSSH provides `sshd -t` to check configuration validity before launching or reloading the daemon. The sudo manual provides `visudo -c` for syntax checking sudoers files. systemd provides `systemd-analyze verify` for unit files, which reports parse and dependency issues. Kubernetes API documentation describes server-side dry-run as a request that passes admission and validation without persistence. Each of these is narrower than a full production test, but each is far better than asking an agent to infer validity from the file's appearance.

Run the check against the candidate, not after overwriting the active file. Some programs make this easy with an explicit configuration path. Others require a staging directory, a container, or a disposable namespace. If a service cannot parse an alternate candidate, build a test environment that mirrors its include paths. Do not accept "we can only validate after install" as a permanent design choice for a service that controls access.

Validation also needs the same account and file visibility as the real process. A command run as an administrator may read a private certificate that the service account cannot read. A local test may resolve a name through a different resolver path than the daemon. Capture the command, user context, and output in the change record so an operator can reproduce the result instead of trusting an agent's paraphrase.

## Reloads need their own safety checks

A successful parse does not guarantee a successful reload. Reload mechanisms vary: some daemons retain the old process when the new process fails, some replace workers gradually, and some accept a reload signal but apply only part of a changed setting to existing connections. A restart has a different failure profile again, because it may terminate every connection before discovering a problem.

Use reload when the service documents it as the intended way to apply configuration and when existing sessions need to survive. Then check the service manager result, inspect the service's own error output, and make a targeted request through the same network path a real client uses. A process that remains "active" can still reject all traffic due to a bad listener, an upstream issue, or an authorization rule.

A focused probe should be tied to the change. If the edit adds a protected path for an internal subnet, test one allowed request and one denied request from appropriate test locations. If the edit changes a backend endpoint, request a known health route and verify the expected status and response marker. If the edit changes an SSH allow group, use a nonprivileged test account rather than the operator's break-glass account.

Avoid a broad `curl` from localhost as the only proof. Localhost can bypass the firewall, DNS path, proxy, TLS name check, and routing path that the change actually affects. The test should travel through the boundary under review. Keep it narrow enough that it does not mutate data or trigger expensive jobs.

For changes that risk cutting off administration, retain the current management session until the new route works. A better setup also schedules an automatic rollback after a short period and cancels it only after the operator confirms reachability. Network equipment often calls this a confirmed commit. The idea applies to hosts and cloud rules too: the system should recover even if the person or agent loses the connection at the worst moment.

An agent should never use the final confirmation as a substitute for a probe. A command returning "reload sent" describes a signal delivery, not the state of the running service. Those are different events, and incident reports get muddled when logs fail to distinguish them.

## Network access changes deserve a separate approval gate

Any edit that changes who can reach a service, which interface accepts traffic, or how traffic reaches a destination requires review before application. This includes firewall rules, cloud security groups, route tables, DNS records for administrative names, proxy access controls, listener addresses, load balancer settings, and SSH `AllowUsers`, `AllowGroups`, or authentication rules.

The reason is blast radius, not file type. A one-line application setting can cause harm, but a one-line CIDR change can expose an internal control plane or remove the only route to a host. A reviewer needs to judge the intended audience, the source network, the destination, the protocol, and the recovery path. A generic request such as "allow the deployer" does not answer those questions.

Require a change request to state the access intent in terms a reviewer can check:

- source identity or network range, with a reason for each range
- destination host or service, listener, and protocol
- whether the rule grants inbound, outbound, or transit access
- planned duration, if the access is temporary
- the test and rollback method, including the current management path

The review should inspect a rendered diff, not merely an agent's natural-language description. Generated firewall systems can expand a friendly rule into several effective rules. Proxy templates can inherit broad defaults that the proposed patch does not show. Cloud APIs can normalize or reorder rules, so retrieve the resulting effective object after application and compare it to the approved intent.

Do not require human approval for every harmless formatting change. That creates approval fatigue and trains reviewers to click through cards they cannot meaningfully evaluate. Classify changes by their effects. A comment update or a timeout adjustment may pass after automated checks. An edit that adds `0.0.0.0/0`, changes a bind address from loopback to all interfaces, removes a deny rule, or broadens an identity match should stop for explicit review.

The classification must inspect the resulting behavior, not only search for suspicious strings. A configuration generator might turn a symbolic group into a broad CIDR. A DNS change can send traffic to a different network without touching a firewall file. An agent can help identify these effects, but the executor should use a fixed detector or require review whenever it cannot determine the effect with confidence.

## Give agents narrow actions instead of privileged terminals

A general administrator shell turns every configuration task into an open-ended authority grant. The agent can read unrelated files, alter its own logging path, erase backups, or run a command that was never part of the approved repair. Prompt instructions do not constrain a process in the way an operating-system permission boundary does.

Give the agent a small set of actions with fixed inputs. One action may accept a candidate Nginx file for a named virtual host, run its required validation, write the backup, and reload only that service. Another may submit a firewall rule change to a staging environment and return the rendered diff. The action should reject paths outside its service directory and should not accept a free-form shell command field.

This is more work than placing an agent in the `sudoers` file with broad privileges. It saves work later because the failure modes become legible. When a change fails, you know which action ran, what it touched, and which check rejected it. When an investigator reads the record, they do not have to reconstruct intent from a long terminal transcript full of exploratory commands.

Keep credentials away from the agent process as well. An agent that holds an SSH private key or cloud token can sidestep your change runner and talk directly to the target. The runner should hold the authority and expose only the needed operation. If a compromise reaches the agent, the attacker then faces the runner's input checks, review gates, and audit trail rather than a reusable credential.

This is where an action gateway is more useful than a proxy. Sallyport lets an MCP-capable agent request SSH and HTTP actions without receiving the stored API or SSH credentials, while its vault gate and authorization controls can require a human decision before the action proceeds. That does not replace service-specific validation or a review of access changes; it keeps the agent from carrying the credential that would bypass those controls.

## A change runner can enforce the order people forget

A small runner should make the safe order unavoidable. The following example illustrates an Nginx-specific operation. It accepts a candidate main configuration already produced by a controlled workflow, preserves the live file, tests the candidate, installs it, tests the installed state once more, reloads, and probes a named HTTPS endpoint.

```sh
#!/usr/bin/env bash
set -euo pipefail

candidate=$1
probe_url=$2
live=/etc/nginx/nginx.conf
backup_dir=/var/backups/config-changes/nginx
stamp=$(date -u +%Y%m%dT%H%M%SZ)

[ -f "$candidate" ] || { echo "candidate missing" >&2; exit 2; }
install -d -m 0700 "$backup_dir"

nginx -t -c "$candidate"
cp -a "$live" "$backup_dir/nginx.conf.$stamp"
sha256sum "$live" > "$backup_dir/nginx.conf.$stamp.sha256"

install -m 0644 "$candidate" "$live"
if ! nginx -t; then
  cp -a "$backup_dir/nginx.conf.$stamp" "$live"
  nginx -t
  nginx -s reload
  echo "candidate rejected, prior configuration restored" >&2
  exit 1
fi

nginx -s reload
curl --fail --silent --show-error --max-time 10 "$probe_url" > /dev/null
printf 'applied=%s backup=%s\n' "$stamp" "$backup_dir/nginx.conf.$stamp"
```

Do not copy this blindly into a production host. Nginx deployments differ in include structure, file mode, service manager integration, and reload command. The example's useful property is the order, not the literal paths. It also exposes an important limitation: if the reload succeeds but the HTTP probe fails, this script exits without restoration. Some teams want automatic restoration in that case; others require a human to inspect traffic before reversal. Choose that policy deliberately and document it.

The runner should write structured records as it goes. Record the requested change identifier, actor, target, candidate hash, active hash before commit, backup path, diff hash, validation output, reload result, and probe result. Do not permit the same process that writes those records to quietly rewrite history. A log that an administrator or agent can alter during a failed change does not settle a dispute later.

Keep secret material out of diffs and logs. Configuration files often contain tokens, private paths, or embedded credentials despite every policy saying they should not. Redact known secret fields before displaying a diff, and reject candidates that introduce plaintext secrets where your configuration format permits detection. Redaction must not change the candidate fed to validation; it only changes the human-facing record.

## Behavioral tests must cross the boundary you changed

A configuration test needs an assertion about behavior, not merely a process check. Choose the smallest test that proves the requested result and run it from a location that experiences the relevant policy. For a firewall opening, use a controlled host in the allowed network and another in a denied network. For DNS, query the resolvers clients use and then make a request using the expected host name. For a proxy ACL, test the route with the intended identity and with an identity that should fail.

Make expected outcomes explicit. "Probe succeeded" is too weak for an access-control change. A record such as "source A received HTTP 200 from `/healthz`; source B received HTTP 403 from `/admin`" lets a reviewer assess whether the rule behaves as intended. If the requirement is that source B cannot connect at all, measure connection failure rather than treating an application-level denial as equivalent.

Test negative cases with care. A denied request should target a harmless endpoint and a dedicated test identity. Do not use a production administrator account to prove that a block works, and do not test a rate-limit rule by flooding a shared service. Agent automation tends to repeat commands; a poorly chosen test can become its own incident.

Make the probe idempotent. Reads, health endpoints, TLS handshakes, and authentication attempts against a dedicated test account are good candidates. A request that creates a user, sends email, charges a card, or starts a deployment is not a validation probe. If the service has no safe endpoint, build one before giving an agent authority to reload it.

## Audit records must connect intent to the final state

A terminal transcript answers only part of the question after a bad change. You also need to know which agent process initiated it, who approved any access expansion, which candidate was reviewed, what code path executed the command, and whether the resulting live state matched the candidate. Without that chain, teams end up with a commit, a vague chat message, and a host whose behavior nobody can explain.

Record agent sessions separately from individual actions. A session record identifies the process and its lifetime. An action record identifies each request within that session, including inputs, approval, result, and relevant hashes. That distinction matters when an agent makes ten harmless reads and one access-changing write. Revoking the session stops later actions, but it does not erase the evidence of the action already performed.

Make the audit log tamper-evident and verify it independently. A hash chain provides a simple check: each entry includes the digest of its predecessor, so removing or rewriting an entry breaks later verification. Sallyport projects its session and activity journals from an encrypted hash-chained audit log, and `sp audit verify` can verify that chain offline over ciphertext without a vault key. Keep verification output with incident records when you rely on that evidence.

Logs do not make a risky process safe. They let you establish what happened after controls fail, and they discourage casual shortcuts because the action remains attributable. The prevention work is still the same: narrow authority, a real rollback copy, validation before commit, targeted proof after reload, and human review when an edit changes access. If your current process cannot state who approved a new network path and how to undo it, do not delegate that process to an agent yet.
