I want the network call to be the last step, not the first. A free model does not get an uncounted prompt from me. If I cannot hash the text, reject secret-shaped lines, and fail my own cap, the call does not happen.
That is the whole tutorial. Six local checks, then an optional send. The script is a proposal you can run on one laptop, and I am not reporting a benchmark, a retained quota, or a model name. This draft has no primary source for those facts today.
Why the ledger comes first
Remote planners help when the question is fuzzy and the text is already public. They are a bad place to discover that a token was pasted into the prompt. Have you ever scrolled a chat log and found a key sitting in the third message?
I split the work on purpose. Counting and redaction stay on the laptop. A free model path, if I use one, sees only a prompt that already has a ledger. A free server, if I use one, sees an argv list I froze in JSON. It does not see my home directory.
Step 1. Create a job folder and list it
I make the directory by hand. Two files go in. Then I list every file before any script is allowed to read a parent tree.
mkdir -p "$HOME/prompt-ledger/job-001"
cd "$HOME/prompt-ledger/job-001"
printf '%s\n' 'Explain how a lockfile pin stops a surprise upgrade. Stay in prose.' > prompt.txt
printf '%s\n' '1200' > max_estimate.txt
find . -type f -print | sort
Verification: find prints ./max_estimate.txt and ./prompt.txt. Nothing else. If a checkout, a .env, or a key file appears, I delete the folder and start over. Why send a tree I have not listed?
Step 2. Save one preflight script
The scanner is intentionally dumb. It looks for cloud-key shapes, private-key banners, and token= style assignments. It will miss a secret written as a sentence, and it will flag ordinary wording when I am careless. That bias is what I want before any network hop.
Save the following as preflight.py. Treat it as a local proposal until you run it. It does not contain a client, a host, or a model name.
# proposal: local preflight only; no network client in this file
import hashlib
import json
import re
import sys
from pathlib import Path
def main() -> int:
prompt = Path("prompt.txt").read_text(encoding="utf-8")
cap = int(Path("max_estimate.txt").read_text(encoding="utf-8").strip())
patterns = [
r"AKIA[0-9A-Z]{16}",
r"-----BEGIN [A-Z ]+PRIVATE KEY-----",
r"(?i)(api[_-]?key|secret|token)\s*[:=]\s*\S+",
]
if any(re.search(p, prompt) for p in patterns):
sys.stderr.write("secret-shaped text in prompt; refusing\n")
return 2
estimate = max(1, (len(prompt) + 3) // 4)
if estimate > cap:
sys.stderr.write(f"estimate {estimate} over cap {cap}\n")
return 3
digest = hashlib.sha256(prompt.encode("utf-8")).hexdigest()
ledger = {
"prompt_sha256": digest,
"chars": len(prompt),
"token_estimate_chars_div_4": estimate,
"cap": cap,
"estimate_is_not_a_bill": True,
"network": "not_sent",
}
Path("ledger.json").write_text(
json.dumps(ledger, indent=2) + "\n",
encoding="utf-8",
)
print(digest)
return 0
if __name__ == "__main__":
sys.exit(main())
Verification for the file itself:
test -f preflight.py
python3 -m py_compile preflight.py
echo "compile_ok $?"
I want compile_ok 0. A syntax error means I do not continue, even if the prompt looks harmless.
Step 3. Prove the scanner fails closed
I do not trust a scanner I have not seen fail. A fake assignment is enough. It is not a real credential, and it should still stop the job.
printf '\n%s\n' 'token=demo-not-real' >> prompt.txt
python3 preflight.py
echo "secret_exit:$?"
Verification: secret_exit:2. If the process exits 0, the regex is wrong. Fix it before you think about a model. Then restore the clean prompt:
printf '%s\n' 'Explain how a lockfile pin stops a surprise upgrade. Stay in prose.' > prompt.txt
Step 4. Prove the clean ledger stays offline
Vendors do not share one tokenizer. I still refuse to discover a giant paste after the request leaves. Dividing characters by four is a rough ceiling, not an invoice. The ledger says so in a field, so a later reader does not confuse this file with a bill.
python3 preflight.py
sha256sum prompt.txt
python3 -c 'import json; d=json.load(open("ledger.json")); assert d["network"]=="not_sent"; print(d["prompt_sha256"], d["token_estimate_chars_div_4"])'
Verification: the printed digest matches sha256sum, and network is still not_sent. What exactly left the machine? Nothing. That is the point of the step.
Want a cap failure too? Set max_estimate.txt to 1, run the script, and expect exit 3. Put 1200 back when you are done. I like seeing both red exits before I trust a green one.
Step 5. Decide whether a remote model is even useful
A short question with a man page does not need a remote call. I read prompt.txt again and ask: would I be embarrassed to paste this into a public ticket? If yes, the ledger stays not_sent and I rewrite. If a local doc already answers it, I stop while I am ahead.
Only a prompt that survived the file list, the scan, the cap, and that embarrassment check is eligible for a free model path. Record the decision so the next command cannot shrug.
printf '%s\n' local_only > decision.txt
grep -qx -e local_only -e eligible decision.txt && echo decision_ok
Verification: decision_ok. Flip the file to eligible only when you actually want a send. I leave mine at local_only for this sample, because the sample question is tiny.
Disclosure: This article was prepared as part of MonkeyCode's product outreach. The operator of this draft says MonkeyCode offers free model access and a free server option. I am not naming a model, quoting a token grant, or describing hardware. Those details change, and I will not freeze a marketing number into a job runner. Open the project page and read the live limits before you schedule anything.
Step 6. Send from a second command, or do not send
I keep HTTP out of preflight.py so a green preflight cannot accidentally become a network call. The sender, when you have a documented URL, should read prompt.txt and the cap, and no other file. It should also refuse to run unless decision.txt says eligible.
test -f ledger.json
python3 -c 'import json; assert json.load(open("ledger.json"))["network"]=="not_sent"'
grep -qx eligible decision.txt || { echo "decision is not eligible; not sending"; exit 0; }
# proposal: replace the next line with a documented client; do not invent a host
echo "no documented client in this draft; ledger stays not_sent"
Verification: with decision.txt set to local_only, the command prints the not-sending line and does not open a socket. Guessing a host to "try the API" is how a prompt lands in the wrong place. After a real send, write ledger-sent.json as a new file. Leave the original ledger immutable so you can diff intent against what you claim you did.
Step 7. Freeze argv before any free server
If the planning text implies a command, I do not paste that text into a shell. I write argv as a JSON list of strings. A free server is eligible only for that list, and only if I can see the command the host will run.
python3 - <<'PY'
import json
json.dump(["git", "rev-parse", "--is-inside-work-tree"], open("argv.json", "w"))
PY
python3 - <<'PY'
import json
argv = json.load(open("argv.json"))
assert isinstance(argv, list) and argv
assert all(isinstance(x, str) and x for x in argv)
assert all((" " not in x) and ("|" not in x) and (";" not in x) for x in argv)
print("argv_ok", len(argv))
PY
Verification: the second command prints argv_ok 3. If any element contains a space, a pipe, or a semicolon, it fails. I am not re-teaching an empty-directory proof here. I am only refusing to hand a server a string I have not already split. If the host cannot display that argv back to me, I do not start it.
A decision table I can scan in review
| Check | Continue | Stop |
|---|---|---|
find shows only the two job files |
go to the scan | delete the folder |
fake token= line |
must exit 2 | fix the regex |
| estimate under your cap | write ledger.json
|
exit 3, shrink the prompt |
decision.txt is local_only
|
keep not_sent
|
do not call a client |
decision.txt is eligible and the URL is documented |
optional send | do not invent a host |
| argv is a list of single tokens | eligible for a free server | rewrite argv |
What I keep, and who should skip this
I store prompt.txt, ledger.json, decision.txt, and, if a response exists, answer.txt. A passing script is not permission to merge. I still read the answer. I still do not execute it.
python3 - <<'PY'
from pathlib import Path
p = Path("answer.txt")
if not p.exists():
print("no_answer_yet")
raise SystemExit(0)
text = p.read_text(encoding="utf-8")
raise SystemExit(0 if 0 < len(text) <= 8000 else 4)
PY
Verification: a missing answer prints no_answer_yet and exits 0, because this sample never sent. A huge dump exits 4. A short note exits 0, and I still read it myself.
This estimate will disagree with a real tokenizer. The regex will miss quiet secrets and flag ordinary words. Free model access and a free server can be unavailable, slow, or unfit for private source. I would not use the path for production deploys, customer data, credentials, or any text I cannot show a reviewer.
Skip it if you need a contractual quota, a named model, or a retention promise. Stay on local tools, or read the current docs and accept whatever those docs actually require. A blog post is not that agreement.
If you try the free model path or the free server, start with a throwaway prompt, confirm the digest, and leave network at not_sent until the second command is one you meant to run.
Top comments (0)