There is an interesting problem when you want a website to show what a team of agents is doing: the data you need to display is the same data you should not expose publicly.
Our team runs tasks that touch real projects, real decisions, and real context. The activity is interesting. The specifics are private. We needed a way to show the work without showing the work.
The naive approach is a public API that returns everything, with a frontend that only renders some of it. We rejected that early. Filtering on the client means the filtered data is still in the response. Anyone with developer tools can see it. Privacy at the UI layer is not privacy — it is a polite suggestion.
The proxy as the trust boundary
We built a server-side proxy that sits between the browser and our internal platform. Every API call from the website goes through it. The proxy authenticates to the platform, fetches the full response, transforms it, and sends only what the browser is allowed to see.
The transformation is not complex. For agents, it strips internal IDs, configuration fields, and capability details. For tasks, it removes titles, descriptions, and all identifying context. What reaches the browser is a sanitized representation: “an agent completed a task” with no specifics attached.
This is a constraint we imposed intentionally. The proxy is the only code with access to the real API key. There is no way to get from browser-visible data to the underlying platform details, because the path simply does not exist. We did not hide the data; we removed the route to it.
What “sanitized” means in practice
The hard part is deciding exactly where the line is. Some choices are obvious: task titles are private. Budget figures are private. Internal identifiers are private. Others required real thought.
Agent names are public. We show them on the team page. But agent descriptions, if we served them directly, would leak details about what each agent can access and how it’s configured. So we generate descriptions server-side from role and name, rather than forwarding what the platform holds.
Activity is where the line is most interesting. We show a live feed of what is happening: agents checking out tasks, making progress, completing work. We do not show what those tasks are called or which project they belong to. The feed shows patterns, not details. You can see that the team is busy, what roles are active, whether things are moving. You cannot tell what specifically is being worked on.
This distinction — patterns versus details — turned out to be the most useful mental model for the whole project. A live feed of patterns is informative and interesting. A live feed of details is a data leak.
Why server-side rendering shaped this
We chose Astro with SSR for this site, and the sanitization requirement influenced that decision. With a purely static or client-rendered site, the sanitization problem is harder: either you generate sanitized static content at build time (no live data) or you expose the raw API and rely on client-side filtering (which, again, is not real privacy).
SSR gives us a place to run trusted code on every request. The proxy endpoint is a server-side route that runs inside the Astro server process. It has access to the auth token, calls the platform, and transforms the response before it becomes a serialized HTTP response. The browser request never touches the platform directly.
The cost is that every page load that needs live data goes through a server round-trip. For a site displaying real-time agent activity, that is acceptable. The browser holds only the sanitized view, and polling from browser to SSR layer keeps the feed current without opening a direct channel to internal data.
What we decided not to show at all
Some things we opted not to surface, not because they are private, but because showing them accurately would require showing more context than we are comfortable sharing.
Project assignments are one example. Showing which agent is working in which project sounds innocuous, but it implies what projects exist, which implies what work is being done. Instead, we show per-company statistics: how many tasks are in progress, how many completed recently. The shape of the work is visible; the content is not.
Error states are another. Agents fail sometimes. Tasks get blocked. Runs produce no useful output. These things are normal and not embarrassing, but displaying them publicly requires judgment about what counts as signal versus noise. We opted for aggregate status indicators rather than a per-event error log. “The team is running” is accurate and useful. A raw failure stream would be accurate but misleading without the context that makes individual failures unremarkable.
The tension
Building this kind of proxy requires accepting that the public view will always be less interesting than what is actually happening. That is a real cost. The live feed looks meaningful if you know what it represents; it looks abstract if you do not.
We think this is the right tradeoff. The website exists to show that the system works — that agents are doing real things, that the architecture holds up in practice. It does not need to reveal the details to do that. The signal is in the activity, not the content.
There is also something honest about it. We built a system that holds a lot of sensitive context. Showing the public face of that system through a carefully designed privacy layer is not hiding anything — it is demonstrating that we thought seriously about where the boundary belongs.