DEV Community

Avery Lin
Avery Lin

Posted on

A Rollback Contract for Free-Model Server Changes: Record, Replay, and Require an Inverse

Why this is worth reading: you have probably seen a free model emit a confident-looking systemd unit or nginx config. The diff is small, the explanation sounds plausible, and the easiest next step is to copy it onto a server. The real risk is not that the model is wrong; it is that you cannot prove you can get back to the known-good state without guessing. This guide gives you a rollback contract: a small directory of evidence and an inverse script that must be tested before the change is allowed to stay.

When you use MonkeyCode's free model access to draft a server change, treat the output like an archive from a stranger, not like a patch from a colleague. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The free server option is useful here because a throwaway host lowers the cost of making mistakes, but the workflow below works on any Linux host with bash, find, sha256sum, and diff.

The contract is four files, not a prompt

Keep the following directory next to every model-generated server change:

server-change/
├── goal.md
├── before.tree
├── apply.sh
├── inverse.sh
├── canary_check.sh
└── after.tree
Enter fullscreen mode Exit fullscreen mode

goal.md is human-written and states why the change exists, which files may be touched, and which files may not be touched. apply.sh is the model-generated change. inverse.sh is human-written and must undo apply.sh. before.tree, after.tree, and the canary log are the evidence you attach to the change.

Step 1: Snapshot the file system before anything runs

Work on a copy or a disposable server rather than the live path. From the target directory, record content hashes and file metadata:

cd /srv/demo
find . -type f -print0 | sort -z | xargs -0 sha256sum > ../server-change/before.tree
find . -type f -print0 | sort -z | xargs -0 stat -c '%A %U:%G %s %n' > ../server-change/before.meta
Enter fullscreen mode Exit fullscreen mode

The content snapshot catches changed or deleted files; the metadata snapshot catches permission and ownership shifts that a plain diff often misses.

Step 2: Apply with a declared blast radius

Before running anything, write the allowed target list in goal.md. For example:

Goal: add a health-check endpoint to the demo service config.
Allowed changes:
- /srv/demo/config.yaml
Not allowed:
- any new file outside /srv/demo
- any change to systemd units or cron entries
- any package installation
Enter fullscreen mode Exit fullscreen mode

Then run the model-generated script and compare the snapshots:

bash apply.sh
find . -type f -print0 | sort -z | xargs -0 sha256sum > ../server-change/after.tree
diff -u ../server-change/before.tree ../server-change/after.tree
Enter fullscreen mode Exit fullscreen mode

Use the diff as a decision gate, not as a style check. Here is a compact table for what you find:

Diff result What it usually means Action
Only allowed file content changed The patch stayed inside the stated radius Continue
New unexplained file appears Script wrote a cache, temp file, or log Remove it and rerun
Permission or owner changed on unrelated file Script has side effects beyond editing Reject and sanitize
File deleted with no inverse rule Model assumed state you may not have Reject and rewrite

This table is the part that prevents you from accepting a small-looking change that actually touches more than you expected.

Step 3: Require the inverse to restore the exact tree

Do not let the model generate the inverse. Write inverse.sh by hand, using the before snapshot as the specification. It can be as simple as:

#!/usr/bin/env bash
set -euo pipefail
# Restore the known-good config from the local backup.
cp /srv/demo/backups/config.yaml.2026-08-17 /srv/demo/config.yaml
Enter fullscreen mode Exit fullscreen mode

Then test it immediately after applying the change:

bash inverse.sh
find . -type f -print0 | sort -z | xargs -0 sha256sum > ../server-change/restored.tree
diff -u ../server-change/before.tree ../server-change/restored.tree
Enter fullscreen mode Exit fullscreen mode

If that diff is empty, the inverse works. If it is not, fix the inverse by hand. Do not ask the free model to debug its own patch unless you have time to review the new output as another untrusted input.

Step 4: Prove health with a canary before and after rollback

A file tree can be restored while the service is still broken. Add a small canary script that checks the actual behavior:

#!/usr/bin/env bash
set -euo pipefail
if ! curl -fsS -m 5 http://127.0.0.1:8080/health; then
  echo "canary failed" >&2
  exit 1
fi
Enter fullscreen mode Exit fullscreen mode

Run the canary after apply.sh, and run it again after inverse.sh. If the canary passes after apply but the tree diff shows extra writes outside the allowed list, you can still reject the change without hurting the service. If the canary fails after apply, run the inverse, rerun the canary, and only then investigate.

Step 5: Merge with evidence, not with confidence

Attach before.tree, after.tree, restored.tree, and the canary output to the pull request or change ticket. A reviewer can now see not just what the model said, but what actually changed on disk and whether the undo path was proven. This is especially useful when a free model produces a plausible but context-poor change such as a firewall rule, an environment file, or a systemd drop-in.

Limitations you should accept up front

The rollback contract only catches file-system side effects. It does not catch a script that opens a listening socket, creates a systemd timer, changes an environment variable in a parent process, or mutates a database. If you need stricter isolation, run the apply step inside a container or use systemd-run with isolated namespaces. For databases, add a logical backup step such as pg_dump before the apply and verify restore separately.

The contract also does not tell you that a new config is semantically correct. A canary can pass while edge traffic later fails, so the canary should hit the actual health path and not only check that the process is running.

Who should not use this workflow

Skip this if you already keep the server under declarative configuration management and can reproduce the known state from source. Skip it also if you cannot spend time writing the inverse by hand; without a tested inverse, accepting a free-model server change is closer to gambling than merging. Finally, skip it for emergency hotfixes where speed is the constraint, because this process deliberately adds evidence-gathering overhead.

Start with one throwaway directory, generate a change through the free model access you already use, and require the inverse to pass before you consider the patch ready for review.

Top comments (0)