DEV Community

Dakota Huang
Dakota Huang

Posted on

A Receipt Checkpoint for Free Model Outputs Before They Become Shell Commands

Free model endpoints fail in ways that leave no trace; save a receipt before the output becomes a shell command, patch, or query.

Most review loops treat a model response as transient. You read it, strip markdown fences, run it, and later you cannot reconstruct which endpoint produced the command, which prompt produced it, or whether the file changed after you saved it. That gap is worse on free compute: hosts are shared, outputs are non-deterministic, and there may be no provider-side audit trail you control.

This checkpoint is not a security boundary. The receipt stores exact bytes, a hash, and enough metadata to replay an investigation. It does not decide whether the output is safe to execute.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

MonkeyCode's free model access and free server option are operator-supplied availability claims. They can serve as a convenient lab for this vendor-neutral workflow. The script works with any HTTP JSON endpoint, so nothing below depends on MonkeyCode-specific behavior.

Why a hash before execution helps

  • Identity: you can prove a command you executed is byte-identical to the output you reviewed.
  • Replay: you can find the prompt and endpoint label after the fact.
  • Change detection: if someone edits the file later, the hash no longer matches.
  • Review record: the receipt is a local record of what you handled.

What the receipt does not do:

  • validate correctness
  • block prompt injection
  • detect data exfiltration
  • guarantee the endpoint is trustworthy

The workflow

  1. Fetch the model output and write the raw body to a file without parsing or trimming.
  2. Hash that exact file.
  3. Append one JSONL receipt with the hash, UTC timestamp, output path, prompt path hash, and endpoint label.
  4. Only then move to a review step, a parser, or an execution gate.
  5. Copy receipts off the execution host if you need them after a cleanup.

Minimal receipt collector

Save this as receipt.py:

#!/usr/bin/env python3
import argparse
import hashlib
import json
import sys
from datetime import datetime, timezone
from pathlib import Path


def sha256_file(path):
    digest = hashlib.sha256()
    with path.open('rb') as handle:
        for chunk in iter(lambda: handle.read(65536), b''):
            digest.update(chunk)
    return digest.hexdigest()


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--output', required=True, type=Path)
    parser.add_argument('--prompt', type=Path)
    parser.add_argument('--label', required=True)
    parser.add_argument('--receipts', type=Path, default=Path('receipts.jsonl'))
    parser.add_argument('--extra', action='append', default=[])
    args = parser.parse_args()

    if not args.output.exists():
        print(f'missing output file: {args.output}', file=sys.stderr)
        return 2

    prompt_hash = None
    if args.prompt and args.prompt.exists():
        prompt_hash = sha256_file(args.prompt)

    receipt = {
        'version': 1,
        'created_at': datetime.now(timezone.utc).isoformat(),
        'output_file': str(args.output),
        'sha256': sha256_file(args.output),
        'prompt_sha256': prompt_hash,
        'label': args.label,
        'extra': args.extra,
    }

    args.receipts.parent.mkdir(parents=True, exist_ok=True)
    with args.receipts.open('a', encoding='utf-8') as handle:
        handle.write(json.dumps(receipt, sort_keys=True) + '\n')

    print(args.receipts)
    return 0


if __name__ == '__main__':
    raise SystemExit(main())
Enter fullscreen mode Exit fullscreen mode

Run it after the model response is saved:

# curl is only an example; use the client your endpoint exposes
curl -s https://example.invalid/v1/chat/completions -o out/raw-response.txt
python3 receipt.py --output out/raw-response.txt --prompt prompt.txt --label free-endpoint-lab --extra temperature=0.2
Enter fullscreen mode Exit fullscreen mode

The prompt path is hashed, not logged, so the receipt does not duplicate prompt text or response text into the log. If you need full prompt text later, store a redacted copy in a separate location.

What the JSONL line gives you later

Each line carries the hash, UTC timestamp, output path, endpoint label, and provider-specific metadata you add with --extra. You can search it with standard tools:

grep 'free-endpoint-lab' receipts.jsonl | tail -n 5
Enter fullscreen mode Exit fullscreen mode

If a file changed after review, recompute the hash:

sha256sum out/raw-response.txt
Enter fullscreen mode Exit fullscreen mode

Compare it with the receipt line. If it does not match, treat the file as altered and do not trust your earlier review.

Limits of the receipt

  • It proves identity, not safety.
  • A local log can be modified by a process that already compromised the host. Write receipts to a separate volume or account if you need stronger integrity.
  • Free server storage can be ephemeral; a receipt left in /tmp disappears.
  • Add a signature or upload to immutable storage only if tamper evidence is a real requirement.

Where this fits among other gates

This script does not replace your validation and permission work:

  • a permission matrix for tool calls
  • a read-only gate for generated SQL
  • a replay harness for model comparison

It adds one missing layer: an audit record before the dangerous part begins. Run it after buffering the raw response and before any parser changes the bytes.

Who should skip this

  • You already have a signed execution log.
  • Your pipeline never runs model output directly.
  • You are handling real credentials in prompts; this hashes a prompt file but still requires secret hygiene elsewhere.
  • You need prevention more than evidence; use a sandbox or gate first.

MonkeyCode's free options are useful for this kind of lab because they give you a throwaway model host and server without touching your own machine. If you already use them, add the receipt step to the same harness; it is small, vendor-neutral, and easy to remove.

Top comments (0)