Most workloads should never touch a free token tier. The useful engineering work is deciding that before the first request, not during the postmortem. A free tier behaves like a borrowed laptop: fine for a sketch, wrong for payroll.
What follows is a disqualification checklist rather than an onboarding guide. It takes the free capacity as real and asks a narrower question — which jobs are refused no matter how attractive the zero on the invoice looks.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
The operator advertises 10 million free tokens and a free server option for the project. Both numbers belong on the project's own pages, and both should be re-read on the day you wire anything in. The gate below does not depend on those numbers, which is the point: policy first, capacity second.
Six disqualifiers, no exceptions
The first is a data boundary. If the payload contains personal data, health data, credentials, customer content, or source you would not paste into a public gist, the free path is closed. Not because the vendor is untrustworthy, but because you cannot audit the path, and unauditable paths do not get to hold regulated data.
The second is privilege. Any workload that deletes, deploys, migrates, signs, or moves money is out. A free endpoint is a shared convenience; privilege grants are a different class of object and should never be reachable from it.
The third is latency in the interactive path. If a human is waiting and your budget is a couple of seconds, a shared free path cannot be inside that budget by construction, because you control neither queue depth nor neighbours. Batch it, or pay for the guarantee.
The fourth is durability. Free servers are for compute, not custody. State that must survive a restart belongs in a database you operate, and the free instance should be able to vanish at any moment without a support ticket.
The fifth is evidence. If an auditor, a customer, or a court may ask which model produced this output, the answer has to be a pinned artifact you own. You cannot pin what you do not control.
The sixth is blast radius. When a failure pages an on-call engineer or surfaces directly to a customer, the failure needs a party contractually on the hook. Free tiers are not on the hook, and pretending otherwise converts a subsidy into an outage.
The gate as code
A checklist that lives in a wiki rots. The one below lives in CI and takes a JSON descriptor per workload. It has no dependencies beyond the standard library.
#!/usr/bin/env python3
"""free_tier_gate.py - refuse a free token tier before it carries work.
Usage:
python3 free_tier_gate.py workload.json
python3 free_tier_gate.py *.json
Exit codes: 0 = all allowed, 1 = at least one denied, 2 = bad input.
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
SENSITIVE = {"pii", "phi", "secrets", "customer_content", "internal_source"}
PRIVILEGED = {"deploy", "delete", "payment", "grant", "migrate", "sign"}
LATENCY_FLOOR_MS = 2000
def r_data(w):
bad = sorted(set(w.get("data_classes", [])) & SENSITIVE)
return f"data classes cross a boundary you cannot audit: {bad}" if bad else None
def r_privilege(w):
bad = sorted(set(w.get("actions", [])) & PRIVILEGED)
return f"privileged actions on a subsidised path: {bad}" if bad else None
def r_latency(w):
budget = w.get("latency_budget_ms", 10**9)
if w.get("user_facing") and budget < LATENCY_FLOOR_MS:
return f"user-facing call with a {budget} ms budget cannot sit on a shared path"
return None
def r_durability(w):
if w.get("stateful") or w.get("needs_durable_disk"):
return "workload expects state to outlive the instance"
return None
def r_evidence(w):
if w.get("requires_pinned_artifact"):
return "workload needs a pinned model or image the free tier does not promise"
return None
def r_blast(w):
if w.get("on_failure") in {"page_oncall", "customer_visible_error"}:
return f"failure mode is {w['on_failure']}, which a free tier cannot underwrite"
return None
RULES = [r_data, r_privilege, r_latency, r_durability, r_evidence, r_blast]
def evaluate(w):
reasons = [r for rule in RULES if (r := rule(w))]
return ("DENY" if reasons else "ALLOW"), reasons
def main(argv):
if not argv:
print(__doc__, file=sys.stderr)
return 2
code = 0
for name in argv:
try:
w = json.loads(Path(name).read_text())
except (OSError, json.JSONDecodeError) as exc:
print(f"{name}: unreadable descriptor ({exc})", file=sys.stderr)
code = max(code, 2)
continue
verdict, reasons = evaluate(w)
print(f"{w.get('name', name)}: {verdict}")
for reason in reasons:
print(f" - {reason}")
if verdict == "DENY":
code = max(code, 1)
return code
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
A descriptor is deliberately boring, and the boring ones are the survivors.
{
"name": "nightly-changelog-summary",
"data_classes": ["public_docs"],
"actions": ["read"],
"user_facing": false,
"stateful": false,
"requires_pinned_artifact": false,
"on_failure": "retry_next_night"
}
Running the gate over a mixed directory produces the kind of output a reviewer can act on without a meeting.
$ python3 free_tier_gate.py *.json
nightly-changelog-summary: ALLOW
invoice-extraction: DENY
- data classes cross a boundary you cannot audit: ['customer_content', 'pii']
- failure mode is customer_visible_error, which a free tier cannot underwrite
Two tests keep the rules honest, because a gate nobody tests slowly drifts toward ALLOW.
import unittest
from free_tier_gate import evaluate
class GateTest(unittest.TestCase):
def test_refuses_privileged_action(self):
verdict, reasons = evaluate({"actions": ["delete"], "on_failure": "retry"})
self.assertEqual(verdict, "DENY")
self.assertTrue(any("privileged" in r for r in reasons))
def test_allows_offline_batch(self):
verdict, reasons = evaluate({"data_classes": ["public_docs"], "on_failure": "retry"})
self.assertEqual((verdict, reasons), ("ALLOW", []))
What survives the gate
Plenty survives, and it is the unglamorous half of the work. Summarising public documentation, drafting commit messages, classifying already-published text, generating throwaway scaffolding for a spike, and labelling a synthetic fixture set all pass every rule above. The free server is a reasonable home for ephemeral previews, demo endpoints fed by fake data, and CI jobs that can be recreated from a lockfile.
The shared property is that a sudden disappearance costs an afternoon, not a customer. That is the only durability guarantee a free tier can offer, and it is enough for this class of work.
Exit criteria you can automate
Refusal is a decision made once. Exit is a decision made continuously. A small probe that demotes the free path without a deploy is worth more than a paragraph in a runbook.
#!/usr/bin/env bash
# exit_probe.sh - remove the free path from rotation without a deploy.
set -euo pipefail
FLAG="${FLAG:-/var/run/free_tier_disabled}"
URL="${URL:?set URL to the health endpoint}"
deadline=$(( $(date +%s) + 3600 ))
while [ "$(date +%s)" -lt "$deadline" ]; do
read -r http secs <<<"$(curl -s -o /dev/null -w '%{http_code} %{time_total}' --max-time 5 "$URL" || echo '000 5.0')"
if [ "$http" != "200" ] || awk -v s="$secs" 'BEGIN{exit !(s > 3.0)}'; then
date -u +%FT%TZ > "$FLAG"
echo "free tier demoted at $(cat "$FLAG")"
exit 1
fi
sleep 60
done
Any scheduler can run it. The flag file is the contract: readers check it before every call, and no one has to trust memory at 3 a.m.
Who should skip this entirely
Teams handling regulated data should not start here. Neither should products whose core promise is latency, teams that need a named party to answer a support ticket, or anyone who cannot rotate the credential the free path uses. A team without centralised logging has one more reason to wait, because an unauditable path plus unreadable logs is indistinguishable from no testing at all.
The approach also has limits worth stating plainly. The gate encodes policy, not security; it will not stop a determined engineer from calling the endpoint by hand. Token allowances and server terms change, and an approval issued in October is not an approval in January. Re-run the descriptors whenever the terms are re-read.
If a descriptor returns ALLOW, running that batch against MonkeyCode's free tier is a sensible first stop, and the checklist is what keeps it cheap rather than expensive later.
MonkeyCode provides free models that can run this workflow.
Top comments (0)