A Residency Lease for Agent Runs: Expiring Placement Decisions Instead of Static Config
Reproducing the failure takes about a minute on any laptop: start an agent turn against a remote model endpoint, then drop the network with nmcli networking off halfway through. The run rarely fails loudly. It stalls, retries, and eventually writes a truncated artifact that still validates against the schema.
The placement config said remote, and nothing in that config knew the link had disappeared ten minutes earlier. A boolean flag cannot expire, so the run kept honouring a decision whose supporting evidence was gone. The fix is not a better flag; it is a decision that carries its own expiry date.
Why a static placement flag cannot survive a long run
Placement is normally decided once, at process start, on the assumption that the conditions measured at start still hold. Agent runs break that assumption in ordinary ways: tool calls take minutes, laptops sleep, tunnels drop, and a shared office uplink degrades at the exact moment a batch job begins. A static config records intent; it does not record evidence, and it cannot tell you when it stopped being true.
A lease replaces the flag with a small, immutable record: the chosen placement, the moment it expires, and the measurements that justified it. Invariants such as "this payload must not leave the host" produce long leases because they do not change with the weather. Measurements such as round-trip latency and packet loss produce short leases because they do.
The lease record
The data model is deliberately boring. Constraints are inputs, a lease is an output, and validity is a pure function of the clock, which makes every decision testable without a network.
# lease.py — a self-expiring placement lease for local-first agent runs
from __future__ import annotations
import os, re, statistics, socket, time
from dataclasses import dataclass
from enum import Enum
class Placement(str, Enum):
LOCAL = "local"
REMOTE = "remote"
@dataclass(frozen=True)
class Constraints:
secret_free_local: bool # payload may not leave the host
offline_capable: bool # must finish with no network at all
p95_budget_ms: int # per-turn latency budget
@dataclass(frozen=True)
class Lease:
placement: Placement
expires_at: float
evidence: dict
def valid(self, now: float | None = None) -> bool:
return (time.time() if now is None else now) < self.expires_at
Because Lease is frozen and carries evidence, a failing run can log the exact probe numbers that produced it. That single property removes most of the "why did it go remote last Tuesday" archaeology from incident reviews.
Step 1 — Probe the remote seat before trusting it
The probe measures the transport path to the server seat, not model quality. Point it at whatever health endpoint the deployment exposes; the socket version below is the minimum viable substitute when no HTTP endpoint is published.
# lease.py (continued) — transport probe, five samples by default
def probe(samples: int = 5, timeout: float = 2.0) -> dict:
host = os.environ["SEAT_HOST"]
port = int(os.environ.get("SEAT_PORT", "443"))
latencies: list[float] = []
for _ in range(samples):
t0 = time.perf_counter()
try:
with socket.create_connection((host, port), timeout=timeout):
latencies.append((time.perf_counter() - t0) * 1000)
except OSError:
latencies.append(float("inf"))
ok = [x for x in latencies if x != float("inf")]
return {
"samples": samples,
"p50_ms": round(statistics.median(ok), 2) if ok else None,
"p95_ms": round(max(ok), 2) if ok else None,
"loss": round(1 - len(ok) / samples, 2),
"at": round(time.time()),
}
Illustrative output on a degraded link looks like {"samples": 5, "p50_ms": 88.1, "p95_ms": 1940.6, "loss": 0.2, "at": 1792471200}. The gap between p50 and p95 matters more than either number alone: a median of 88 ms with a 1.9 s tail is a link that will pass a smoke test and ruin a long run.
Step 2 — Turn constraints into invariants
Two constraints should outrank every measurement. If the payload contains secrets, or if the run must complete with no network, the lease goes local with a long expiry and no probe at all, because no amount of good latency changes the requirement.
SECRET_PATTERNS = [re.compile(p) for p in (
r"sk-[A-Za-z0-9]{16,}",
r"-----BEGIN [A-Z ]*PRIVATE KEY-----",
r"(?i)aws_secret_access_key\s*=\s*\S+",
)]
def looks_secret_bearing(payload: str) -> bool:
return any(p.search(payload) for p in SECRET_PATTERNS)
This pattern list is a coarse gate and nothing more. A dedicated redaction gate for the payload itself belongs upstream; the check here exists to catch the case where a secret appears mid-run, in turn seven, long after placement was settled.
Step 3 — Derive the expiry from measured latency
The expiry is the interesting part, because it determines how quickly a bad decision gets reconsidered. Short leases re-probe often and cost little; long leases save probe overhead and risk staleness. The table below is the rule set this module implements.
| Condition observed | Placement | Lease TTL | Evidence recorded |
|---|---|---|---|
| Payload secret-bearing | local | 3600 s | constraint flag, no probe |
| Run must survive offline | local | 3600 s | constraint flag |
| Probe loss at or above 34% | local | 90 s | samples, loss, timestamp |
| p95 above per-turn budget | local | 120 s | p95, budget |
| p95 at or below half the budget, zero loss | remote | 300 s | p95, loss |
| Everything else | remote | 120 s | p95, loss |
# lease.py (continued) — probe result becomes a lease
def decide(c: Constraints, now: float | None = None) -> Lease:
now = time.time() if now is None else now
if c.secret_free_local or c.offline_capable:
return Lease(Placement.LOCAL, now + 3600,
{"reason": "invariant", "probe": None})
p = probe()
if p["p95_ms"] is None or p["loss"] >= 0.34:
return Lease(Placement.LOCAL, now + 90, {"reason": "loss", **p})
if p["p95_ms"] > c.p95_budget_ms:
return Lease(Placement.LOCAL, now + 120, {"reason": "latency", **p})
ttl = 300 if p["p95_ms"] * 2 <= c.p95_budget_ms else 120
return Lease(Placement.REMOTE, now + ttl, {"reason": "measured", **p})
Note that a healthy link does not earn an unlimited lease. It earns five minutes, which is roughly the interval at which a moving laptop or a shared uplink can change its mind.
Step 4 — Check the lease at every turn boundary
Enforcement belongs at the boundary between turns, never inside a tool call. Migrating mid-call produces the duplicated-side-effect bug that takes a week to diagnose; letting the call finish locally or remotely and re-deciding afterwards does not.
# lease.py (continued) — per-turn enforcement
def next_lease(current: Lease | None, c: Constraints) -> Lease:
if current is not None and current.valid():
return current
return decide(c)
def run_turn(state, payload: str, c: Constraints) -> str:
lease = next_lease(state.lease, c)
if lease.placement is Placement.REMOTE and looks_secret_bearing(payload):
lease = Lease(Placement.LOCAL, time.time() + 3600,
{"reason": "secret_in_turn", "probe": None})
state.lease = lease
if lease.placement is Placement.REMOTE:
try:
return state.remote(payload)
except RemoteUnavailable as exc:
state.lease = Lease(Placement.LOCAL, time.time() + 90,
{"reason": type(exc).__name__, "probe": None})
return state.local(payload)
The state object is deliberately unspecified here, because the interesting contract is the lease, not the client library. Any existing local-first runner can adopt the four functions above behind its current dispatch point.
Step 5 — Revoke, do not restart
Three signals should revoke a lease immediately rather than wait for expiry. A secret appearing in the current turn revokes to local for an hour, because the constraint is structural. A transport exception revokes to local for ninety seconds, giving the link time to recover before the next probe. An explicit rate-limit response from the server revokes for at least the advertised backoff window, and the run stops rather than probing around the limit.
Clock handling deserves its own line. After a host suspend, wall-clock time can jump while monotonic time does not, so a lease that looks valid may be hours stale. Comparing time.monotonic() deltas against the lease TTL, and treating a mismatch as expiry, catches the resume case that a pure time.time() comparison hides.
A fault matrix worth running before you trust it
| Fault injected | Expected behaviour | Assertion |
|---|---|---|
| Link dropped mid-run | renewal probe fails, placement flips to local | no remote call after drop |
| Lease expires during a tool call | call completes, re-decision happens after | exactly one execution per turn |
| Secret appears in turn 7 | immediate revoke to local, one hour TTL | remote client untouched after turn 7 |
| Host suspend and resume | wall-clock jump treated as expiry | fresh probe on next turn |
| Server returns a rate-limit response | revoke and stop, no retry loop | request count stays flat |
The decision function itself is testable in milliseconds by replacing the probe, which keeps the suite fast enough to run on every commit.
import pytest, lease
@pytest.mark.parametrize("constraints,probe_result,expected", [
(lease.Constraints(True, False, 800), None, lease.Placement.LOCAL),
(lease.Constraints(False, True, 800), None, lease.Placement.LOCAL),
(lease.Constraints(False, False, 800),
{"p95_ms": None, "loss": 0.6}, lease.Placement.LOCAL),
(lease.Constraints(False, False, 200),
{"p95_ms": 640.0, "loss": 0.0}, lease.Placement.LOCAL),
(lease.Constraints(False, False, 900),
{"p95_ms": 120.0, "loss": 0.0}, lease.Placement.REMOTE),
])
def test_decide(constraints, probe_result, expected, monkeypatch):
if probe_result is not None:
monkeypatch.setattr(lease, "probe", lambda samples=5: probe_result)
assert lease.decide(constraints).placement is expected
The module and tests above were written for this article and exercised only against a stubbed probe; treat the probe target as the part you must adapt to your own deployment.
Where the free tier actually fits in the lease
The lease does not care which remote seat it points at, only whether that seat is reachable inside the budget. That is precisely why a free server option is useful as the remote side of a first lease: it makes the probe step cheap to run on real traffic, and the remote branch of the decision table can be exercised without a procurement conversation.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
MonkeyCode is described by its maintainers as an open source project, and the operator states that it offers free model access together with a free server option, with a ten-million-token allowance. Both are availability claims that can change without notice, so verify the current terms in the project's own materials before sizing a batch run against them; this article makes no claim about model names, hardware, throughput, or how long the offer lasts.
Placed inside the lease, the free server is the default remote branch for turns that carry no secrets and tolerate a measured p95. The free models are the reason the remote branch is worth probing at all for routine summarisation, classification, and drafting work, where a two-second tail is annoying rather than disqualifying.
Limitations and who should skip this
The probe measures transport, not output quality, so a lease can be perfectly healthy while the remote model produces worse results than the local one. Teams with contractual data-residency requirements need attested infrastructure, and a lease record is not an attestation. Anyone whose per-turn budget sits under about 50 ms on a LAN will find the lease flipping to remote and back for no operational benefit.
Single-host runners get the most from this pattern. Multi-tenant systems need a lease per tenant and per credential scope, which is a larger design than four functions. If the run is a one-shot script under thirty seconds, a static flag plus a timeout is genuinely the better engineering choice.
A reasonable next step is to implement only probe() and log its output for a week before wiring the decision in; the numbers will tell you whether your workload has the latency variance this lease exists to handle.
Top comments (0)