I do not send a prompt to a free agent server until that box is empty of my credentials. Free model access is a convenience. It is not a reason to upload your real repo, your SSH agent, or your shell history.
Sound harsh? Maybe. Have you ever pointed a coding agent at a checkout and only later noticed SSH_AUTH_SOCK was still set? I refuse to start a remote job while that variable is present.
This tutorial is the replacement. You build a tiny public fixture, scrub the environment, hash the upload, and only then let a remote job see those files. You can run every check with git, bash, and python3. I am not claiming a benchmark, and I am not asking you to trust a number I did not re-read today.
A lot of agent demos this season keep the model inside a browser tab. Fine idea. The failure I care about is the other direction: a free server job that still inherits the laptop. If the demo never shows the file list, ask yourself what else it forwarded.
What you will have when this works
You will have a throwaway git repo, a test that fails on purpose, a scrub script, and a two-file manifest. You will also have a rule for the patch that comes back: apply it only after your laptop reruns the test. If any check fails, you stop. No partial upload just to see what the model says.
Step 1 — Make a workspace that is not your real repo
Create a directory that has never held a private remote. Do it outside every client folder you actually ship.
mkdir -p "$HOME/agent-fixtures/blank-box-01"
cd "$HOME/agent-fixtures/blank-box-01"
git init -b main
git config user.email "fixture@example.invalid"
git config user.name "Fixture Only"
Why a fake identity? Because a global user.email from your employer does not belong in a throwaway experiment. Is your shell still proud of that work address? Keep it out of this repo.
Verify:
git rev-parse --is-inside-work-tree
test "$(git config --get user.email)" = "fixture@example.invalid"
echo "step1 ok"
You want true, then step1 ok. If the email check fails, fix the local config in this repo only. Do not edit the global gitconfig while you are here.
Step 2 — Add one failing test and nothing else
The agent needs a job small enough that you can read the entire diff. One function. One test. No dotenv just for later.
cat > add.py << 'PY'
def add(a, b):
return a - b # wrong on purpose
PY
cat > test_add.py << 'PY'
from add import add
def test_add():
assert add(2, 3) == 5
PY
git add add.py test_add.py
git commit -m "fixture: add() is wrong on purpose"
Verify the test fails for the reason you planted:
python3 -m py_compile add.py test_add.py
python3 -c "from test_add import test_add; test_add()"
echo "step2_exit=$?"
You want a non-zero step2_exit. A passing test means the fixture is already fixed, and you have nothing to judge. Did the assertion mention 5? Good. That is the only bug you are allowed to upload.
Step 3 — Scrub the environment you would otherwise forward
Remote jobs inherit whatever you forward. I treat a secret-shaped variable as a failed preflight, not as a warning you click through.
cat > scrub_env.sh << 'SH'
#!/usr/bin/env bash
set -euo pipefail
deny_re='(TOKEN|SECRET|KEY|PASS|AUTH|COOKIE|SESSION|CREDENTIAL)'
fail=0
while IFS='=' read -r name _; do
if [[ "$name" =~ $deny_re ]]; then
echo "refusing: $name is set" >&2
fail=1
fi
done < <(env)
if [[ -n "${SSH_AUTH_SOCK:-}" ]]; then
echo "refusing: SSH_AUTH_SOCK is set" >&2
fail=1
fi
if [[ -f "$HOME/.netrc" ]]; then
echo "warning: ~/.netrc exists; do not mount $HOME" >&2
fi
exit "$fail"
SH
chmod +x scrub_env.sh
Verify the script can pass in a clean environment, then verify your real shell:
env -u SSH_AUTH_SOCK ./scrub_env.sh
echo "clean_scrub_exit=$?"
./scrub_env.sh
echo "real_scrub_exit=$?"
Clean must be 0. Real must also be 0 before you continue. If it refuses, unset those names in this terminal or open a new one. Would you paste that value into a stranger's form? Then it does not board the job.
Step 4 — Hash the upload set
The server should receive two source files and a manifest. Not another project's .git/config. Not your shell history. Not the scrub script, unless you decide the script itself is the lesson.
python3 - << 'PY'
import hashlib, pathlib
allow = ["add.py", "test_add.py"]
lines = []
for name in allow:
digest = hashlib.sha256(pathlib.Path(name).read_bytes()).hexdigest()
lines.append(f"{digest} {name}")
manifest = "\n".join(lines) + "\n"
pathlib.Path("UPLOAD_MANIFEST.sha256").write_text(manifest)
print(manifest, end="")
PY
Verify the names, then recompute the hashes:
python3 - << 'PY'
import hashlib, pathlib
names = []
for line in pathlib.Path("UPLOAD_MANIFEST.sha256").read_text().splitlines():
digest, name = line.split(" ", 1)
names.append(name)
got = hashlib.sha256(pathlib.Path(name).read_bytes()).hexdigest()
if got != digest:
raise SystemExit(f"mismatch: {name}")
if names != ["add.py", "test_add.py"]:
raise SystemExit(f"unexpected upload set: {names}")
print("step4 ok")
PY
If this prints anything other than step4 ok, you added a file you did not mean to upload. Delete it and hash again. A manifest you do not re-check is just a diary.
Step 5 — Hand two files over, not your home directory
Copy add.py, test_add.py, and the manifest into the client. Do not copy the parent directory. Do not enable credential forwarding. Do not paste ~/.ssh.
Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode is the open-source project I am using as the handoff target, and only for two operator-stated options: free model access, and a free server. I will not name a model, print a token quota, describe a machine size, or promise how long either option lasts. Those details move. A tutorial that freezes them becomes a stale claim by next week. If the docs page you open today does not list the free options, stop. Do not fish an old screenshot out of a chat log.
I am also not inventing a CLI flag. Use the invocation printed in the current docs, and only that. A stale flag is worse than no flag, because it fails in a way that tempts you to upload the whole folder. Remove the product name and this preflight still applies to any other remote agent host. That is intentional.
Before you submit, the client has to answer three questions. Does the file list match the manifest? Is every secret field empty? Does the prompt stay inside this fixture? You need yes, yes, and yes. A single no means you close the tab.
A prompt I would actually send:
Fix add() so test_add.py passes.
Touch only add.py.
Do not add dependencies.
Return the full file, not a summary.
When something comes back, save it beside the repo and look at the diff before you trust it:
# Save the returned file as /tmp/add.py.returned first.
diff -u add.py /tmp/add.py.returned || true
Boring means one operator change, subtraction to addition, and no new import. Anything else is a failed job, even if a test on the server would have passed. Why would a one-line fix add a network call? It would not. Discard it whether the cause is confusion or something worse.
Step 6 — Apply nothing until your own test passes
cp add.py add.py.bak
cp /tmp/add.py.returned add.py
python3 -c "from test_add import test_add; test_add()"
echo "local_test_exit=$?"
You want local_test_exit=0. Then decide, yourself, whether to keep the file. I do not auto-commit agent output. A green test on a fixture this small is necessary. It is not a review of a real service, and it is not permission to point the same session at your monorepo.
Step 7 — Discard the remote side
Delete the remote workspace in the product UI if that control exists. Locally, put the fixture back unless you created your own branch on purpose.
mv add.py.bak add.py
rm -f /tmp/add.py.returned
git status --short
git diff --stat
Verify you are back to the committed failing fixture, or that the only new commit is one you made. The free server should hold no copy you still need. If the only copy of the work lives on hardware you do not control, you skipped a step. Start over from the manifest, not from memory.
Read this table before you click submit
| Check | Pass | Stop |
|---|---|---|
| Git identity | fixture email only | work email in this repo |
| Test before the job | fails on purpose | already green, or import crash |
| Env scrub | exit 0 | TOKEN, KEY, or SSH_AUTH_SOCK set |
| Manifest | exactly two files | extra path, or hash mismatch |
| Returned diff | only the operator in add() | new files, network, or a rewritten test |
| Local retest | exit 0 on your machine | it passed on the server |
Print the table. Check a row. Then check the next. Skipping a row because the editor feels friendly is how a laptop identity ends up on a box you do not administer.
Limits, and who should skip this
This does not make a shared server trustworthy. It makes your upload boring enough that a leak is the fixture, not your customer data. A host can still log prompts. A free tier can shrink, vanish, or swap models without telling your script. I am not saying the box is private. I am saying you proved you did not hand it anything private.
Skip this if the repo is a private monorepo, if any production credential is in scope, or if you need the vendor to attest isolation. A bash preflight is not a contract, a residency guarantee, or a security review. Pay for the agreement that actually says those words, and have counsel read it.
Also skip it if you wanted a leaderboard. This page has no timing, no token count, and no model name. I will not freeze a figure I did not copy from a primary page on the day you run the commands.
If you try the handoff, confirm free model access and the free server in the current MonkeyCode docs, then run the same seven checks. The docs are the quota. This page is only the preflight.
Top comments (0)