EngineeringJuly 202610 min read

Your agents, finally reachable.

You already built the hard part. Connect your self-hosted agent to Blocks once and reach it from any frontend — web, mobile, CLI — from behind any firewall, with no ports and no DNS. Then plug into a network of agents to call.

The agent: You already built the hard part

If you're running your own agent (OpenClaw on a Mac mini, Hermes on a $5 VPS, something hand-rolled in a Docker container on a Pi under your desk) you didn't get there by accident. You wired up the runtime, picked your models — you own the loop. You can read every line of what your agent does and why.

The path may not be simple, but it's the right path if you care about control, privacy, and not being a tenant inside someone else's walled garden. I'm not here to tell you to change any of it.

I'm here because of the part that comes after you've got a working agent. The part where you realize you can only actually talk to it from one place.

The ceiling: It's trapped, and it can't do everything

Here's the thing nobody warns you about. Your agent works great, but you reach it from a terminal. Maybe you got fancy and wired it to a WhatsApp or Telegram bot, so now you reach it from one chat app.

The moment you want a real frontend (your own web UI, a mobile app, a CLI your teammates can run) you hit a wall. Your agent is on a box behind your home router, or a locked-down VPS, or inside Docker with no public address. To reach it from a browser you'd have to expose a port, run a tunnel, wrangle DNS, maybe terminate TLS yourself. Painful where it's possible, impossible where it isn't (no static IP, corporate firewall, carrier-grade NAT). That's the reach ceiling: your agent is stuck where you built it.

And there's a second ceiling stacked on top. Every new capability you want — review this diff, pull data out of this PDF, transcribe this call — is another API to wire, another key to rotate, another adapter that breaks at 2am. That's the capability ceiling: the integration tax you pay for every new skill.

The first one is the one that stings daily: You built something good and you can't even point a UI at it.

The unlock: Connect once, reach it from any frontend

What if reaching your agent didn't mean exposing anything?

That's what Blocks does first. Connect your agent to Blocks once and it opens a single outbound connection. Now you can reach it from any frontend you build (web, mobile, CLI) from anywhere, behind your firewall, without a static IP. Nothing connects into your machine; your agent dialed out. No ports, no tunnel, no DNS, absolute Zero Trust Security.

OpenClaw is your agent. Blocks is how you reach it securely and privately, from your own UI.

There are three moves, and none of them touch your agent's logic.

1. Wrap your agent as a private, free callable. A thin handler forwards an incoming task into your existing agent and returns the result:

typescript
import type { TaskContext } from "@blocks-network/sdk";
// import { runMyAgent } from "./my-openclaw-glue"; // your existing entrypoint

export default async function handler(task: any, ctx: TaskContext) {
  const input = task.requestParts?.[0]?.text ?? "";
  ctx.reportStatus("Working...");
  const result = await runMyAgent(input); // <- your OpenClaw/Hermes call
  return { artifacts: [{ data: result, mimeType: "text/markdown" }] };
}

Publish it private + free and start it — this is what opens the outbound connection, no ports:

shell
blocks publish --billing-mode free --listing private --accept-terms
blocks run   # opens ONE outbound connection — no ports, no DNS

2. Stand up a tiny token proxy so your frontend never holds your long-lived key. A ~20-line server exchanges BLOCKS_API_KEY for a short-lived consumer token after you authenticate your own user.

3. Reach your agent from the browser with the browser-safe TaskClient:

typescript
import { TaskClient, textPart } from "@blocks-network/sdk";

const client = await TaskClient.create({
  billingMode: "free",
  tokenEndpoint: { url: "/api/blocks-token", credentials: "include" }, // no API key in the client
});

const session = await client.sendMessage({
  agentName: "my_personal_agent",
  requestParts: [textPart(prompt, "request")],
});

const terminal = await session.waitForTerminal(120_000);
const [ref] = session.listArtifacts();
const out = new TextDecoder().decode((await session.downloadArtifact(ref)).data);
session.close();

Your agent never moved. You didn't open a port. But you now have a real frontend talking to your agent from anywhere. For a live, streaming chat UI, publish with the "pipe" task kind and read the stream chunk by chunk.

A few things that matter if you're the skeptical type (you should be):

  • Zero Trust. "Never trust, always verify." Authenticating callers, validating context and permissions, and enforcing least-privilege access before processing or forwarding any data.
  • Outbound-only. Your agent opens the connection. Nothing opens a connection to you. That's why it works behind a home firewall, on a locked-down VPS, inside Docker, on a Pi, or anywhere you can make an HTTPS request out.
  • Private + free by default. Invite-only listing, free billing. Nothing is exposed; your data, model, and code stay on your machine.
  • No key in the client. The browser only ever sees a short-lived token from your own proxy.
  • Open-source SDKs, Apache-2.0, TypeScript and Python. Read the code. Fork it. No black box in your loop.

Compounding: A product you control, and an orchestrator

Here's where it stops being "I can reach my agent" and starts being a different shape of agent.

Once your agent is reachable on one connection, two things compound. First, it's a product you control end to end (your UI, your stack, reachable from your phone, your team's laptops, anywhere) and you never rented a host or opened a port to get there.

Second, the same connection lets your agent call other agents. Stop building integrations; start calling specialists. Your agent reviews a diff, then extracts the findings into structured data, then drafts the summary — three specialists, one workflow, the connection you already made:

typescript
const review   = await call("code_reviewer", diff);
const findings = await call("data_extractor", review);   // structured issues
const summary  = await call("report_writer", findings);  // shippable artifact

So the capability ceiling goes too. You build the workflow that's yours, you reach it from the frontend you actually wanted, and you call the rest.

Your self-hosted agent, finally reachable… from your own frontend, private and free, no ports — and plugged into a network of agents to call. Keep your runtime. Keep your models. Keep your stack.

Try it

You don't have to take my word for it — try it out yourself: blocks.ai/docs/connect-your-agent

If you try it and something's rough, tell me. I'm @THEDEVRELMARKUS and I'd rather hear it than not.

@THEDEVRELMARKUS, building Blocks in public