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.
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, ...
Connic Bridge
Runs in your VPC
TLS encrypted
Connic Cloud
Connic Tunnel Endpoint
relay.connic.co
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
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.
Run the Connic Bridge
Deploy the bridge inside your private network. It needs to reach both your private services and the internet.
Docker:
docker run -d --name connic-bridge \
-e BRIDGE_TOKEN=cbr_your_token_here \
-e ALLOWED_HOSTS=kafka:9092,postgres:5432 \
connicorg/bridge:latestpip:
pip install connic-bridge
connic-bridge \
--token cbr_your_token_here \
--allow kafka:9092 \
--allow postgres:5432Docker Compose:
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: INFOOnce the bridge is online, connectors, LLM providers, custom code, and MCP servers can route through it. Configure each connection independently.
Where bridges are used
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:
- Apache Kafka (inbound and outbound)
- AWS SQS (inbound and outbound)
- PostgreSQL (inbound via LISTEN/NOTIFY)
- Email / IMAP / SMTP (inbound and outbound)
- AWS S3 (file downloads)
- HTTP Webhook (outbound callbacks)
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.
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:
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:
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.
{
"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.
exactmatches one hostname or IP.regexaccepts 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:
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.
- If the bridge agent has
ALLOWED_HOSTSconfigured, 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_hostnameas 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.
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.
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
| Variable | Required | Description |
|---|---|---|
BRIDGE_TOKEN | Yes | Bridge authentication token from the Connic dashboard |
ALLOWED_HOSTS | No | Optional comma-separated host:port allowlist. Unset or empty allows every network-reachable target. |
RELAY_URL | No | Connic tunnel endpoint (default: wss://relay.connic.co) |
LOG_LEVEL | No | DEBUG, 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
docker ps) and has outbound internet access. Verify the token is correct and has not been regenerated.ALLOWED_HOSTS environment variable (or --allow flag) of the bridge container and restart it.docker exec connic-bridge nc -zv kafka 9092).