/projects/airia-cloud-connector

All projects

Airia Cloud Connector

A reverse tunnel that reaches into a private network without opening it.

R&D Engineer — security, routing and command surface · Jun 2025 – Oct 2025

.NET 9SignalRRedisJWTMCPxUnitTestcontainersHelm
Private
Airia Cloud Connector screenshot

Overview

A trimmed, single-file executable that runs inside a customer network and holds an outbound channel open to the cloud platform. Everything the platform needs on the other side of the firewall — an HTTP call, a database query, an MCP tool invocation — travels back down that one channel as a typed command.

What I did

I owned how the connector authenticates, how a request finds the right one, and what it is able to do once it gets there.

  • Mutual TLS between connector and hub — built, and switched off a week later.
  • Routing — resolving which connector in which customer group answers a given request.
  • The database command type, for both relational engines and document stores.
  • MCP support: listing an internal server’s tools and executing them through the tunnel.
  • Per-environment release packaging, and installation as a native Windows service.

The repository predates me by three months and several engineers shared it; the hub’s browser-agent surface is someone else’s work.

The problem

An enterprise buys a cloud AI platform, and then the agents it builds there need the systems that actually hold its data — a database, an internal API, an MCP server — all of which sit behind its firewall. The standard answers are a VPN, a site-to-site tunnel, or an inbound rule for the vendor’s address range, and each one asks a security team to open the perimeter for software it does not run. The connector inverts the direction instead: nothing dials in, so there is nothing to open.

Architecture

The connector opens a SignalR connection outward and registers itself under a customer group. The hub keeps that registry in Redis rather than in memory, so any hub instance can find any connector and correlate the reply — which is what lets the hub scale horizontally behind a load balancer. A platform request becomes a typed command envelope, is pushed down the connector’s channel, executed against whatever is on the private side, and the response is tracked back to the instance still holding the caller.

  1. Airia platformIssues an ordinary HTTP request, addressed to a customer group rather than to a host.
  2. Cloud HubWraps it as a typed command and looks up which connector should answer.
  3. Redis registryHolds the connector-to-instance map and the pending responses, so the hub can run as more than one replica.
  4. ConnectorReceives the command on the channel it already opened, from inside the customer network.
  5. Internal serviceThe API, database or MCP server that never became reachable from outside.

What travels down the channel

CommandWhat the platform asks for
HttpCall an internal API and return the response
DatabaseRun a query, or read the schema first
McpServerInfoList the tools an internal MCP server exposes
McpToolExecutionInvoke one of those tools by name
SystemInfoReport the connector’s own health and version
Adding a capability means adding a command type, not another proxy.

What it does

  • Outbound-only: the connector dials the cloud, never the other way round.
  • Integration tests run against a real Redis through Testcontainers, not a fake.
  • Queries relational engines and document stores on the private side, schema included.
  • Exposes an internal MCP server’s tools to the platform through the same tunnel.
  • Ships as one trimmed, self-contained executable, installable as a Windows service.

Engineering decisions

  • Invert the direction rather than open the perimeter

    A persistent outbound connection does everything an inbound rule would, and asks the customer for nothing their egress policy does not already allow. The security review this avoids is not a small one: it is the difference between a deployment a network team approves in an afternoon and one that spends a quarter in committee.

  • Mutual TLS, built and then switched off

    A bearer token proves the connector to the hub and does nothing to prove the hub to the connector, so client certificates went on both ends, with an explicit clock-skew allowance and a readable error in place of a raw handshake failure. It lasted a week. I merged the change that disabled it myself, the certificate requirement was dropped the next day, and the wiring is still commented out on both sides — the reason is not recorded anywhere I can point to, and I am not going to reconstruct one. What ships is bearer tokens over TLS. The honest lesson is not about the cryptography: a security control that a customer’s ops team has to hold up their end of is only as real as the certificate distribution nobody had built yet.

  • The connector registry lives in Redis, not in the hub’s memory

    A connector is attached to exactly one hub instance, but a platform request can land on any of them. Keeping the registry and the pending responses in Redis means the instance that receives a request can route it to the instance holding the connection, and the reply finds its way back. Without that, the hub is pinned to a single replica — a strange thing to accept in the one component every customer’s traffic passes through.

  • One command envelope instead of a proxy per capability

    HTTP came first, and databases and MCP could each have been a second tunnel with its own lifecycle. Making them command types on the existing channel meant authentication, routing, reconnection and response correlation were solved once. When MCP support was added, none of that had to be rebuilt — it was a new command type and a handler.

  • A trimmed single file, and the serializer that requires

    The connector is installed by someone else’s ops team on a machine nobody on the vendor side can log into, so it ships self-contained: no runtime to install, one file to copy, and later a native Windows service so it survives a reboot without a human. Trimming that binary breaks reflection-based JSON, which is why the command envelope is serialized through a source-generated context — an unglamorous constraint that follows directly from choosing a deployment the customer can actually operate.