A dead network should not freeze a coding session. Park each prompt on local disk first. Flush it only when three conditions hold.
Those conditions are a live line, a clean secret boundary, and a job that actually needs remote capacity. AI-assisted coding made this placement problem louder. More diffs now start as prompts, not as keystrokes.
The laptop remains the source of truth. The cloud is a mill across town. You do not haul every board through traffic.
This week’s feeds keep arguing whether models already outcode most developers. Treat that as noise, not a deployment plan. The practical question is narrower. Where does this prompt run without stalling you or leaking the repo?
The offline window is the real constraint
Cafe wifi dies in the middle of a refactor. A train enters a tunnel. A hotel captive portal sits on the first hop. None of that is exotic. It is Tuesday.
If your editor blocks on a remote model, the session stops. If the prompt sits in a local queue, you keep moving. Local search, tests, and patches still run. The mill can wait.
Think of the queue as a mailbox on the porch. The courier only comes when the road is open. The letters stay yours until then.
Latency is not one number. It is a chain. DNS, TLS, queue time, tokens, and the trip home all add up. A local grep does not pay that chain.
Secrets make the chain sharper. .env files, cookies, and private keys live on disk for a reason. A free remote box does not erase that reason. Compute can travel. Credentials should not.
When a free server actually wins
A free remote path is not a personality. It is a burst lane. Use it when the laptop is the bottleneck, not when the prompt is small.
Heavy refactors across many files can justify the hop. Long generated tests can justify it. A thermally throttled fan can justify it. A one-line rename rarely does.
Offline work still wins the morning commute. Local work still wins the secret-touched path. Remote work wins only after the line is honest and the job is large.
Do not confuse “free” with “always on.” Shared capacity is a courtesy, not an SLA. Keep production traffic off that lane.
Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode offers free model access and a free server option for that burst path. This article does not name models, quote quotas, or invent hardware. The method below still works if you swap the remote target.
Artifact: a disk queue with three gates
The script below is a proposed workflow. It is not a measured bake-off. It stores prompts as JSONL on disk. It refuses a flush when any gate fails.
Gate A probes connectivity with a short TCP handshake. Gate B scans attached paths for secret-like names. Gate C compares payload size to a local-first threshold you set.
Save it as prompt_queue.py.
#!/usr/bin/env python3
"""Local-first prompt queue. Proposed workflow, not a vendor SDK."""
from __future__ import annotations
import argparse
import json
import socket
import time
from pathlib import Path
QUEUE = Path.home() / ".cache" / "prompt_queue" / "jobs.jsonl"
SECRET_NAMES = {
".env",
".env.local",
"id_rsa",
"id_ed25519",
"credentials.json",
"secrets.yaml",
}
SECRET_PARTS = {".pem", ".p12", "kubeconfig"}
LOCAL_FIRST_BYTES = 32_768 # tune per team; not a benchmark
def load_jobs() -> list[dict]:
if not QUEUE.exists():
return []
rows = []
for line in QUEUE.read_text(encoding="utf-8").splitlines():
if line.strip():
rows.append(json.loads(line))
return rows
def save_jobs(rows: list[dict]) -> None:
QUEUE.parent.mkdir(parents=True, exist_ok=True)
payload = "".join(json.dumps(r) + "\n" for r in rows)
QUEUE.write_text(payload, encoding="utf-8")
def secret_hits(paths: list[str]) -> list[str]:
hits = []
for raw in paths:
p = Path(raw)
name = p.name.lower()
blob = str(p).lower()
if name in SECRET_NAMES or any(part in blob for part in SECRET_PARTS):
hits.append(str(p))
return hits
def payload_bytes(prompt: str, paths: list[str]) -> int:
total = len(prompt.encode("utf-8"))
for raw in paths:
p = Path(raw)
if p.is_file():
total += p.stat().st_size
return total
def line_is_honest(host: str, port: int, timeout: float) -> tuple[bool, float]:
start = time.perf_counter()
try:
with socket.create_connection((host, port), timeout=timeout):
pass
return True, time.perf_counter() - start
except OSError:
return False, time.perf_counter() - start
def decide(job: dict, host: str, port: int) -> dict:
live, rtt = line_is_honest(host, port, timeout=1.5)
hits = secret_hits(job.get("paths") or [])
size = payload_bytes(job["prompt"], job.get("paths") or [])
if hits:
action = "HOLD_SECRETS"
elif not live:
action = "HOLD_OFFLINE"
elif size <= LOCAL_FIRST_BYTES:
action = "LOCAL"
else:
action = "FLUSH"
return {
"action": action,
"rtt_s": round(rtt, 4),
"secret_hits": hits,
"payload_bytes": size,
}
def cmd_add(args: argparse.Namespace) -> None:
jobs = load_jobs()
jobs.append(
{
"id": int(time.time() * 1000),
"prompt": args.prompt,
"paths": args.paths or [],
"created_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
}
)
save_jobs(jobs)
print(f"queued {jobs[-1]['id']} at {QUEUE}")
def cmd_status(args: argparse.Namespace) -> None:
jobs = load_jobs()
if not jobs:
print("queue empty")
return
for job in jobs:
verdict = decide(job, args.host, args.port)
print(json.dumps({"id": job["id"], **verdict}, indent=2))
def cmd_flush(args: argparse.Namespace) -> None:
jobs = load_jobs()
kept = []
for job in jobs:
verdict = decide(job, args.host, args.port)
if verdict["action"] != "FLUSH":
kept.append(job)
print(f"keep {job['id']}: {verdict['action']}")
continue
# Proposed remote call site. Wire your own runner here.
print(
json.dumps(
{
"flushed_id": job["id"],
"rtt_s": verdict["rtt_s"],
"payload_bytes": verdict["payload_bytes"],
"note": "replace this print with your remote runner",
}
)
)
save_jobs(kept)
def main() -> None:
parser = argparse.ArgumentParser(description="Park prompts on disk until the line is honest.")
parser.add_argument("--host", default="1.1.1.1")
parser.add_argument("--port", type=int, default=443)
sub = parser.add_subparsers(dest="cmd", required=True)
add_p = sub.add_parser("add")
add_p.add_argument("prompt")
add_p.add_argument("--paths", nargs="*")
add_p.set_defaults(func=cmd_add)
st_p = sub.add_parser("status")
st_p.set_defaults(func=cmd_status)
fl_p = sub.add_parser("flush")
fl_p.set_defaults(func=cmd_flush)
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()
Run it from a throwaway clone, not from a secrets directory.
chmod +x prompt_queue.py
python3 prompt_queue.py add "sketch a retry wrapper" --paths src/http_client.py
python3 prompt_queue.py status --host example.com --port 443
python3 prompt_queue.py flush --host example.com --port 443
The status command prints a verdict per job. It does not send the prompt. The flush command only drops jobs that passed every gate. Held jobs remain on disk for the next honest line.
A companion check belongs in your shell profile. It should fail closed when the probe host is unreachable.
# proposed helper; label it as unexecuted until you time it yourself
probe_line() {
python3 - <<'PY'
import socket, sys
try:
socket.create_connection(("example.com", 443), timeout=1.5)
except OSError:
sys.exit(1)
PY
}
if ! probe_line; then
echo "line is not honest; keep the queue on disk"
fi
How to read a verdict
HOLD_OFFLINE means the mill is unreachable. Keep typing locally. Re-run status after the tunnel.
HOLD_SECRETS means a attached path looks like a credential. Strip that path. Then queue a redacted slice. Do not “just this once” the flush.
LOCAL means the payload is small enough to finish on disk. Your editor, compiler, and tests already live there. Spending a round trip on a 2 KB hint is theatre.
FLUSH means the road is open, the envelope is clean, and the crate is heavy. That is the only time a free server is the rational mill. Even then, pin the work to a branch you can delete.
Notice the order. Secrets beat connectivity. Connectivity beats size. Size never overrides a secret hit. That order is the whole policy.
A short decision matrix you can copy
Keep this table next to the script. It is a method, not a scoreboard. Fill the last column with your own timings later.
| Situation | Line | Secrets in paths | Payload | Action |
|---|---|---|---|---|
| Train tunnel | down | no | any | HOLD_OFFLINE |
Cafe wifi, .env attached |
up | yes | any | HOLD_SECRETS |
| Office net, 8 KB helper | up | no | small | LOCAL |
| Office net, multi-file refactor | up | no | large | FLUSH |
| Captive portal | down or fake | no | large | HOLD_OFFLINE |
The captive portal row matters. A handshake to port 443 can still lie. If the portal returns HTML, treat the line as dishonest. Add an HTTP status check before you trust FLUSH in hotels.
# proposed extra gate; run only after TCP succeeds
import urllib.request
def http_looks_real(url: str) -> bool:
try:
with urllib.request.urlopen(url, timeout=2) as resp:
return 200 <= resp.status < 400 and "text/html" not in resp.headers.get("Content-Type", "")
except Exception:
return False
That snippet is conservative on purpose. A false negative keeps work local. A false positive ships a prompt into a login page.
Limitations
This queue does not encrypt the JSONL file. Disk access still means laptop access. Use full-disk encryption and a locked screen.
Filename heuristics miss secrets in ordinary source. A hard-coded token in app.py will pass Gate B. Pair this with a real secret scanner before any flush.
The TCP probe is not a bandwidth test. A 200 ms handshake can still precede a terrible upload. Time a small PUT yourself if the payload is large.
There are no model names here, and no token budgets. Shared free capacity can vanish without notice. Do not build a release pipeline on it.
The script never proves that remote output is better. It only proves the hop was allowed. Review stays on you.
Who should not use this
Skip this approach if policy forbids any remote inference. Air-gapped shops should delete the flush command entirely.
Skip it if your working tree is patient data, unpublished keys, or unreleased exploits. A free server is the wrong mill for that timber.
Skip it if you need guaranteed GPUs and pinned regions. A courtesy box is not a contract.
Skip it if the team already streams the whole monorepo to a hosted agent. This workflow assumes the laptop is still the door.
Close the loop on disk
Start every prompt on disk. Let tests, diffs, and redaction happen there. Move compute only when the line is honest, the envelope is clean, and the crate is heavy.
That rule survives model hype cycles. It also survives a tunnel. If a hop already passed those gates, the free remote path is one mill you can try on a throwaway branch.
Top comments (0)