DEV Community

Emery Li
Emery Li

Posted on

Pin the Secret, Ship the Scaffold: Artifact Residency for Local Agents

A backend engineer boarded a delayed evening train with a laptop that still held production .env files. The next task was a long test harness that needed no customer records and no private keys. The local model was already warm, yet chassis fans climbed after a day of compile loops. Public Wi-Fi appeared for a few minutes, vanished in a tunnel, and returned behind a captive portal.

That scene is not a routing puzzle about which remote model looks smarter on a leaderboard. It is a residency puzzle about which bytes may leave the machine during a flaky commute. Latency, secret material, and offline gaps pull in different directions on the same prompt pack. A single job label cannot express those three forces without hiding a dangerous file.

This article treats every intermediate artifact as an object with a residency tag before any hop. The walkthrough includes a runnable ledger, a decision table, and an honest list of cases that should stay local. A free remote server appears only after redaction, and only when the laptop is the wrong place to finish public work.

Job labels still leak files

Many local-first agent setups classify the whole task as local or cloud, then ship the entire packed prompt. That shortcut fails when one turn mixes a secret file list with a public scaffold request. The harness generator does not need the contents of .env sitting beside the README. The laptop also cannot assume the network will last long enough for a remote call.

The more durable unit of control is the artifact rather than the job title on a ticket. A path, a prompt chunk, a log excerpt, or a generated patch can carry its own tag. The agent may run locally for the secret slice and remotely for the public slice. Nothing in that split requires a claim about which model writes better tests.

Three residency classes

The ledger uses three tags only, because extra categories tend to collapse during later policy review.

  1. LOCAL_ONLY marks bytes that contain secrets, customer identifiers, or private keys without a safe redaction. Those artifacts never leave the host, including into builder logs on a machine the user does not control.
  2. REDACT_THEN_REMOTE marks mixed text where sensitive tokens sit beside useful structure for a generator. A deterministic redactor must run first, and only the redacted form may cross the network.
  3. REMOTE_OK marks public docs, generated tests, or license headers with no residual secret risk. Those bytes may go remote when local latency, heat, or a coming offline window makes a server hop worthwhile.

Offline behavior follows the tag instead of hope that a tunnel will end in the next few minutes. LOCAL_ONLY work stays on disk and waits for the laptop to be usable again. REDACT_THEN_REMOTE work may queue a redacted payload, and it must never queue the original. REMOTE_OK work may wait for the next network window or for a free server that remains reachable.

Decision table

Artifact example Signals on disk Tag Local action Remote action
.env, PEM files, SSH keys reserved names, key suffixes LOCAL_ONLY read in-process only refuse the hop
CI log with pasted tokens secret regex hits REDACT_THEN_REMOTE keep original local send redacted text only
README, public API docs no secret hits REMOTE_OK optional local draft allowed when local is slow
Unified diff under /tests public paths, clean text REMOTE_OK optional local draft allowed after a clean scan
Customer support dump emails, account identifiers LOCAL_ONLY summarize on the host refuse the hop

This table is a policy sketch, not a measured production matrix from a live fleet. Teams should replace the examples with their own path prefixes and secret scanners before any real traffic. When a classifier is uncertain, the default remains LOCAL_ONLY rather than a hopeful remote send.

Build a small residency ledger

The following Python module is a complete sketch that runs as a command-line preflight. It is a proposal: it was not executed against production traffic for this article, and any timings it prints are samples from the host that runs it.

Step 1 — Define tags and secret hints

# residency_ledger.py
from __future__ import annotations

from dataclasses import dataclass, asdict
from enum import Enum
from pathlib import Path
import hashlib
import json
import re
import time
from typing import Iterable

class Residency(str, Enum):
    LOCAL_ONLY = "LOCAL_ONLY"
    REDACT_THEN_REMOTE = "REDACT_THEN_REMOTE"
    REMOTE_OK = "REMOTE_OK"

SECRET_NAMES = {".env", ".env.local", "id_rsa", "id_ed25519", "credentials.json"}
SECRET_SUFFIXES = {".pem", ".p12", ".key"}
SECRET_DIR_PARTS = {".ssh", ".gnupg", "secrets"}

TOKEN_RE = re.compile(
    r"(?im)(?:api[_-]?key|secret|token|password|passwd|authorization)"
    r"\s*[:=]\s*\S+"
)
Enter fullscreen mode Exit fullscreen mode

Step 2 — Tag each path before the prompt is packed

@dataclass(frozen=True)
class Artifact:
    path: str
    tag: Residency
    reason: str
    sha256: str
    redacted: bool = False

def _sha(data: bytes) -> str:
    return hashlib.sha256(data).hexdigest()[:16]

def classify_path(path: Path) -> tuple[Residency, str]:
    name = path.name
    parts = {p.lower() for p in path.parts}
    if name in SECRET_NAMES or path.suffix.lower() in SECRET_SUFFIXES:
        return Residency.LOCAL_ONLY, "secret-filename"
    if parts & SECRET_DIR_PARTS:
        return Residency.LOCAL_ONLY, "secret-directory"
    try:
        text = path.read_text(encoding="utf-8", errors="replace")
    except OSError as exc:
        return Residency.LOCAL_ONLY, f"unreadable:{exc.__class__.__name__}"
    if TOKEN_RE.search(text):
        return Residency.REDACT_THEN_REMOTE, "secret-pattern"
    return Residency.REMOTE_OK, "clean-text"
Enter fullscreen mode Exit fullscreen mode

Step 3 — Redact with stable placeholders

Stable hashes keep a remote model oriented without restoring the secret value. The original file never enters the outbound payload, even when the hop chooser later selects a server.

def redact_text(text: str) -> str:
    def repl(match: re.Match[str]) -> str:
        token = match.group(0)
        digest = hashlib.sha256(token.encode("utf-8")).hexdigest()[:10]
        return f"[REDACTED_{digest}]"
    return TOKEN_RE.sub(repl, text)

def load_artifact(path: Path) -> tuple[Artifact, str]:
    tag, reason = classify_path(path)
    raw = path.read_bytes() if path.is_file() else b""
    text = raw.decode("utf-8", errors="replace")
    if tag is Residency.LOCAL_ONLY:
        body = ""  # never pack secrets
        redacted = False
    elif tag is Residency.REDACT_THEN_REMOTE:
        body = redact_text(text)
        redacted = True
    else:
        body = text
        redacted = False
    art = Artifact(str(path), tag, reason, _sha(raw), redacted)
    return art, body
Enter fullscreen mode Exit fullscreen mode

Step 4 — Choose a hop from latency, secrets, and offline state

The hop chooser does not score model quality or vendor ranking in any form. It only asks whether the packed body is allowed to leave, whether the network is up, and whether the local hop recently felt slow. The numeric threshold below is a placeholder for operators to replace on their own hardware.

@dataclass
class HopDecision:
    target: str  # local | remote | queue-local | queue-redacted
    reason: str
    artifacts: list[Artifact]

def probe_local_latency(sample_fn, repeats: int = 3) -> float:
    """Proposal helper: time a local completion function the caller supplies."""
    samples = []
    for _ in range(repeats):
        start = time.perf_counter()
        sample_fn()
        samples.append(time.perf_counter() - start)
    return sum(samples) / len(samples)

def choose_hop(
    artifacts: Iterable[Artifact],
    packed_body: str,
    network_up: bool,
    local_latency_s: float,
    local_slow_after_s: float = 8.0,
) -> HopDecision:
    arts = list(artifacts)
    if any(a.tag is Residency.LOCAL_ONLY for a in arts):
        return HopDecision("local", "secret-pin", arts)
    if not packed_body.strip():
        return HopDecision("local", "empty-payload", arts)
    remote_allowed = all(
        a.tag in {Residency.REMOTE_OK, Residency.REDACT_THEN_REMOTE}
        for a in arts
    )
    if not remote_allowed:
        return HopDecision("local", "mixed-forbidden", arts)
    if not network_up:
        if any(a.tag is Residency.REDACT_THEN_REMOTE for a in arts):
            return HopDecision("queue-redacted", "offline-redacted", arts)
        return HopDecision("queue-local", "offline-public", arts)
    if local_latency_s >= local_slow_after_s:
        return HopDecision("remote", "local-slow", arts)
    return HopDecision("local", "local-fast-enough", arts)
Enter fullscreen mode Exit fullscreen mode

Step 5 — Keep an append-only ledger on the host

LEDGER = Path(".agent_residency.jsonl")

def record(decision: HopDecision) -> None:
    line = json.dumps({
        "target": decision.target,
        "reason": decision.reason,
        "artifacts": [asdict(a) for a in decision.artifacts],
        "ts": time.time(),
    })
    with LEDGER.open("a", encoding="utf-8") as handle:
        handle.write(line + "\n")

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="Tag artifacts before any model hop")
    parser.add_argument("paths", nargs="+", type=Path)
    parser.add_argument("--network-up", action="store_true")
    parser.add_argument("--local-latency", type=float, default=1.5)
    args = parser.parse_args()

    loaded = [load_artifact(p) for p in args.paths]
    arts = [pair[0] for pair in loaded]
    body = "\n\n".join(
        pair[1] for pair in loaded if pair[0].tag is not Residency.LOCAL_ONLY
    )
    decision = choose_hop(arts, body, args.network_up, args.local_latency)
    record(decision)
    print(json.dumps({
        "target": decision.target,
        "reason": decision.reason,
        "artifacts": [asdict(a) for a in arts],
    }, indent=2))
    print("--- packed body (secrets stripped) ---")
    print(body[:2000])
Enter fullscreen mode Exit fullscreen mode

Run the preflight against a mixed folder before the agent starts packing context:

python residency_ledger.py README.md tests/test_api.py .env --network-up --local-latency 9.2
Enter fullscreen mode Exit fullscreen mode

The expected sketch result is a secret-pin decision whenever .env is in the path list, even if the README files are clean. Remove the secret path, raise local latency, and the same command can select remote for the remaining public files. That second run is the only moment a free server is in scope.

A small test plan for the ledger

Treat the next checks as a reproducible review, not as published benchmark numbers from a lab.

  1. Place a fake .env with API_KEY=example beside a public README.md, then run the command with --network-up and a high latency value. The target must stay local with reason secret-pin.
  2. Drop .env from the path list and keep only README.md. The packed body must contain README text, and a slow local probe may select remote.
  3. Put token: example inside notes.txt so the tag becomes REDACT_THEN_REMOTE. The packed body must contain [REDACTED_ and must not contain the original assignment.
  4. Repeat step 3 without --network-up. The target must be queue-redacted, and the working tree must still hold the unredacted file.
  5. Point the classifier at a binary .p12 name even if the file is empty. The tag must be LOCAL_ONLY from the suffix rule, not from file contents.

These checks keep the policy honest when someone later wires the ledger into an agent loop. They do not prove that the regex list will catch every credential format used in the wild.

Latency, secrets, and the offline gap

Local inference wins when the completion is short and the context is already in memory on the laptop. There is no handshake, no upload, and no extra copy of the prompt on a machine the user does not hold. Local inference also wins whenever the packed body still contains irreducible secrets, because a faster remote answer is not a valid answer. Secret residency beats latency in every row of the table above.

Remote inference wins in a narrower band that many agent tutorials skip. The laptop may be thermally constrained after compile loops, while the remaining work is a long public scaffold. The user may be about to close the lid, while a server can finish a redacted generation. The payload must already be REMOTE_OK or redacted, or the hop is simply a leak with extra waiting.

Offline policy is the third axis and should not be folded into a latency number. A disconnected host can keep writing LOCAL_ONLY summaries from disk, including commit messages that mention internal hostnames. It must not flush an unredacted queue the moment a coffee-shop portal appears. The queue-redacted target exists so original bytes stay in the working tree while a sanitized copy waits.

A free remote server is useful in that narrow window and not as a default home for the repository. The work is already public or redacted, the laptop will sleep or overheat, and finishing the scaffold elsewhere does not expand the secret radius. That is the opposite of sending the whole tree to whichever endpoint answers first.

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

MonkeyCode fits that window as a remote hop with free model access and a free server option, used only after the ledger says the packed body may leave. It does not replace local tagging, local secret pins, or an offline queue on the laptop. Readers who already keep secrets on-device can try that remote path on redacted public scaffolds rather than on the full working tree.

Limitations and who should skip this

The regex list is intentionally small and will miss cloud-specific credentials, identity tokens in binary files, and secrets split across wrapped lines. Filename rules will also miss a password pasted into notes.txt without a matching key name. Teams handling regulated data, production customer dumps, or hardware keys should keep a real secret scanner and a deny-by-default network policy.

The hop chooser does not measure thermal headroom, battery drain, or model quality, and it should not be cited as a benchmark. Anyone who needs guaranteed offline operation with no remote vendor at all should ignore the remote branch. Anyone whose prompts cannot be redacted without losing the task should stay local for the entire job.

Do not use this ledger to justify copying private repositories onto a shared server because a weak scan returned REMOTE_OK. The tag is only as strong as the classifier that produced it on that host. When classification is uncertain, the laptop keeps the bytes, and the free server does not get a vote.

What stays on the laptop

The useful split is not a culture war between local models and cloud models as identity badges. It is a file-by-file promise about latency, secrets, and the next network gap on a commute. Pin the secret artifacts, redact the mixed ones, and ship only the scaffold when a free server actually shortens the wait. The train can still arrive late, but the .env file never rides the public hop.

Top comments (0)