Skip to main content
Connic
Platform

Bridge

Securely reach private services from anywhere in Connic: connectors, LLM providers, MCP servers, and your own tools and middlewares, without opening inbound firewall rules.

Last updated

What is the Connic Bridge?

The Connic Bridge is a lightweight agent that runs inside your private network and creates a secure outbound tunnel to Connic Cloud. Anything in your project (connectors, custom LLM providers, MCP servers, and your own tools and middleware) can then reach services that are not publicly accessible, as if they were running next to them.

Because the bridge only makes outbound connections, you do not need to open any inbound firewall rules or expose your services to the internet.

When do you need it?

You need the bridge if your target service is:

  • Inside a private AWS VPC, GCP VPC, or Azure VNet
  • Running on-premises behind a corporate firewall
  • Accessible only via private DNS or private IPs
  • Behind an IP allowlist that cannot include Connic's IPs

If your services are publicly reachable (e.g. managed Kafka on Confluent Cloud, AWS SQS via public endpoint), you do not need the bridge.

Connection Flow

Your Network

Private Services

Kafka, PostgreSQL, SQS, ...

↑ local TCP

Connic Bridge

Runs in your VPC

TLS encrypted

Connic Cloud

Connic Tunnel Endpoint

relay.connic.co

↑ routes traffic

Connectors, LLM Providers,
Tools & Middlewares, MCP Servers

All reach private services via the bridge

1. You deploy the Connic Bridge as a Docker container inside your network.

2. The bridge makes an outbound WSS connection to relay.connic.co (no inbound ports needed).

3. Connic sends connections for the selected bridge to the bridge running in your network.

4. If ALLOWED_HOSTS is configured, the bridge validates the target against that list. Without it, any target reachable from the bridge's network is allowed. The bridge then opens a local TCP connection and proxies the traffic.

5. The WSS connection encrypts traffic between the bridge and the Connic tunnel endpoint.

Setup Instructions

1

Create a Bridge

Go to Project Settings > Bridge and click Add Bridge. Give it a name (e.g. "Production VPC") and copy the token that is displayed. It will only be shown once. You can create as many bridges as you need, each with its own token, to reach different networks or environments.

2

Run the Connic Bridge

Deploy the bridge inside your private network. It needs to reach both your private services and the internet.

Docker:

terminal
docker run -d --name connic-bridge \
  -e BRIDGE_TOKEN=cbr_your_token_here \
  -e ALLOWED_HOSTS=kafka:9092,postgres:5432 \
  connicorg/bridge:latest

pip:

terminal
pip install connic-bridge

connic-bridge \
  --token cbr_your_token_here \
  --allow kafka:9092 \
  --allow postgres:5432

Docker Compose:

docker-compose.yaml
services:
  connic-bridge:
    image: connicorg/bridge:latest
    restart: always
    environment:
      BRIDGE_TOKEN: cbr_your_token_here
      ALLOWED_HOSTS: kafka:9092,postgres:5432,my-db:5432
      LOG_LEVEL: INFO

Once the bridge is online, connectors, LLM providers, custom code, and MCP servers can route through it. Configure each connection independently.

Where bridges are used

1

Connectors

When creating or editing a connector, choose which bridge to route through in the Bridge dropdown of the Network Access section. Leave it set to None to connect directly without a bridge.

The following connector types support bridge access:

2

LLM Providers

For private LLM endpoints (vLLM, Ollama, a LiteLLM proxy, or any OpenAI-compatible server), open Project Settings > LLM Provider, expand the custom provider, and pick a bridge in the Route via Bridge dropdown. Every LLM request from an agent that uses this provider is tunnelled through the bridge.

3

Tools & Middlewares

Code you write in your project (custom tools, middleware, tool hooks, and custom guardrails) can reach private services through a bridge hostname:

<target>.cnc-bridge-<bridge_id>

where target is the hostname of the service inside your private network (e.g. postgres-primary, kafka, billing) and bridge_id is copied from Project Settings > Bridge. Each bridge card has a copyable "Custom-tool host" field.

Connic routes connections that use this hostname pattern through the named bridge. Standard Python clients such as psycopg, aiokafka, httpx, requests, and redis-py can use the pattern directly:

tools/lookup_order.py
import psycopg
from aiokafka import AIOKafkaProducer
import httpx

BRIDGE_ID = "abc123"  # copy from Project Settings > Bridge

async def lookup_order(order_id: str) -> dict:
    # Postgres in a private VPC
    with psycopg.connect(
        host=f"postgres-primary.cnc-bridge-{BRIDGE_ID}",
        port=5432, dbname="orders", user="reader", password="...",
    ) as conn:
        row = conn.execute(
            "SELECT data FROM orders WHERE id = %s", (order_id,)
        ).fetchone()

    # Private Kafka topic
    producer = AIOKafkaProducer(
        bootstrap_servers=f"kafka.cnc-bridge-{BRIDGE_ID}:9092"
    )
    await producer.start()
    await producer.send("order-lookups", order_id.encode())
    await producer.stop()

    # Private HTTP service
    r = httpx.get(f"http://billing.cnc-bridge-{BRIDGE_ID}/v1/orders/{order_id}")
    return {"row": row, "billing": r.json()}

You can also import a small helper if you prefer explicit code over string concatenation:

tools/example.py
from connic import bridge_host

host = bridge_host("abc123", "postgres-primary")
# -> "postgres-primary.cnc-bridge-abc123"

Automatic routes for discovered endpoints

Some protocols connect to an initial service and receive a different hostname or IP for the next connection. Redis Sentinel, Kafka metadata, and database failover clients commonly work this way. When you cannot add the magic suffix to that returned destination, configure automatic routes under Project Settings > Bridge, or with the authenticated GET/PUT /v1/projects/{project_id}/bridges/{bridge_id}/routes API. API keys need bridges.update for both operations.

bridge-routes.json
{
  "routes": [
    {
      "match_type": "exact",
      "target": "redis-sentinel.internal",
      "port": 26379
    },
    {
      "match_type": "regex",
      "target": "^redis-[a-z0-9-]+\\.internal$",
      "port": 6379
    }
  ]
}
  • Each bridge can have up to 32 routes, with 256 automatic routes total across a project. Every route matches a hostname or IP and one required TCP port.
  • exact matches one hostname or IP. regex accepts a safe, anchored ^...$ subset with at most one quantified character class, such as [a-z0-9-]+. Groups, lookarounds, backreferences, alternation, braces, unescaped dots, and broad .* patterns are rejected.
  • Explicit .cnc-bridge-<bridge_id> hostnames take priority, followed by exact routes and then regex routes. Matches across different bridges fail closed.
  • Bridge list and detail responses include route_count. Read route definitions from the dedicated routes endpoint.

Redis Sentinel example

Route the Sentinel service on port 26379 and the possible master hostnames on port 6379, then use the ordinary hostnames in redis-py:

tools/redis_health.py
from redis.sentinel import Sentinel

sentinel = Sentinel(
    [("redis-sentinel.internal", 26379)],
    socket_timeout=1,
)
redis = sentinel.master_for("orders-primary", socket_timeout=1)

# The master hostname returned by Sentinel is routed automatically.
value = redis.get("health")

Routing and authorization are separate. If ALLOWED_HOSTS is configured, it must list the Sentinel and every possible master exactly, for example ALLOWED_HOSTS=redis-sentinel.internal:26379,redis-1.internal:6379,redis-2.internal:6379. If Sentinel announces IP addresses, use exact IP routes or a narrowly scoped IP regex instead of the hostname pattern above.

Notes
  • If the bridge agent has ALLOWED_HOSTS configured, the target host:port must be in that list. Unset or empty allows every target reachable from the bridge's network.
  • Automatic routes preserve the original hostname for TLS verification. When you use an explicit magic hostname, configure SNI / server_hostname as the real target if the client verifies certificates.
  • Automatic routing supports standard synchronous sockets and asyncio TCP connections. Custom native resolvers, libraries built on aiodns, and later background-thread connections require the explicit bridge hostname.
4

MCP Servers

Private MCP servers that run inside your VPC, on-prem, or behind a corporate firewall can be reached by setting the bridge field on the server entry in your agent YAML. Connic tunnels the Streamable HTTP connection through the bridge and negotiates MCP 2026-07-28 or a supported earlier revision with the server.

agents/agent.yaml
mcp_servers:
  - name: internal-mcp
    url: http://mcp.internal:8080/mcp
    bridge: ${INTERNAL_BRIDGE_ID}

If the bridge agent has ALLOWED_HOSTS configured, it must include the MCP server's host:port. See Private MCP Servers via Bridge for details.

Configuration Reference

VariableRequiredDescription
BRIDGE_TOKENYesBridge authentication token from the Connic dashboard
ALLOWED_HOSTSNoOptional comma-separated host:port allowlist. Unset or empty allows every network-reachable target.
RELAY_URLNoConnic tunnel endpoint (default: wss://relay.connic.co)
LOG_LEVELNoDEBUG, INFO, WARNING, or ERROR (default: INFO)

Security

  • Outbound-only - the bridge never accepts inbound connections. No ports need to be opened.
  • Optional allowed hosts - configure exact host:port values to restrict what the bridge can reach. Unset or empty allows every target available from its network.
  • Token authentication - each bridge has its own token tied to a single Connic project. Tokens can be rotated at any time, and you can run multiple bridges in different networks for the same project.
  • TLS encryption - communication between the bridge and the Connic tunnel endpoint uses WSS (WebSocket over TLS).

Troubleshooting

Check that the bridge container is running (docker ps) and has outbound internet access. Verify the token is correct and has not been regenerated.

The connector references a bridge that is not currently connected. Start the matching Connic Bridge agent in your network, or change the connector's Bridge dropdown to a different bridge or to None.

The bridge rejected the connection because the target host:port is not in the allowed hosts list. Add it to the ALLOWED_HOSTS environment variable (or --allow flag) of the bridge container and restart it.

The bridge is connected to Connic but cannot connect to the target service. Verify that the bridge container can reach the target host:port from within its network (e.g. via docker exec connic-bridge nc -zv kafka 9092).