What Leaves the Laptop: A Prompt-Payload Glossary, a Four-Leaf Tree, and a Worked Example at Every Leaf
A checkout service pages at 02:14. The on-call engineer pastes the traceback, a slice of application.yml, and the last twenty request logs into a coding agent on a free remote box. Twelve minutes later the model returns
A checkout service pages at 02:14. The on-call engineer pastes the traceback, a slice of application.yml, and the last twenty request logs into a coding agent on a free remote box. Twelve minutes later the model returns a plausible patch. It also restates the database password from that YAML file in a comment "for clarity."
The failure was not model quality. It was a missing payload class. Free remote models and free remote servers make the same failure cheap to repeat, because the loop is easy and the data path is easy to ignore.
This article is a glossary, a four-leaf tree, and a worked example at every leaf. The artifact is a small classifier you can run before any agent session. Remove every product name and the workflow still holds.
Glossary
Use these terms as written. Do not collapse them into a single word like "sensitive."
- Payload. The union of prompt text, attached files, tool-visible workspace paths, and retrieved snippets a remote model can read.
- Secret-bearing. Material that confers access: passwords, API tokens, private keys, session cookies, connection strings, signing secrets.
- Restricted record. Material you are not free to ship off-laptop even after secrets are stripped: customer PII, health or finance identifiers, unreleased IP, production traffic that identifies a person.
- Internal-nonsensitive. Private source or logs with no secrets and no restricted records, but not public.
- Public reproduction. An already-public bug report, open-source file, or synthetic fixture you would post on a public issue tracker.
- Remote free loop. Any coding agent whose model, disk, or logs live on a machine you do not control, including free-model and free-server options.
- Stand-in. A minimized fake dataset that still fails the same test as production.
- Egress decision. The explicit choice to send a payload off-laptop, keep it local, or refuse the session.
A patch can be public-grade while its payload is secret-bearing. Classify the payload, not the intention.
The tree
Walk the questions in order. Stop at the first yes.
- Does the payload contain secret-bearing material, or will the agent be able to
open()a file that does? - If not: does the payload contain restricted records?
- If not: is the material internal-nonsensitive?
- If not: is it a public reproduction?
The leaves are refuse-or-redact, stand-in only, bounded remote, and unrestricted public.
Session procedure
Apply the tree as a gate, not as a retrospective.
- Copy only the repo slice you believe the task needs.
- Run the search command and
payload_class.pybelow against that slice. - Name the leaf in the chat title or the PR body before the first prompt.
- Start the remote loop, keep it local, or refuse.
- Scan the resulting diff with the same secret regex the classifier uses.
If step 2 and step 3 disagree, step 2 wins. The claimed leaf is a hypothesis. The scan is the evidence.
Leaf 1 — Refuse or redact (secret-bearing)
Worked example. A Flask app fails to boot. The engineer wants to attach .env and config.py so the model can "see the real config."
# .env — do not send, do not mount
DATABASE_URL=postgres://app:[email protected]:5432/app
STRIPE_SECRET_KEY=sk_test_example_not_real
Command before any prompt:
rg -n -i -g '!node_modules' -g '!.git' \
'BEGIN .*PRIVATE KEY|AKIA[0-9A-Z]{16}|sk_live_|sk_test_|password\s*=|SECRET|api[_-]?key' .
If that search hits, the egress decision is no remote loop until the hits are removed from the workspace the agent can see. Redaction is not deleting a line in the prompt while the file remains on a mounted free server. Move secrets to a local, unmounted path. Inject them at runtime from an env manager the agent cannot list.
Independent check: the classifier in the artifact section must return secret_bearing. A human still reviews false negatives. Regex is a gate, not a proof.
Leaf 2 — Stand-in only (restricted records)
Worked example. Support pastes a ticket: customer email, order id, last four of a card, and a stack trace. The stack trace is useful. The rest is not.
Build a stand-in that preserves the failing shape:
# tests/test_order_status_standin.py
from order_status import parse_event
def test_missing_shipment_id_returns_409():
event = {
"email": "[email protected]",
"order_id": "ord_000000",
"card_last4": "0000",
"shipment_id": None,
}
status, body = parse_event(event)
assert status == 409
assert body["code"] == "shipment_missing"
Numbered steps for this leaf:
- Copy the failing control flow, not the person.
- Replace identifiers with documented fakes in the
example.testdomain. - Confirm the stand-in fails the same assertion the production path fails.
- Send only the stand-in, the test, and the relevant source. Leave the ticket out of the payload.
If you cannot rebuild the failure without restricted records, do not use a remote free loop. Keep the session on a machine under the same data policy as production logs.
Leaf 3 — Bounded remote (internal-nonsensitive)
Worked example. An internal rate limiter double-counts a burst of 100 requests in 800ms. Logs are already scrubbed. No customer identifiers. Source is proprietary but not a secret.
A bounded remote session is allowed only with three constraints:
-
Workspace allowlist. Mount the limiter package and its unit tests. Do not mount
infra/,deploy/, or homedir dotfiles. - Retention assumption. Treat prompts and tool traces as durable. If org policy forbids vendor logs, this leaf collapses to local-only.
- Output scan. Diffs still pass through the same secret regex, because models echo context.
git diff --stat
git diff -- . ':(exclude)*.pem' ':(exclude)*.env'
python3 payload_class.py --root ./limiter --allow internal_nonsensitive
This is the leaf where a remote free loop is actually useful. Iteration is cheap, the blast radius is a library, and you can throw the machine away. It is not a license to upload the rest of the monorepo "for context."
Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode offers free model access and a free server option; those two facts are the only product claims used here. They fit Leaf 3 and Leaf 4 on an allowlisted, nonsensitive slice. They do not move a Leaf 1 payload into Leaf 3.
Leaf 4 — Unrestricted public
Worked example. A public issue includes a 12-line repro and a failing pytest. Everything you would paste is already public.
# repro_public.py
def clamp(n, lo, hi):
if lo > hi:
raise ValueError("empty range")
return min(max(n, lo), hi)
# test_clamp.py
import pytest
from repro_public import clamp
def test_inverted_bounds_raise():
with pytest.raises(ValueError):
clamp(0, 10, -10)
Send the repro. Run the test on the remote box. Keep the same evidence standard you would use for a human contributor: the patch must fail before and pass after, using tests the agent did not silently weaken.
pytest -q test_clamp.py
# after the patch
pytest -q test_clamp.py
git diff test_clamp.py # reject assertion deletions
Public is not "skip review." It only means egress is not the blocking risk.
Artifact: a payload classifier you can run
Label this as an unexecuted example until you run it against your own tree. It encodes the tree. It does not replace legal review.
#!/usr/bin/env python3
"""payload_class.py — classify a workspace before a remote agent session."""
from __future__ import annotations
import argparse
import re
from pathlib import Path
SECRET_RES = [
re.compile(r"-----BEGIN (?:RSA |OPENSSH |EC )?PRIVATE KEY-----"),
re.compile(r"AKIA[0-9A-Z]{16}"),
re.compile(r"sk_(?:live|test)_[A-Za-z0-9]+"),
re.compile(r"(?i)(password|secret|api[_-]?key)\s*[:=]\s*\S+"),
re.compile(r"(?i)postgres(?:ql)?://[^\s]+"),
]
RESTRICTED_RES = [
re.compile(r"(?i)\b(?:ssn|social security)\b"),
re.compile(r"\b\d{3}-\d{2}-\d{4}\b"),
re.compile(r"(?i)\b(?:patient|medical record|iban)\b"),
re.compile(r"(?i)\b(?:card_last4|cvv|pan)\b"),
]
SKIP_PARTS = {".git", "node_modules", "__pycache__", ".venv"}
def iter_files(root: Path):
for p in root.rglob("*"):
if not p.is_file():
continue
if any(part in SKIP_PARTS for part in p.parts):
continue
if p.stat().st_size > 1_000_000:
continue
yield p
def classify(root: Path) -> str:
secret_hits = []
restricted_hits = []
for p in iter_files(root):
try:
text = p.read_text(encoding="utf-8", errors="ignore")
except OSError:
continue
for rx in SECRET_RES:
if rx.search(text):
secret_hits.append(str(p))
break
for rx in RESTRICTED_RES:
if rx.search(text):
restricted_hits.append(str(p))
break
if secret_hits:
return "secret_bearing"
if restricted_hits:
return "restricted_record"
return "internal_or_public"
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--root", type=Path, default=Path("."))
parser.add_argument(
"--allow",
choices=[
"secret_bearing",
"restricted_record",
"internal_nonsensitive",
"public",
],
required=True,
help="The leaf you believe you are on.",
)
args = parser.parse_args()
found = classify(args.root)
print(f"found={found} claimed={args.allow}")
if args.allow in {"public", "internal_nonsensitive"} and found != "internal_or_public":
return 2
if args.allow == "restricted_record" and found == "secret_bearing":
return 2
if found == "secret_bearing" and args.allow != "secret_bearing":
return 2
return 0
if __name__ == "__main__":
raise SystemExit(main())
Minimal tests for the classifier:
# test_payload_class.py
from pathlib import Path
import payload_class
def test_secret_env(tmp_path: Path):
(tmp_path / ".env").write_text("PASSWORD=EXAMPLE_ONLY\n")
assert payload_class.classify(tmp_path) == "secret_bearing"
def test_restricted_ticket(tmp_path: Path):
(tmp_path / "ticket.txt").write_text("patient iban listed in notes\n")
assert payload_class.classify(tmp_path) == "restricted_record"
def test_clean_src(tmp_path: Path):
(tmp_path / "clamp.py").write_text("def clamp(n, lo, hi):\n return n\n")
assert payload_class.classify(tmp_path) == "internal_or_public"
pytest -q test_payload_class.py
python3 payload_class.py --root . --allow internal_nonsensitive
echo $?
If the exit code is 2, do not start the remote session. Fix the payload class first.
Decision table
| Leaf | Payload class | Remote free loop | Required artifact |
|---|---|---|---|
| 1 | Secret-bearing | No | Unmount or refuse |
| 2 | Restricted record | Only with a stand-in | Failing synthetic test |
| 3 | Internal-nonsensitive | Yes, allowlisted | Classifier exit 0 + diff scan |
| 4 | Public reproduction | Yes | Fail-then-pass tests, no weakened asserts |
The table is the tree flattened. If a row needs a fact you do not have — vendor log retention, data-policy scope, whether a stand-in actually fails — you do not have an egress decision yet.
Limitations
This tree does not detect secrets in images, compiled binaries, or encrypted vault files that an agent might still cat. Regex will miss custom token formats. It will also false-positive on documentation that discusses the word "password."
Who should not use this approach:
- Teams under HIPAA, PCI, or equivalent controls that require a vetted DLP product, not a gist-sized script.
- Anyone whose remote vendor retention terms are unknown; Leaf 3 assumes you can accept vendor logs.
- Sessions that need production data to reproduce a bug. If a stand-in cannot fail the test, the remote free loop is the wrong tool.
The classifier is a pre-prompt gate. It is not an oracle for the patch, and it is not a substitute for git diff review.
Closing
Debates about whether models code "better than developers" skip a prior question. What did you send them? A free model on a free server does not change Leaf 1 or Leaf 2. It only makes Leaf 3 and Leaf 4 cheaper to execute well.
Run the classifier. Name the leaf out loud. Then start the agent — or do not.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.