I like software built with care , healthy teams and puzzle pieces that fit
Software development has changed, and so should our development environments. While running AI agents with full access to your local machine feels empowering, it presents significant risks, including identity theft and accidental data destruction.
This post introduces a new tool for running terminal agents in the cloud. There are two primary benefits; isolation of your personal data and long-running sessions. You can find the new tool at github.com/adamtait/remote-agent
Agents and AI models have reached a tipping point around trust. For the past few years, models would unpredictably suggest foolish actions such as deleting a database or rm -rf /. They were untrusted. Today, models are much more capable of complex reasoning and reliable in carrying it out. Many users now want to employ AI models to do more (see the OpenClaw movement). But as we hand agents more power, the downside grows right alongside the capability.
Your personal computer is deeply personal. You access your email and social media accounts there. You do your banking and pay your taxes. Maybe you journal and record your private thoughts. You are probably careful not to share your personal computer account with other people. When we talk about identity theft, we’re often talking about information available from your personal computer.
Enter a powerful but unpredictable new productivity tool. Even without explicit permission and CLI command classifiers (see Claude Code auto mode), the risk that an LLM-as-agent could grab your local credentials and post them on the dark web is non-trivial. A prompt injection hidden in a GitHub issue or a branch name can make an agent run an attacker's commands inside its container — enough to steal a GitHub OAuth token or read your .env and ~/.aws/credentials. Even a local VM container is no guarantee. Frontier models have been shown to discover and execute container escapes on their own. As one security writeup put it: "Docker doesn't survive a model that's read its own CVE feed."
You might ask - how did we land on such a powerful default access point for AI agents? Early models started as chat bots (think ChatGPT) but modern LLM-based agents for software engineering were popularized by Cursor-like IDE integrations. The thinking was that developers live in their IDE and there are clear extension points (though many new extensions were added to VS Code for Github Copilot). Command line interface (CLI) or terminal agents came later, as reasoning models became capable. Builders reached for the terminal because it's ubiquitous, powerful, and (relatively) easy to build on. Many developers have turned a blind eye to these risks, driven by the desire to reduce productivity friction.
Beyond the risks, running agents (not to mention LLMs) on your personal machine is inconvenient. You have to keep your laptop open for the agents to keep running. One developer reportedly keeps a laptop open on the passenger seat of their car to monitor agents while stopped.
As agents run tools, they burn your machine's resources — CPU, memory, network. Your machine feels slow and you can't do that internet search or join a meeting. Peter Steinberger (creator of OpenClaw) got so tired of local agents heating up his CPU and slowing his whole machine that he built Crabbox to run them in the cloud instead.
While many have proclaimed the death of code, it's primarily the human-hand authoring of code in programming languages that has changed since the rise of LLM-based agents. Software engineers continue to design, architect and verify code changes that they delegate to agents. Many continue to use IDEs as they have been well designed for reviewing and verifying (in addition to authoring).
In my own work, GitHub's Pull Requests and IDEs (Cursor, Antigravity, VS Code, Emacs) are under heavy use in reviewing the changes and iterating.
With few exceptions, builders in big tech (Google, Meta, etc…) are working on cloud developer environments and have been for years. These organizations employ dedicated developer experience teams to maintain robust environments, enabling high-quality, secure workflows.
Fortunately, Google Cloud's Workstations are very similar to the environments that Google engineers work in. A VM running Google's code-oss container image with enough (configurable) power for developing most applications. The code-oss container comes baked with VS Code (web) so you can use a familiar IDE remotely (alternatively you can also tunnel Google's Antigravity from your local machine). For terminal agents, you can ssh into the machine and run them yourself.
Running on Google Cloud (aka GCP), you've got access to anything your organization or you personally need for safe and productive software development work. Further, you can use enterprise-trusted IAM and private VPC networks to control access and limit risks.
Google Cloud comes with a powerful but less user friendly permissions system (IAM). Fortunately, you can reduce the downside by using your favorite terminal agent to setup and maintain fine grained permissions access to your personal data (GMail, Drive, Docs) and GCP resources.
Most of the AI Labs and popular agent tools have solutions to run agents remotely.
Some distinguishing points:
The objective of Remote Agent is to ensure modern, AI-focused developers derive maximum value from terminal agents in a safe, remote, persistent environment that mirrors their own development environment. The design is simple and extensible - built on enterprise cloud development environments. Use the terminal agents you already know and love, and do it safely.
Clone the repo and make install.
ra setup and you get your own container and cloud configurationsra create creates a Cloud Workstation on Google Cloudra connect connects via ssh to your workstation.Its implementation is a Go terminal tool wrapping gcloud commands.
Remote Agent was designed with two types of extensions and the goal of allowing near complete customization of the packaging and live environment. The two types are configuration and plugins.
Configuration should be project or user-specific. You intend to keep these customizations scoped and do not need re-use.
Plugins are canonical and easily sharable.
The mental model of how Remote Agent works is the same for both configuration and plugins, which are simply collections of Dockerfile scripts, workstation startup and shell profile scripts. Configuration for a project is the source of truth (highest precedence) followed by the user-level configuration. Plugin scripts are included verbatim in either project or user-level configuration (after installation).
Configuration is centralized in two locations, and standard CLI tools ensure everything remains discoverable for both you and your agents. You & your agents also have complete power to fix or customize with local changes, then later decide to contribute upstream.
Remote Agent's extension mode shares some design cues from Claude Code such as a user-level configuration (kept in ~/.config/ra) and a project-level configuration (stored in a local .ra/). The project-level config can be sparse and overrides the user-level configuration. The user-level configuration contains the full Dockerfile and startup scripts so you can directly modify the scaffold that creates your remote development environment.
Plugins can be installed either from a remote GitHub repo or a local directory. Follow the prompts with ra plugin install .... The README has instructions for creating your own plugins, which LLMs can easily follow.
Versioning should be handled by your code; Remote Agent does not include a package manager. When a plugin is installed, the scaffolded scripts are verbatim copied into the project/user configuration directory.
The adamtait/ra-plugins repo comes with some batteries included plugins:
If you're in the Claude ecosystem, consider installing claude-sync which constantly syncs and merges your Claude conversation history across your workstations. Amazing for sharing conversation status across different agents or doing a single usage review across all your agent sessions. Conversation history is persistently stored in a Google Cloud Storage Bucket, which is created when you install the plugin. Plugin can be installed at either the user or project level.
Many remote services (like most cli agents) require authentication credentials. Remote Agent is designed to collect and store your secrets securely in Google Cloud. When you install a plugin requiring a secret, the CLI collects it and sends it directly to Google Cloud Secret Manager.
When provisioning a new machine, Remote Agent creates a Google Service Account which can access the secrets and associates the Service Account with the Cloud Workstation. Secrets are not baked into the container image and only accessed when the workstation runs.
The real cost isn't the workstation — it's the cluster. To run any Cloud Workstation you first need a Workstation Cluster Control Plane, and it bills \~$5 / day 24/7, whether or not a single workstation is awake. Auto-sleep and auto-shutdown do nothing for it; the only way to stop the charge is to delete the cluster (ra teardown). The Cluster resource includes a VPC network and gateway and stores/secures persistent disks, so it lives on even after you delete every Workstation.
By comparison the workstation itself is rounding error: a single e2-standard-2 adds about $1.34 / day of running charges, and drops to $0 the moment it auto-sleeps. Consolidate to one shared cluster and the control-plane fee stays flat no matter how many workstations you run under it. See the appendix for the full breakdown.
Your laptop is where your life lives. It's the worst possible place to run something that reads untrusted text and executes code on your behalf. The engineers building the biggest systems in the world stopped developing on their local machines years ago — and the tooling to do the same for your agents is (now) easily available.
Remote Agent gives you the control, maintains strong security, uses full developer environments, limits cost, frees you from your laptop and allows you to leverage your existing LLM subscription plans directly.
I had Claude report on my own usage by reviewing my Google Cloud billing data. After reading and starting to edit it, I decided it was written well enough to include verbatim. Enjoy -
e2-standard-2 (2 vCPU / 8 GB) in us-central1pd-balanced (persistent home disk)Billed per-minute only while running; all three components stop when it auto-sleeps.
| Component | Rate | Per running hour |
|---|---|---|
| Workstations management fee | $0.05 / vCPU-hr × 2 vCPU | $0.100 |
| Compute Engine VM (e2-standard-2) | \~$0.067 / hr | $0.067 |
| Total | \~$0.167 / running hour |
Average for a typical work day (\~8 running hours — active coding plus short idle tails before the 3 h auto-sleep kicks in):
| Scenario | Running hrs | Compute + mgmt cost |
|---|---|---|
| Light day | 4 h | \~$0.67 |
| Typical day | 8 h | \~$1.34 |
| Heavy (hits 10 h shutdown cap) | 10 h | \~$1.67 |
A typical work day ≈ $1.30–1.35 in running charges, plus the base below.
These bill continuously whether or not any workstation is awake.
| Resource | Size | Monthly | Per day |
|---|---|---|---|
pd-balanced home disk | 50 GB × $0.10/GiB-mo | $5.00 | $0.164 |
| Artifact Registry (Docker image) | \~3 GB × $0.10/GB-mo | \~$0.25 | \~$0.008 |
| Secret Manager | \~5–8 versions (partly free-tier) | $0–0.36 | \~$0.01 |
| GCS claude-sync bucket | \<1 GB standard | \~$0.05 | \~$0.002 |
| Workstations cluster control plane | $0.20/cluster-hr × 24 h | $144.00 / cluster | $4.80 / cluster |
| Total base (1 cluster) | \~$149/mo | \~$5.0 / day |
The cluster control plane fee dominates everything else. It is a fixed $0.20 per cluster per hour, billed 24/7 regardless of whether any workstation is running — auto-sleep and auto-shutdown do nothing for it. At one cluster that's \~$144/month ($4.80/day); observed June billing averaged \~$9/day, consistent with two clusters in us-central1 (2 × $4.80 \= $9.60/day). The only way to stop this charge is to delete the cluster itself. By comparison the 50 GB disk (\~$0.16/day) and everything else are rounding error, and the VM/management fees are $0 while asleep.
ra recreates it in a few minutes).ra teardown to stop the fee. ra teardown deletes ra-provisioned resources (workstations → config → cluster → service account/IAM → secrets → Artifact Registry repo) in one command; removing the cluster is what actually ends the control-plane charge. Run ra teardown --dry-run first to preview, then ra teardown. Because the cluster (and SA/repo/secrets) are shared across projects, teardown deletes them only when the project you run it from is the last config on the cluster; pass --delete-shared to force removal while other projects still use it. GCS buckets (synced data) are preserved unless you pass --delete-buckets.machine_type to e2-standard-8 for heavy builds roughly quadruples running cost (\~$0.67/running hr → \~$5.4 for an 8 h day).gcloud billing), or grant an account with billing.viewer.Published: 2026-07-03
In the popular imagination, the software engineer is a romantic hero who sits down at a glowing screen, cracks their knuckles, and immediately starts typing lines of code at a furious pace. They don't think; they just build.
In the real world, this is a recipe for building a system that runs beautifully, passes its tests, and solves entirely the wrong problem.
Rich Hickey’s talks have long been a guide for developers trying to escape this trap. He is famous for advocating "hammock time"—the practice of stepping away from the keyboard, closing your eyes, and actually thinking about the problem before writing a single line of code.
Hickey’s talk, Design in Practice, is a masterclass in how to formalize this process. It is the practical companion to his philosophical talks, offering a concrete structure for recording design decisions without drowning in corporate bureaucracy. It also serves as a beautiful elaboration on Architecture Decision Records (ADRs), a concept first popularized by Mike Nygard's 2011 post, which I still reference regularly.
Here are my key takeaways and reflections on the talk.
If you ask the average developer why they don't write design documents, they'll tell you they don't have time. They need to ship features.
But design isn't about satisfying auditors or writing massive Word documents that no one reads. Design is a cognitive savings account. The goals of formalizing design are intensely practical:
Hickey emphasizes that design is essentially a Socratic dialogue with yourself. It requires detaching your ego from your ideas. The goal isn't to prove your favorite solution is right; it's to discover the objective truth of the system.
He suggests framing your progress around four fundamental questions:
By continuously asking these questions, you engage in reflective inquiry—being aware of your own cognitive state.
Part of this discipline is linguistic precision. If two developers use the same word to mean different things, design fails. Hickey recommends compiling a glossary early in the process. Force yourself to define your terms. If you can't explain what a term means in two sentences, you don't understand it yet.
To turn these philosophical ideas into practice, Hickey breaks the software design and development process into six distinct, sequential phases:
graph TD
A[1. Describe] --> B[2. Diagnose]
B --> C[3. Delimit]
C --> D[4. Direction]
D --> E[5. Design]
E --> F[6. Dev]
E -.->|Backtrack if needed| C
Write down the situation as you hear it. Focus entirely on describing symptoms, not solutions. How big is the problem? What is the business impact? If a database is slow, don't say "we need Redis." Say "queries on the user table are taking 800ms, delaying checkout."
Accumulate hypotheses. There is never just one reason for a bug or bottleneck. List them all and use the scientific method to test them one by one. Prove your hypotheses with data, not vibes.
If you do steps 1 and 2 correctly, you will discover that you are facing a Hydra of multiple, interconnected problems. You cannot solve them all. Delimiting means drawing a hard boundary around the single problem you are going to solve right now.
List high-level approaches and evaluate their trade-offs. Hickey suggests putting these in a decision matrix: columns are your approaches, and rows are your criteria (e.g., development time, operational cost, legal compliance). This is where bad ideas go to die, cheaply and quickly, before they cost a single line of code.
Once you have chosen a direction, map out the implementation path. Choose your libraries, database schemas, and API contracts. If you discover during this phase that your assumptions were wrong, backtrack. It is infinitely cheaper to rewrite a design document than to refactor a production database.
Actually write the code. If you did phases 1 through 5 correctly, this should be the easiest, most mechanical part of the entire process. You aren't guessing or exploring; you are simply executing a well-defined blueprint.
Writing code is fun. Thinking is hard.
But as software systems grow in complexity, the developers who succeed are not the ones who type the fastest. They are the ones who know how to sit in the hammock, ask the right questions, and write their decisions down.
For more details, check out the video of the talk or read the full transcript.
Published: 2023-08-17
Tagged: talk clojure design richhickey
Every developer, at some point in their career, falls into the same seductive trap. You have some string input—maybe a custom config file format, a DSL for layout, or a set of nested parenthetical commands. You need to parse it.
You think: "I don't need a heavy grammar engine. I'll just write a quick regular expression and a split loop. It'll take ten minutes."
And it does. And it works beautifully. You feel like a wizard.
But then, the requirements change. You need to support nested brackets. You need to handle escaped quotes. Suddenly, that "quick regex" turns into a recursive, state-holding, back-tracking monster. You spend your weekends stepping through 300 lines of nested if/else statements, muttering dark oaths under your breath. You are trapped by the sunk-cost fallacy, unwilling to throw away the subsystem you’ve spent weeks debugging.
A hand-written parser is a financial loan shark: it offers incredibly easy terms upfront, followed by ruinous interest rates later.
If we want to build a better parser, we have to follow the classic Rich Hickey advice: let's decomplect it. We must untangle the three distinct duties that a parser implicitly performs:
f, u, n, c, t, i, o, n are recognized collectively as a single keyword token.Historically, computer scientists separated these concerns into specialized tools. In 1975, Mike Lesk and Eric Schmidt (who would later become the CEO of Google) wrote Lex, a lexical analyzer generator. It was typically paired with Yacc ("Yet Another Compiler-Compiler"), a parser generator.
For decades, CompSci students (including myself, fifteen years ago) were forced to learn these tools. It was dry, theoretical, and slightly painful. But the professors had a point: formal grammars are vastly superior to custom procedural parsing code.
Today, we don't have to use Lex and Yacc. We have tools like ANTLR 4 (Another Tool for Language Recognition), which combines lexer and parser definitions into a single, clean grammar file.
Here is a simple ANTLR 4 grammar for the ubiquitous CSV (comma-separated values) format:
csvFile: hdr row+ ;
hdr : row ;
row : field (',' field)* ' '? '\r'? '\n' ;
field : TEXT
| STRING
|
;
TEXT : ~[, "\r\n]+ ;
STRING : '"' ('""'|~'"')* '"' ;
In ANTLR, lexer rules (which define tokens) begin with an uppercase letter (TEXT, STRING), while parser rules (which define structure) begin with a lowercase letter (csvFile, hdr, row, field).
Look at how declarative this is. You don't write loops or search algorithms. You simply describe what a CSV file is. The generator takes this description and outputs highly optimized parsing code in Java, Clojure, Python, or JavaScript.
The trickiest part of designing your own language is ambiguity. An ambiguous language is one where a single string of characters can match more than one parser rule.
Consider this naive grammar for links:
link: '[[' STRING ']]' ;
alias: '[' STRING '](' STRING ')' ;
STRING: [a-zA-Z0-9 ]+ ;
What happens if the parser encounters [[alias](target)]?
Because both rules start with the open bracket [, the parser has to guess which path to follow. Without sufficient lookahead, it may commit to the link rule, proceed to look for ]], fail to find it, and throw a syntax error.
To avoid this, you should design your language syntax to be structurally unambiguous from the start. If that isn't possible, you can resort to lookahead markers or design your parser rules to explicitly allow optional characters to resolve the conflict. For example, using lookahead checks like ANTLR’s semantic predicates to check what characters follow.
If you want to escape the cycle of hand-written parser grief, I highly recommend:
Published: 2022-12-11