mirror of
https://github.com/fosrl/docs-v2.git
synced 2026-09-26 15:59:07 +02:00
port mintlify to fumadocs
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
---
|
||||
title: "How Pangolin Works"
|
||||
description: "Learn about the fundamentals of Pangolin and how they work together."
|
||||
---
|
||||
|
||||
This page explains Pangolin's concepts: the server, sites, resources, users, and clients. Read it first. For a more technical picture of the software components and how they interact, see [System Architecture](/development/system-architecture) after this page.
|
||||
|
||||
## Basic Steps
|
||||
|
||||
<Steps>
|
||||
<Step title="Make remote networks available with sites">
|
||||
Access remote networks using site connectors. Pangolin's lightweight connectors use intelligent routing and NAT traversal to make any network anywhere available. Once a site is created, you can access resources on that network from anywhere.
|
||||
</Step>
|
||||
|
||||
<Step title="Define resources">
|
||||
Create resources for the things users should reach on the network of your site. Each resource follows a protocol: HTTPS in the browser, an IP or network range over the client, SSH, remote desktop, LLM access, and more. Resources can be public or fully private.
|
||||
</Step>
|
||||
|
||||
<Step title="Users access resources">
|
||||
Authenticated users access resources via the protocol of the resource. Commonly, public resources are accessed through a web browser, while private resources are accessed through a Pangolin client. The same users, roles, and policies apply across both. Users don't think about connecting to a site, they just access resources and Pangolin routes to the right connector automatically.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Pangolin relies on several components that work together to provide secure networking. Each component has a specific role in ensuring that only authenticated users can access the resources they are authorized to use.
|
||||
|
||||
### Pangolin Server
|
||||
|
||||
The Pangolin server is the central coordination component for your network. It stores configuration changes, manages access policies, and coordinates connections between clients and sites. The server handles user authentication, generates access control lists that determine what resources each user can reach, hosts the UI and API, and more. It is the brains of your Pangolin network.
|
||||
|
||||
You can use [Pangolin Cloud](https://app.pangolin.net/auth/signup), which is fully managed, or you can self-host your own Pangolin server for complete control over your infrastructure and data. See [Pangolin Cloud vs. Self-Hosted](/about/pangolin-cloud-vs-self-hosted).
|
||||
|
||||
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
|
||||
Fastest way to get started with the fully managed control plane.
|
||||
</Card>
|
||||
|
||||
<Card title="Read about how to self-host Pangolin" icon="server" href="/self-host/quick-install">
|
||||
Learn how to deploy your own self-hosted Pangolin server.
|
||||
</Card>
|
||||
|
||||
### Sites
|
||||
|
||||
Sites connect remote networks to your Pangolin server. They use Pangolin Site connectors (sometimes referred by their engineering name, "Newt") to create secure tunnels from remote networks back to Pangolin. Sites let you expose [resources](/manage/resources/understanding-resources) on those networks to authorized users.
|
||||
|
||||
Sites are an expected to entirely be managed by administrators and are typically set-and-forget. Users don't need to know about sites, they just access resources that are available on the site.
|
||||
|
||||
<Frame caption="Manage Sites in the Pangolin dashboard, with status, uptime, and resource counts per site.">
|
||||
<img src="/images/sites.png" alt="Manage Sites page in the Pangolin dashboard"/>
|
||||
</Frame>
|
||||
|
||||
Sites run behind firewalls on remote networks. They maintain outbound connections to the Pangolin server. By default, sites block all traffic until you define resources and grant access. This ensures that just [deploying a site](/manage/sites/install-site) does not expose any network resources.
|
||||
|
||||
<Frame caption="Creating a site: pick a platform, copy the install command, and run the connector on the remote network.">
|
||||
<img src="/images/create-site.png" alt="Create Site page in the Pangolin dashboard"/>
|
||||
</Frame>
|
||||
|
||||
When [private resources](/manage/resources/understanding-resources#private-resource-types) (VPN-like access) are used, [clients](/manage/clients/understanding-clients) connect directly to the site connector using peer-to-peer (P2P) [NAT traversal](/manage/clients/nat-traversal). If the client is on the same network as the site connector, it will use the [local network address](/manage/clients/nat-traversal#same-network-detection).
|
||||
|
||||
The site connector is very intelligent and handles tunnel creation, NAT traversal, and routing. It makes remote networks available without requiring complex firewall rules or public IP addresses. They also unlock browser-based [SSH](/manage/resources/public/ssh), [RDP](/manage/resources/public/rdp), and [VNC](/manage/resources/public/vnc) resources, [private HTTP](/manage/resources/private/private-http) with edge TLS termination, intelligent [multi-site routing](/manage/resources/private/multi-site-routing) when the same resource is reachable from more than one location, and much more.
|
||||
|
||||
<Card title="Read more about sites" icon="plug" href="/manage/sites/understanding-sites">
|
||||
Learn about sites, how they work, and how to install and configure them.
|
||||
</Card>
|
||||
|
||||
### Resources
|
||||
|
||||
Pangolin is resource-based. A resource is the unit of access: you define it, grant users and roles, and Pangolin routes only authorized traffic. Users connect to resources, not to sites.
|
||||
|
||||
Resources follow different protocols depending on what you are exposing (for example, but not limited to):
|
||||
|
||||
- An HTTPS app, available in the browser
|
||||
- An IP address, reached through the Pangolin client
|
||||
- A network range, reachable through the Pangolin client
|
||||
- An [AI Gateway](/manage/ai/overview) resource for LLM access
|
||||
- Remote desktop
|
||||
- SSH
|
||||
|
||||
The access model stays the same while the protocol changes.
|
||||
|
||||
[Public resources](/manage/resources/understanding-resources#public-resource-types) are publically available proxies to your Pangolin server. Often, these are exposed as a public FQDN, like a website available in the web-browser or an API.
|
||||
|
||||
<Frame caption="Public resources in the dashboard: HTTPS, SSH, RDP, and VNC with health, uptime, and access URLs.">
|
||||
<img src="/images/public-resources.png" alt="Manage Public Resources page in the Pangolin dashboard"/>
|
||||
</Frame>
|
||||
|
||||
[Private resources](/manage/resources/understanding-resources#private-resource-types) require a [Pangolin client](/manage/clients/install-client) connection and stay off the public internet. They are for VPN-like, fully private access to resources on your remote network.
|
||||
|
||||
<Frame caption="Private resources in the dashboard: hosts, HTTP, SSH, and CIDR ranges with destinations and aliases.">
|
||||
<img src="/images/private-resources.png" alt="Manage Private Resources page in the Pangolin dashboard"/>
|
||||
</Frame>
|
||||
|
||||
You must define resources and assign access before users can reach them. By default, no resources are available on sites. This ensures that only explicitly defined resources can be accessed.
|
||||
|
||||
#### Resource Launcher
|
||||
|
||||
The Resource Launcher is the dashboard your users (non-admins) see. It lists every resource they are allowed to access in one place, grouped by site or label, with search and filters. Users open a web app, copy a hostname, or launch a private resource from that hub.
|
||||
|
||||
<Frame caption="The Resource Launcher, grouped by site, showing the resources a user can open.">
|
||||
<img src="/images/resource-launcher.png" alt="Resource Launcher in the Pangolin dashboard"/>
|
||||
</Frame>
|
||||
|
||||
Selecting a resource always expands a panel with the details that matter for that type: URL or alias, how to connect, and any extra setup. The screenshot below is an [AI Gateway](/manage/ai/overview) example. The same panel is used for every resource.
|
||||
|
||||
<Frame caption="Resource Launcher detail panel. This example shows a private SSH resource.">
|
||||
<img src="/images/resource-launcher-expanded.png" alt="Resource Launcher detail panel showing a private SSH resource as an example."/>
|
||||
</Frame>
|
||||
|
||||
<Card title="Resource Launcher" icon="grid-2" href="/manage/resource-launcher">
|
||||
Find, launch, and save views of the resources you can access.
|
||||
</Card>
|
||||
|
||||
<Card title="Read more about resources" icon="link" href="/manage/resources/understanding-resources">
|
||||
Learn about public and private resources and how to create them.
|
||||
</Card>
|
||||
|
||||
### Users and Roles
|
||||
|
||||
Identity lives in the same control plane as sites and resources. You manage your team in one place: Pangolin users, [identity providers](/manage/identity-providers/add-an-idp), and the [roles](/manage/access-control/create-user#roles) that grant access.
|
||||
|
||||
Use Pangolin's built-in users, or bring Google Workspace, Microsoft Entra ID, Okta, or any OIDC provider. Users authenticate once. That identity applies to the dashboard, browser resources, the Pangolin client, the AI Gateway, and everything else.
|
||||
|
||||
Roles group people for RBAC. You assign roles on each resource, so access follows the team rather than a separate list per protocol. A user's effective access is the union of what their roles can reach. The same users and roles apply to public resources, private resources, and AI Gateway resources.
|
||||
|
||||
<Frame caption="Users in the dashboard, with identity providers and roles managed together.">
|
||||
<img src="/images/users.png" alt="Manage Users page in the Pangolin dashboard"/>
|
||||
</Frame>
|
||||
|
||||
<Card title="Users and Roles" icon="users" href="/manage/access-control/create-user">
|
||||
Add internal or external users and assign roles for resource access.
|
||||
</Card>
|
||||
|
||||
<Card title="Add an Identity Provider" icon="key" href="/manage/identity-providers/add-an-idp">
|
||||
Let users sign in with Google, Microsoft Entra ID, Okta, or any OIDC provider.
|
||||
</Card>
|
||||
|
||||
### Clients
|
||||
|
||||
Clients are software components installed on user devices or machines. They let users and automated systems connect directly to sites to access [private resources](/manage/resources/understanding-resources#private-resource-types) through a secure tunnel. Clients also enforce access control and security at the edge.
|
||||
|
||||
Users authenticate through the client using their [accounts](/manage/access-control/create-user). [Machines](/manage/clients/credentials) connect with credentials. Once connected, users can reach all resources their account has access to. The client handles [routing](/manage/clients/nat-traversal) decisions and establishes encrypted tunnels to the appropriate [sites](/manage/sites/understanding-sites).
|
||||
|
||||
<Frame caption="User devices in the dashboard, with identity provider, connection status, and client version.">
|
||||
<img src="/images/user-devices.png" alt="User Devices page in the Pangolin dashboard"/>
|
||||
</Frame>
|
||||
|
||||
Clients are available on [all major platforms](/manage/clients/install-client). They work transparently with applications, so no application configuration is required.
|
||||
|
||||
<Card title="Download Pangolin clients" icon="download" href="https://pangolin.net/downloads" arrow="true">
|
||||
Get the client for Mac, Windows, Linux, iOS, and Android.
|
||||
</Card>
|
||||
|
||||
<Card title="Read more about clients" icon="desktop" href="/manage/clients/understanding-clients">
|
||||
Learn how user and machine clients connect to private resources.
|
||||
</Card>
|
||||
|
||||
### AI Gateway
|
||||
|
||||
An [AI Gateway](/manage/ai/overview) is a special resource type for LLM access. It is protocol-aware, in the same way an HTTPS resource understands HTTP.
|
||||
|
||||
<Frame caption="Creating a public resource with type set to AI Gateway.">
|
||||
<img src="/images/ai/create-ai-gateway-resource.png" alt="Create Public Resource form with Type set to AI Gateway"/>
|
||||
</Frame>
|
||||
|
||||
Pangolin already inspects HTTPS traffic to apply identity, access rules, [request logs](/manage/analytics/request), and analytics. An AI Gateway resource does the equivalent for model APIs. Because the gateway understands the LLM protocol, it can attribute each call to a user, record [cost and token usage](/manage/ai/usage-analytics), keep [chat session history](/manage/ai/session-logs), and enforce who can use which [models](/manage/ai/providers/model-routing) and [budgets](/manage/ai/budgets).
|
||||
|
||||
Access is identity-based. You grant users and roles on the resource the same way you do for HTTPS or SSH. Pangolin then decides which models that identity may call and whether a budget still allows the request.
|
||||
|
||||
<Frame caption="AI Gateway session logs, showing provider, model, resource, and the user who made the call.">
|
||||
<img src="/images/ai/expanded-session-logs.png" alt="AI Gateway session logs in the Pangolin dashboard"/>
|
||||
</Frame>
|
||||
|
||||
A [private AI Gateway](/manage/resources/private/ai-gateway) uses the [Pangolin client](/manage/clients/install-client) the same way every other private resource does. The client running on the end user's device already authenticated that user. Coding agents on that device call the resource over the tunnel, and Pangolin attributes the request to the connected identity. That eliminates provider API keys on the laptop: the upstream key stays on the [provider](/manage/ai/providers/overview).
|
||||
|
||||
<Card title="Read more about AI Gateway" icon="sparkles" href="/manage/ai/overview">
|
||||
Set up providers, resources, and identity-based access for coding agents and AI clients.
|
||||
</Card>
|
||||
|
||||
### Remote Nodes
|
||||
|
||||
Remote nodes are self-hosted Pangolin servers that you control while using Pangolin Cloud for management and coordination. You maintain complete control over your infrastructure and data flow, while the cloud handles the control plane, DNS, certificate management, and backups.
|
||||
|
||||
You can deploy multiple remote nodes for high availability and automatic failover. If your nodes become unavailable, traffic can optionally fail over to cloud infrastructure until you restore service.
|
||||
|
||||
<Card title="Read more about remote nodes" icon="circle-nodes" href="/manage/remote-node/understanding-nodes">
|
||||
Learn about remote nodes and how they provide high availability and simplified operations.
|
||||
</Card>
|
||||
|
||||
## System Architecture
|
||||
|
||||
For a more technical picture of the software components and how they interact, see [System Architecture](/development/system-architecture).
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: "Cloud vs. Self-Hosted"
|
||||
description: "Choose managed Pangolin Cloud or run the same software on your own infrastructure"
|
||||
---
|
||||
|
||||
You can run Pangolin as [Pangolin Cloud](https://app.pangolin.net/auth/signup) or [self-host](/self-host/quick-install) the same open-source software. Sites, resources, and clients work the same way in both. The difference is who operates the server: the control plane, DNS, certificates, and the nodes that terminate tunnels.
|
||||
|
||||
## Pangolin Cloud
|
||||
|
||||
Cloud is the managed control plane. You create an account, install [sites](/manage/sites/install-site) and [clients](/manage/clients/install-client), and define resources. Pangolin runs the dashboard, database, certificates, and globally distributed nodes.
|
||||
|
||||
Use Cloud when you want high availability, automatic updates, and less operational work. You can still keep traffic on infrastructure you control with [remote nodes](/manage/remote-node/understanding-nodes).
|
||||
|
||||
## Self-Hosted
|
||||
|
||||
Self-hosted Pangolin runs on a server you operate. You supply a public IP, a domain, and the listener ports. See [Quick Install](/self-host/quick-install) and [DNS and Networking](/self-host/dns-and-networking). You are responsible for updates, backups, certificates, and uptime.
|
||||
|
||||
Two editions:
|
||||
|
||||
- **Community Edition** is the core platform.
|
||||
- **[Enterprise Edition](/self-host/enterprise-edition)** uses the `ee` image and a license key. It unlocks extra identity, logging, and management features. Compare tiers on the [Self-Hosted pricing page](https://pangolin.net/pricing#Self-Hosted-identity-and-access-management).
|
||||
|
||||
<Check>
|
||||
Enterprise Edition is **free** for personal use and organizations with less than $100,000 USD gross annual revenue. You still need a license key. Larger organizations need a paid license.
|
||||
</Check>
|
||||
|
||||
## Remote Nodes (Hybrid)
|
||||
|
||||
[Remote nodes](/manage/remote-node/understanding-nodes) are a middle path but are part of Pangolin Cloud. You run the node (tunnels, TLS, relay) on your server and bandwidth. Cloud still handles the dashboard, DNS, certificates, and failover. If your node goes down, traffic can fail over to other nodes or cloud points of presence.
|
||||
|
||||
Public TCP/UDP listeners that bind a port on the Pangolin host are a self-hosted-server feature. On Cloud, deploy a remote node for that. See [TCP / UDP](/manage/resources/public/raw-resources).
|
||||
|
||||
## Quick Comparison
|
||||
|
||||
| | Pangolin Cloud | Self-hosted |
|
||||
|---|---|---|
|
||||
| **Control plane** | Managed | You run Pangolin, Traefik, and Gerbil |
|
||||
| **Traffic path** | Cloud nodes, or your [remote nodes](/manage/remote-node/understanding-nodes) | Your server |
|
||||
| **DNS and certificates** | Managed | You configure DNS and Let's Encrypt |
|
||||
| **Updates and backups** | Included | You operate them |
|
||||
| **High availability** | Included | You design it ([clustering](/self-host/clustering/understanding-clustering) on Enterprise) |
|
||||
| **Identity and logs** | Cloud plans | Community core; extra IdPs, streaming, and AI session logs on [Enterprise Edition](/self-host/enterprise-edition) |
|
||||
| **Data residency** | Cloud regions, or traffic on your remote nodes | Entirely on your infrastructure |
|
||||
| **Best for** | Fast start and less ops | Air-gapped networks, full control, compliance that requires self-hosting |
|
||||
|
||||
Cloud and self-hosted plans are listed separately on [pricing](https://pangolin.net/pricing). Click "Self-Hosted" at the top to toggle between deployment options.
|
||||
|
||||
## When to Use Each
|
||||
|
||||
Use **Pangolin Cloud** if you want to start quickly, get HA and certificate management included, and optionally attach remote nodes so tunnel traffic stays on your network.
|
||||
|
||||
Use **self-hosted** if the control plane and data must stay on infrastructure you operate, you need an air-gapped or fully isolated deployment, or you already run Pangolin as Community or Enterprise Edition.
|
||||
|
||||
<Card title="Start on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up">
|
||||
Managed control plane. Install sites and clients, then define resources.
|
||||
</Card>
|
||||
|
||||
<Card title="Self-host Pangolin" icon="server" href="/self-host/quick-install">
|
||||
Install Community Edition or Enterprise Edition with the quick installer.
|
||||
</Card>
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: "Pangolin vs. Bifrost vs. LiteLLM"
|
||||
description: "What are the similarities and differences between Pangolin's AI Gateway and traditional LLM gateways like Bifrost and LiteLLM?"
|
||||
---
|
||||
|
||||
Pangolin's [AI Gateway](/manage/ai/overview) does the work you would expect from a traditional LLM gateway: one URL in front of many providers, model routing, usage tracking, and access control. The difference is how it is built. It is a protocol-aware Pangolin resource, so identity, tunnels, and governance are the same system you already use for HTTPS and SSH.
|
||||
|
||||
## Why Use a Gateway
|
||||
|
||||
Coding agents and AI clients want a provider URL and a key. If every laptop holds the org's OpenAI, Anthropic, or Gemini key, you cannot tell who spent what, you cannot limit which models a contractor may call, and rotating a leaked key means touching every machine.
|
||||
|
||||
An AI gateway sits in front of those upstream APIs. Clients call the gateway. The gateway authenticates the caller, picks a provider and model, records usage, and forwards the request with the org's real key. LiteLLM and Bifrost are dedicated products for that job. Pangolin does it as an identity-aware resource on the same platform as the rest of your application and infrastructure access.
|
||||
|
||||
## A Protocol-Aware Resource
|
||||
|
||||
An [AI Gateway resource](/manage/ai/overview) is not a separate stack. It is a Pangolin resource type, the same way HTTP/HTTPS is a type for web apps and SSH is a type for terminals.
|
||||
|
||||
Pangolin already inspects HTTPS to apply identity, access rules, [request logs](/manage/analytics/request), and analytics. An AI Gateway resource does the equivalent for model APIs. Because it understands the LLM protocol, it can attribute each call to a user, record [cost and token usage](/manage/ai/usage-analytics), keep [chat session history](/manage/ai/session-logs), and enforce who can use which [models](/manage/ai/providers/model-routing) and [budgets](/manage/ai/budgets).
|
||||
|
||||
That is the justification in Pangolin terms: an identity-aware gateway, not only a model router.
|
||||
|
||||
## What Each Solution Provides
|
||||
|
||||
**[LiteLLM](https://docs.litellm.ai)** is a dedicated LLM proxy. It presents a unified API in front of many model providers, with virtual keys, spend tracking, fallbacks, and load balancing. Teams often run it as a self-hosted proxy or use its SDK inside applications.
|
||||
|
||||
**[Bifrost](https://www.getmaxim.ai/bifrost)** is a dedicated LLM gateway focused on complex routing rules, failover, and performance. For example, you can use it to route requests to different models based on the user's location or the request's content. You can also run it downstream of Pangolin as a [Custom provider](/manage/ai/providers/custom/bifrost) when Pangolin should authenticate callers and Bifrost should pick models.
|
||||
|
||||
**Pangolin** provides the same gateway job as a [protocol-aware resource](/about/how-pangolin-works#ai-gateway). Providers, model lists, [virtual API keys](/manage/ai/virtual-api-keys) on public resources, budgets, and session logs are all there. Identity comes from Pangolin users, roles, and (for private AI gateway resources) the [desktop client](/manage/clients/install-client). Site and client tunnels are how you reach self-hosted models and how you keep provider keys off laptops.
|
||||
|
||||
## Identity-Aware Gateway
|
||||
|
||||
Access follows the resource. You grant [users and roles](/manage/access-control/create-user) the same way you do for other Pangolin resources. Pangolin then decides which models that identity may call and whether a [budget](/manage/ai/budgets) still allows the request.
|
||||
|
||||
On a [public AI Gateway](/manage/resources/public/ai-gateway), coding agents send a [virtual API key](/manage/ai/virtual-api-keys). Pangolin identity keys identify the user; you grant the user or role, not the key.
|
||||
|
||||
On a [private AI Gateway](/manage/resources/private/ai-gateway), the [Pangolin client](/manage/clients/install-client) running on the end user's device already authenticated that user. Coding agents on that device call over the tunnel fully privately. Pangolin attributes the request to the connected identity. That eliminates provider API keys on the laptop: the upstream key never leaves the provider.
|
||||
|
||||
## Virtual API Keys
|
||||
|
||||
On other gateways you mint a key for each person, then keep track of who holds which key and which models that key may call. Pangolin [virtual API keys](/manage/ai/virtual-api-keys) work the other way around.
|
||||
|
||||
Every user already has an **identity key**. You do not grant the key access to the gateway. You grant the [user or role](/manage/access-control/create-user) access to the AI Gateway resource and its models. The key only identifies the caller as that user. When you change who is on the resource, the key follows. You do not manage a key inventory as the source of access.
|
||||
|
||||
Users retrieve their own key:
|
||||
|
||||
- Log in at the public gateway URL in a browser, using Pangolin or their [identity provider](/manage/identity-providers/add-an-idp). After login, Pangolin presents the key.
|
||||
- Open the Resource Launcher, pick the resource, and copy the key. The launcher also shows which models they can use and how to configure a coding agent to use the key.
|
||||
- Admins can [email keys](/manage/ai/virtual-api-keys#email-keys-to-users) to users.
|
||||
|
||||
You can still mint **manual keys**, the same way other gateways issue arbitrary virtual keys. Use those for services, machines, CI, or a person if you want a key that is not tied to resource grants. Creating the key grants access to the public gateways you scope it to. See [Identity Keys vs Manual Keys](/manage/ai/virtual-api-keys#identity-keys-vs-manual-keys).
|
||||
|
||||
## Tunneling
|
||||
|
||||
Tunneling is a Pangolin strength, on both sides of the network.
|
||||
|
||||
### Sites: Self-Hosted Models on Remote Networks
|
||||
|
||||
A [site connector](/manage/sites/understanding-sites) sits on the network where [Ollama](/manage/ai/providers/custom/ollama), [vLLM](/manage/ai/providers/custom/vllm), or another model server already runs. [Custom providers](/manage/ai/providers/custom) use **Site Targets** so the gateway reaches that API over the tunnel. The model host does not need a public IP or an inbound port. Pangolin can also call cloud providers from the node, so one resource can mix hosted APIs and self-hosted models.
|
||||
|
||||
You can put LiteLLM or Bifrost on that same site network and attach them as a Custom provider. Pangolin authenticates; the downstream gateway routes.
|
||||
|
||||
### Clients: Remote Access Without Provider Keys
|
||||
|
||||
Users connect with the Pangolin client, then reach a private AI Gateway the same way they reach a private host or SSH resource. That is how someone on a laptop uses a self-hosted model on a remote network, or a cloud provider, without holding the upstream key. The client connection is the credential so you don't need to distribute virtual API keys to users.
|
||||
|
||||
## When to Use Each Solution
|
||||
|
||||
Use **LiteLLM** if you want a dedicated Python proxy or SDK in front of many providers, and you already have a network path and your own identity layer.
|
||||
|
||||
Use **Bifrost** if you want a dedicated high-performance LLM gateway for routing and failover. You can run it on its own, or behind Pangolin as a [Custom provider](/manage/ai/providers/custom/bifrost).
|
||||
|
||||
Use **Pangolin** if you want LLM access as an identity-aware resource: the same users and roles as the rest of Pangolin, site tunnels to self-hosted models, and client tunnels that authenticate and govern usage at the edge so provider keys stay off devices.
|
||||
|
||||
<Card title="AI Gateway Overview" icon="sparkles" href="/manage/ai/overview">
|
||||
Set up providers, resources, and identity-based access for coding agents and AI clients.
|
||||
</Card>
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: "Pangolin vs. Proxy vs. VPN"
|
||||
description: "What are the similarities and differences between Pangolin and traditional reverse proxies and VPNs?"
|
||||
---
|
||||
|
||||
Pangolin combines the capabilities of both a reverse proxy and a VPN into a single platform. It provides reverse proxy functionality through public resources and VPN functionality through private resources, all with zero-trust access control and distributed architecture.
|
||||
|
||||
## What Each Solution Provides
|
||||
|
||||
**Reverse Proxies** expose web applications to the internet. They handle SSL termination, load balancing, and basic authentication. Users access applications through web browsers using domain names. They are usually protocol-aware of HTTPS only.
|
||||
|
||||
**VPNs** create encrypted tunnels that give users access to entire private networks as if they were on the local network. Users install client software and connect to a VPN server. Once connected, they can access any resource on the network they have network-level access to.
|
||||
|
||||
**Pangolin** provides both capabilities, and several things neither traditional tool does on its own. [Public resources](/manage/resources/understanding-resources#public-resource-types) are publicly served proxies. That is often a website in a browser, but it can also be an API or an [AI Gateway](/manage/ai/overview). [Private resources](/manage/resources/understanding-resources#private-resource-types) work like a zero-trust VPN, giving users access to specific hosts or network ranges when connected with a client.
|
||||
|
||||
## What Pangolin Does Differently
|
||||
|
||||
| Capability | Traditional reverse proxy | Traditional VPN | Pangolin |
|
||||
|------------|--------------------------|-----------------|----------|
|
||||
| Publicly served proxies | Web apps | No | Yes: HTTP/HTTPS, APIs, [AI Gateway](/manage/ai/overview) |
|
||||
| Protocol-aware proxies | HTTPS | No | Yes: HTTPS, SSH, RDP, VNC, AI providers, and more |
|
||||
| Browser SSH, RDP, VNC | No | No | Yes — public SSH, RDP, VNC |
|
||||
| Client-only private access | No | Yes | Yes — host, CIDR, SSH, private HTTP |
|
||||
| Per-resource access control | Limited | Network-wide | Yes — users and roles per resource |
|
||||
| No open inbound ports | No | Sometimes | Yes — outbound site tunnels |
|
||||
| TLS at the network edge (private) | No | No | Yes — [private HTTP/HTTPS](/manage/resources/private/private-http) |
|
||||
| Multi-site routing and failover | Uncommon | Uncommon | Yes — automatic site selection |
|
||||
|
||||
## Reverse Proxy Capabilities
|
||||
|
||||
Pangolin's public resources function as reverse proxies. They are publicly served, not limited to web browsers. A traditional reverse proxy is usually protocol-aware of HTTPS only. Pangolin understands many protocols, including HTTPS, SSH, RDP, VNC, and AI provider APIs, so identity, access rules, and logging apply to each the same way.
|
||||
|
||||
**HTTP/HTTPS** resources expose web applications and APIs through domain names with automatic SSL certificates. A browser is the common client; any HTTP client can call the same FQDN. Identity-aware access control supports SSO, MFA, and rules based on user identity, roles, geographic location, IP addresses, and URL paths.
|
||||
|
||||
**AI Gateway** resources are public proxies for LLM APIs. Coding agents call the FQDN instead of the upstream provider. Pangolin authenticates the caller and applies identity, model access, and budgets. See [AI Gateway](/manage/ai/overview).
|
||||
|
||||
**SSH, RDP, and VNC** resources render full sessions in the browser. Users get a terminal, Windows desktop, or VNC display without installing SSH clients or remote desktop software—while still passing through Pangolin authentication first.
|
||||
|
||||
**TCP and UDP** resources bind to a port on the Pangolin server for raw protocol proxying when you need a public pipe without a domain name or auth layer.
|
||||
|
||||
Unlike traditional reverse proxies, Pangolin does not require public IP addresses or open ports on your network. Sites create outbound tunnels to Pangolin, so your applications remain behind firewalls.
|
||||
|
||||
## VPN Capabilities
|
||||
|
||||
Pangolin's private resources function like a zero-trust VPN—but with tighter scope than a traditional VPN.
|
||||
|
||||
**Host and CIDR** resources route traffic to specific machines or subnets over the tunnel. Users only reach what you explicitly grant them, with optional per-resource port restrictions—not an entire flat network.
|
||||
|
||||
**Private HTTP/HTTPS** resources behave like a reverse proxy that only exists on the tunnel. TLS terminates at your [site edge over peer-to-peer transport](/manage/resources/private/private-http)—the application is never reachable from the public internet, only from connected clients with valid access.
|
||||
|
||||
**[Private SSH](/manage/resources/private/ssh)** resources provide terminal access via `pangolin ssh`, with optional automatic user provisioning from Pangolin identity—no manual key distribution required.
|
||||
|
||||
Clients work transparently with applications. No application configuration is required. Users connect once and can access all their authorized resources. The client handles routing and establishes encrypted tunnels automatically.
|
||||
|
||||
## Why Pangolin Combines Both
|
||||
|
||||
Many organizations need both reverse proxy and VPN capabilities. You might want to expose a customer portal through a browser while also giving developers SSH access to internal servers and a private HTTPS dashboard that never touches the public internet.
|
||||
|
||||
With Pangolin, you use one platform for all of these. Public resources handle publicly served access: websites in a browser, APIs, [AI Gateway](/manage/ai/overview) resources, and SSH, RDP, and VNC sessions. Private resources handle tunnel-only access to hosts, subnets, internal HTTPS apps, and CLI SSH. Both use the same authentication system, access control policies, and infrastructure.
|
||||
|
||||
This unified approach simplifies management. You configure users, roles, and access policies once. Those policies apply to both public and private resources. You do not need to maintain separate systems for reverse proxy and VPN access.
|
||||
|
||||
## Infrastructure and Availability
|
||||
|
||||
Traditional reverse proxies and VPNs typically run on a single server. If that server fails, all access is lost. They also require public IP addresses and open ports, which adds complexity and security concerns.
|
||||
|
||||
Pangolin uses a distributed architecture with multiple nodes. If one node fails, traffic automatically routes to another node. Sites create outbound tunnels, so your networks do not need public IP addresses or open ports.
|
||||
|
||||
When a resource is reachable from multiple site connectors, Pangolin selects the healthiest path based on latency and availability—users connect to the resource, not to a specific site. You can deploy multiple remote nodes for high availability. If your nodes become unavailable, traffic can optionally fail over to cloud infrastructure until you restore service.
|
||||
|
||||
## When to Use Each Solution
|
||||
|
||||
Use a traditional reverse proxy if you only need to expose web applications over HTTP/HTTPS, you have a public IP address, and you do not need advanced access control or high availability.
|
||||
|
||||
Use a traditional VPN if you need broad network access, you can accept the security risks of flat network visibility, and you do not need application-specific access control.
|
||||
|
||||
Use Pangolin if you need both reverse proxy and VPN capabilities, publicly served APIs and AI gateways, browser SSH/RDP/VNC, private HTTPS with edge TLS termination, granular per-resource access control, multi-site routing, high availability, or outbound-only connectivity without open ports on your networks.
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
title: "Join Us!"
|
||||
---
|
||||
We are looking for talented engineers to join our team and help build secure remote access. If you're passionate about open-source software, networking, and security, we'd love to hear from you.
|
||||
|
||||
## About Pangolin
|
||||
|
||||
Pangolin delivers identity-aware remote access to internal apps and services. Our platform replaces legacy VPNs and simplifies secure access to infrastructure, applications, and developer environments.
|
||||
|
||||
We build in the open and are self‑hosted by default so teams retain control over data and infrastructure. The system is policy‑driven, integrates with standard IdPs, exposes clear observability and health, and provides an API for automation.
|
||||
|
||||
If you're interested in open-source auth and networking infrastructure, we'd love to chat.
|
||||
|
||||
## Open Roles
|
||||
|
||||
- [Software Engineer - Full Stack](./software-engineer-full-stack)
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: "Software Engineer - Full Stack"
|
||||
---
|
||||
- Location: `San Francisco`
|
||||
- Salary: `$125k - $185k + 0.5% - 1.5% equity`
|
||||
- Years of experience: `3+`
|
||||
- Skills: `TypeScript, Go, SQL (PostgreSQL, SQLite), NextJS, AWS`
|
||||
|
||||
## About Pangolin
|
||||
|
||||
Pangolin delivers identity-aware remote access to internal apps and services. Our platform replaces legacy VPNs and simplifies secure access to infrastructure, applications, and developer environments.
|
||||
|
||||
We build in the open and are self‑hosted by default so teams retain control over data and infrastructure. The system is policy‑driven, integrates with standard IdPs, exposes clear observability and health, and provides an API for automation.
|
||||
|
||||
If you're interested in open-source auth and networking infrastructure, we'd love to chat.
|
||||
|
||||
## About The Role
|
||||
|
||||
As a Full Stack Software Engineer at Pangolin, you'll help architect, build, and maintain the core of the Pangolin system. You'll work primarily on the Pangolin container, the central server with the UI, APIs, schemas, and more. As an early hire, you'll play a foundational role in shaping how Pangolin is built as both a product and a company.
|
||||
|
||||
## What You'll Do
|
||||
|
||||
- Design, develop, and test the core of our self-hosted platform
|
||||
- Frontend (NextJS, Tailwind, ShadCN)
|
||||
- Backend (Express APIs, SQL, Drizzle ORM)
|
||||
- Processes (CICD, Internal Tooling)
|
||||
- Troubleshooting and resolving complex issues related to distributed systems, networking, and security.
|
||||
- Actively engaging with the open-source community, providing support, and driving engagement on platforms like GitHub and Discord
|
||||
- Ship quickly and get instant feedback from our large open-source user-base
|
||||
|
||||
## What We're Looking For
|
||||
|
||||
### High Level
|
||||
|
||||
- Must be a current or recent graduate with more than 3 years of work experience in computer science
|
||||
- Must be authorized to work in the US
|
||||
- Must be comfortable with and excited by early stage startup culture and figuring things out as we go
|
||||
- Must have your own ideas and be willing to communicate them
|
||||
|
||||
### Other Technical Qualifications
|
||||
|
||||
- Extensive TypeScript experience and can write a little Go
|
||||
- Familiarity with web identity and authentication standards: OAuth2, OIDC, SSO
|
||||
- Experience with cloud infrastructure and operational technologies (Docker, Kubernetes, Linux, AWS)
|
||||
- Basic knowledge of networking concepts: tunneling, WireGuard, proxies, SSL/TLS
|
||||
|
||||
## What You Can Expect
|
||||
|
||||
- Competitive salary
|
||||
- Hybrid (in-person + work-from-home)
|
||||
- Quiet work environment
|
||||
- Small, trusting team of founders and engineers
|
||||
- Relocation assistance
|
||||
- Unlimited PTO
|
||||
|
||||
## Process
|
||||
|
||||
1. We will review your provided materials
|
||||
2. Complete a quick “get to know you interview” with founders
|
||||
3. 1-2 technical interviews
|
||||
4. Fly out to meet the team for a day (paid if not hired)
|
||||
5. Onboard!
|
||||
|
||||
## How to Apply
|
||||
|
||||
1. [Apply here](https://www.ycombinator.com/companies/pangolin/jobs/7DeBchl-founding-engineer-full-stack)
|
||||
2. Attach your GitHub profile and/or any relevant open-source contributions
|
||||
3. Include a brief thoughtful message about why you're interested in Pangolin and this role
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: "Software Engineer - Go + Network"
|
||||
---
|
||||
- Location: `San Francisco`
|
||||
- Salary: `$125k - $185k + 0.5% - 1.5% equity`
|
||||
- Years of experience: `3+`
|
||||
- Skills: `Go, Networking Fundamentals, TypeScript (yes for coordination)`
|
||||
|
||||
## About Pangolin
|
||||
|
||||
Pangolin delivers identity-aware remote access to internal apps and services. Our platform replaces legacy VPNs and simplifies secure access to infrastructure, applications, and developer environments.
|
||||
|
||||
We build in the open and are self‑hosted by default so teams retain control over data and infrastructure. The system is policy‑driven, integrates with standard IdPs, exposes clear observability and health, and provides an API for automation.
|
||||
|
||||
If you're interested in open-source auth and networking infrastructure, we'd love to chat.
|
||||
|
||||
## About The Role
|
||||
|
||||
As a Go Network Software Engineer at Pangolin, you'll help architect, build, and maintain the core of the Pangolin system. You'll work primarily on Newt, Gerbil, and Olm tunneling components and the central server APIs. As an early hire, you'll play a foundational role in shaping how Pangolin is built as both a product and a company.
|
||||
|
||||
## What You'll Do
|
||||
|
||||
- Design, develop, and test the core of our networking and tunneling systems
|
||||
- Work to develop a hub and spoke, hole punched, zero-trust VPN solution and desktop applications
|
||||
- Troubleshooting and resolving complex issues related to distributed systems, networking, and security.
|
||||
- Actively engaging with the open-source community, providing support, and driving engagement on platforms like GitHub and Discord
|
||||
- Ship quickly and get instant feedback from our large open-source user-base
|
||||
|
||||
## What We're Looking For
|
||||
|
||||
### High Level
|
||||
|
||||
- Must be a current or recent graduate with more than 3 years of work experience in computer science
|
||||
- Must be authorized to work in the US
|
||||
- Must be comfortable with and excited by early stage startup culture and figuring things out as we go
|
||||
- Must have your own ideas and be willing to communicate them
|
||||
|
||||
### Other Technical Qualifications
|
||||
|
||||
- Extensive Go experience and can write a little TypeScript
|
||||
- Experience with cloud infrastructure and operational technologies (Docker, Kubernetes, Linux, AWS)
|
||||
- Strong knowledge of networking concepts: tunneling, WireGuard, proxies, SSL/TLS
|
||||
|
||||
## What You Can Expect
|
||||
|
||||
- Competitive salary
|
||||
- Hybrid (in-person + work-from-home)
|
||||
- Quiet work environment
|
||||
- Small, trusting team of founders and engineers
|
||||
- Relocation assistance
|
||||
- Unlimited PTO
|
||||
|
||||
## Process
|
||||
|
||||
1. We will review your provided materials
|
||||
2. Complete a quick “get to know you interview” with founders
|
||||
3. 1-2 technical interviews
|
||||
4. Fly out to meet the team for a day (paid if not hired)
|
||||
5. Onboard!
|
||||
|
||||
## How to Apply
|
||||
|
||||
1. Add [Owen](https://www.linkedin.com/in/owenschwartz/) on LinkedIn
|
||||
2. Send your resume/CV
|
||||
3. Send your GitHub profile and highlight any past projects
|
||||
@@ -0,0 +1,326 @@
|
||||
---
|
||||
title: "Contribution Guide"
|
||||
description: "Set up your local development environment for contributing to Pangolin"
|
||||
---
|
||||
This guide describes how to set up your local development environment for contributing to Pangolin. We recommend using Docker Compose for the most consistent development experience across different environments.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node 24
|
||||
- NPM 11 or similar
|
||||
- Go 1.25
|
||||
- Git
|
||||
- Docker & Docker Compose
|
||||
- Python (for NPM builds)
|
||||
- Make
|
||||
- G++
|
||||
|
||||
<Info>
|
||||
For managing multiple versions of Go, you may want to use [gvm](https://github.com/moovweb/gvm).
|
||||
For managing multiple versions of NodeJS, you may want to use [nvm](https://github.com/nvm-sh/nvm).
|
||||
</Info>
|
||||
|
||||
## Setup Your Repository
|
||||
|
||||
Below is an example if you're working on the Pangolin repository.
|
||||
|
||||
<Steps>
|
||||
<Step title="Fork and clone">
|
||||
[Fork](https://help.github.com/articles/fork-a-repo/) the repository(ies) to your own GitHub account and [clone](https://help.github.com/articles/cloning-a-repository/) to your local device:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/YOUR_USERNAME/pangolin.git
|
||||
cd pangolin/
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add upstream remote">
|
||||
Add the remote `upstream`:
|
||||
|
||||
```bash
|
||||
git remote add upstream https://github.com/fosrl/pangolin.git
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create feature branch">
|
||||
Create a new branch:
|
||||
|
||||
```bash
|
||||
git checkout -b BRANCH_NAME dev
|
||||
```
|
||||
|
||||
It is recommended to give your branch a meaningful name, relevant to the feature or fix you are working on.
|
||||
|
||||
**Good examples**:
|
||||
- `docs-docker`
|
||||
- `feature-new-system`
|
||||
- `fix-title-cards`
|
||||
|
||||
**Bad examples**:
|
||||
- `bug`
|
||||
- `docs`
|
||||
- `feature`
|
||||
- `fix`
|
||||
- `patch`
|
||||
</Step>
|
||||
|
||||
<Step title="Open pull request">
|
||||
If you open a pull request, open it against the `dev` branch of the original repository.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Important Best Practices for PRs
|
||||
|
||||
- **Keep PRs small and single-purpose**: One feature, fix, or improvement per PR for easier review and testing.
|
||||
- **Prefer improvements over new features**: If you want to propose a net-new feature, contact us by email or on Discord first so we can confirm it fits the roadmap and help scope it.
|
||||
- **Frontend consistency**:
|
||||
- Use existing styles, components, and patterns.
|
||||
- Use Credenza for modals and Zod for form validation.
|
||||
- Keep Tailwind classes minimal; prefer component defaults.
|
||||
- Look for an existing example and mirror that pattern. Extract a small reusable component only when it clearly improves reuse.
|
||||
- **Stick to established patterns**: Avoid introducing new architectures or abstractions without discussing them with us first.
|
||||
- **Auth changes require extra care**:
|
||||
- Pangolin is multi-tenant. Handle user controls at the org level (varies by control) or globally via the server admin panel as appropriate.
|
||||
- Protect all API routes with the correct middleware and verify user permissions and access to referenced entities before performing actions.
|
||||
- **Database changes**:
|
||||
- Keep SQLite and Postgres schemas fully in sync and backward compatible.
|
||||
- Use datatypes supported by both databases.
|
||||
- No need to write versioned migrations; maintainers will handle these during releases.
|
||||
- **Add visuals**: Include screenshots or short videos when applicable to speed up reviews.
|
||||
|
||||
## Databases
|
||||
|
||||
Pangolin supports two database types: SQLite and Postgres. You can switch between them with the provided scripts:
|
||||
|
||||
Before running these, read local development setup below.
|
||||
|
||||
```bash
|
||||
npm run set:sqlite
|
||||
# or
|
||||
npm run set:pg
|
||||
```
|
||||
|
||||
After switching, regenerate and apply the schema using the matching scripts for that database. Keep both SQLite and Postgres schemas fully in sync and backward compatible.
|
||||
|
||||
## Private Files and Directories
|
||||
|
||||
Pangolin includes both AGPLv3 code and some proprietary code licensed under the Fossorial Commercial License. Proprietary files include a license header and often live in directories whose names start with `private`.
|
||||
|
||||
You may edit proprietary files in your PR as long as your PR includes the required CLA.
|
||||
|
||||
- Frontend: no proprietary code.
|
||||
- Backend: proprietary code exists, primarily under `server/private/`. Subdirectories mirror the structure under `server/`.
|
||||
|
||||
To keep the AGPLv3 distribution fully compliant, be careful about imports:
|
||||
|
||||
- AGPLv3 files must never import from the private directory. In TypeScript, the alias `#private/` points to proprietary code and should only be used inside other private files.
|
||||
- If you must expose proprietary behavior to AGPLv3 code, use a dynamic import pattern. Create a file that mirrors the proprietary file’s relative location between `server/private` and `server`, and ensure the exported APIs have exactly matching function signatures. Dynamic import aliases start with `#dynamic`.
|
||||
- At build time, depending on the build flag, `#dynamic` imports are resolved to the appropriate implementation (AGPLv3 or proprietary).
|
||||
|
||||
Build flags control which distribution you are working on: `oss`, `enterprise`, or `saas`. Enterprise and SaaS include proprietary code; OSS must be 100% AGPLv3 compliant and excludes proprietary code. Use the existing npm scripts to switch:
|
||||
|
||||
```bash
|
||||
npm run set:oss
|
||||
# or npm run set:enterprise
|
||||
# or npm run set:saas
|
||||
```
|
||||
|
||||
Switching distributions updates TypeScript path aliases so `#dynamic` resolves to the correct locations. The build flag is also used in code to conditionally enable or disable features per distribution.
|
||||
|
||||
As a rule of thumb, write as much AGPLv3 code as possible. Place only core, distribution-specific functionality in the proprietary layer (Enterprise/SaaS).
|
||||
|
||||
Database schemas are never proprietary; all distributions share the same schemas.
|
||||
|
||||
If you have any questions about this setup, email us or reach out on Discord.
|
||||
|
||||
## Pangolin Development Setup
|
||||
|
||||
Choose your preferred development approach. We strongly recommend Docker Compose for the most consistent experience across all platforms.
|
||||
|
||||
### Local Development
|
||||
|
||||
<Steps>
|
||||
<Step title="Install dependencies">
|
||||
Install package dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Configure environment">
|
||||
Ensure you have a `config/` directory at the root with a `config.yml` inside. Refer to the [Pangolin Configuration docs](/self-host/advanced/config-file) or the `config.example.yml` in the repo for a sample of what to include in that file.
|
||||
|
||||
<Warning>
|
||||
You may need to tweak this to run in dev, such as setting the `dashboard_url` to `http://localhost:3002`.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Set your environment">
|
||||
Choose to build from the oss/enterprise/saas codebase:
|
||||
```bash
|
||||
npm run set:oss
|
||||
# or npm run set:enterprise
|
||||
# or npm run set:saas
|
||||
```
|
||||
|
||||
Then choose your database:
|
||||
```bash
|
||||
npm run set:sqlite
|
||||
# or npm run set:pg
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Generate database schema">
|
||||
Generate the database schema and push it:
|
||||
|
||||
```bash
|
||||
npm run db:generate
|
||||
npm run db:push
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Start development server">
|
||||
Start the development server using Docker Compose:
|
||||
|
||||
```bash
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
Or, start the development server directly:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Exit Nodes
|
||||
|
||||
When running Pangolin for the first time there will be no exit nodes. This means that there have been no Gerbil "exit nodes" registered in the database, and therefore, you cannot create Newt sites. When Gerbil first starts up and requests its config from Pangolin for the first time it gets registered as an exit node.
|
||||
|
||||
The easiest way to resolve this is to run Gerbil and have it register in your dev environment. Download the Gerbil binary and run it with localhost:
|
||||
|
||||
```bash
|
||||
./gerbil \
|
||||
--reachableAt=http://localhost:3004 \
|
||||
--generateAndSaveKeyTo=/var/config/key \
|
||||
--remoteConfig=http://localhost:3001/api/v1/
|
||||
```
|
||||
|
||||
Or enter in a dummy exit-node manually to the database:
|
||||
|
||||
```
|
||||
INSERT INTO "exitNodes" (
|
||||
"name",
|
||||
"address",
|
||||
"endpoint",
|
||||
"publicKey",
|
||||
"listenPort",
|
||||
"reachableAt",
|
||||
"type"
|
||||
)
|
||||
VALUES (
|
||||
'exit-node-1',
|
||||
'10.0.0.1/24',
|
||||
'gerbil.pangolin.net',
|
||||
'abc123',
|
||||
1234,
|
||||
'gerbil.pangolin.net',
|
||||
'gerbil'
|
||||
);
|
||||
```
|
||||
|
||||
## Windows Development Considerations
|
||||
|
||||
<Warning>
|
||||
Windows users with Docker Desktop + WSL2: File change detection may not work properly when project files are stored on the Windows filesystem.
|
||||
</Warning>
|
||||
|
||||
<Tabs>
|
||||
<Tab title="WSL2 Filesystem (Recommended)">
|
||||
**Best performance and compatibility**
|
||||
|
||||
|
||||
- **Where to store your project files:**
|
||||
- For best performance, always store your project inside the Linux filesystem of your Docker or Default WSL2 instance, e.g. `/home/<user>/pangolin`.
|
||||
- If other WSL instances are used, ensure the Docker Desktop WSL integration is enabled for that distribution.
|
||||
- For further information, see Link Section below.
|
||||
|
||||
- **Accessing WSL2 files from Windows:**
|
||||
- You can access your WSL2 home directory from Windows using the UNC path: `\\wsl$\<DistroName>\home\<user>\pangolin` (replace `<DistroName>` with your actual WSL distribution, e.g. `Ubuntu-22.04`).
|
||||
- This path works in Windows Explorer, VS Code, and other Windows applications. You can drag & drop files, create shortcuts, or map a network drive for convenience.
|
||||
- **Note:** This UNC path is for Windows tools only. Do not use it for Docker container mounts.
|
||||
|
||||
- **How to mount WSL2 files in Docker containers:**
|
||||
- Always use the absolute Linux path from inside WSL2 for Docker volumes. This is the only method fully supported and recommended by Docker.
|
||||
- **Correct Docker Compose example:**
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
volumes:
|
||||
- /home/<user>/pangolin:/app
|
||||
```
|
||||
- **Correct docker run example:**
|
||||
```bash
|
||||
docker run -v /home/<user>/pangolin:/app my-image
|
||||
```
|
||||
- **Never use `\\wsl$` or Windows paths** (e.g. `/mnt/c/...`) for Docker volumes when running with the WSL2 backend. This is not supported and can lead to poor performance or errors.
|
||||
- File watchers and hot reload works natively when your project is inside the WSL2 filesystem and mounted using the Linux path.
|
||||
|
||||
<Note> You may want to use the [VS Code Remote - WSL extension](https://code.visualstudio.com/docs/remote/wsl) or [VS Code Remote - SSH Extension](https://code.visualstudio.com/docs/remote/ssh) to open your project folder directly in VSCode from the WSL/Remote Filesystem for seamless Development. </Note>
|
||||
|
||||
|
||||
**Reference Links**
|
||||
- [WSL Docker Best Practices](https://docs.docker.com/desktop/features/wsl/best-practices/)
|
||||
- [Use WSL for Development](https://docs.docker.com/desktop/features/wsl/use-wsl/)
|
||||
- [WSL2 Setup](https://docs.docker.com/desktop/features/wsl/)
|
||||
</Tab>
|
||||
|
||||
<Tab title="Windows Filesystem + Polling (Workaround)">
|
||||
If you need to keep your files on the native Windows filesystem (`C:\Users\...`), enable **Polling Mode** for file watchers.
|
||||
|
||||
Enable polling mode by adding the following environment variables to your `docker-compose.yml` or `.env` file:
|
||||
|
||||
For `.env`:
|
||||
```env
|
||||
WATCHPACK_POLLING=true
|
||||
CHOKIDAR_USEPOLLING=true
|
||||
```
|
||||
|
||||
For `docker-compose.yml`:
|
||||
```yaml
|
||||
environment:
|
||||
- WATCHPACK_POLLING=true
|
||||
- CHOKIDAR_USEPOLLING=true
|
||||
```
|
||||
|
||||
<Note>This increases CPU usage but ensures file watchers work properly. Polling mode is not required when working directly on the WSL filesystem.</Note>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Component Development
|
||||
|
||||
### Gerbil
|
||||
|
||||
- Go 1.25
|
||||
|
||||
```bash
|
||||
make local
|
||||
```
|
||||
|
||||
### Newt
|
||||
|
||||
- Go 1.25
|
||||
|
||||
```bash
|
||||
make local
|
||||
```
|
||||
|
||||
### Olm
|
||||
|
||||
- Go 1.25
|
||||
|
||||
```bash
|
||||
make local
|
||||
```
|
||||
@@ -0,0 +1,114 @@
|
||||
---
|
||||
title: "Feature Requests & Bug Reports"
|
||||
description: "How to submit feature requests and report bugs for Pangolin"
|
||||
---
|
||||
We welcome contributions from the community to help improve Pangolin. To ensure your feedback is properly tracked and prioritized, use the repository that matches the component where your issue or request belongs.
|
||||
|
||||
## Pick the Right Repository
|
||||
|
||||
Pangolin is made up of multiple components. To get the fastest response, file your request or bug report in the repository that matches what you're using.
|
||||
|
||||
### Server Components
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Pangolin" icon="server" href="https://github.com/fosrl/pangolin">
|
||||
Dashboard and core platform.
|
||||
[Discussions](https://github.com/fosrl/pangolin/discussions) and [Issues](https://github.com/fosrl/pangolin/issues).
|
||||
</Card>
|
||||
|
||||
<Card title="Newt" icon="plug" href="https://github.com/fosrl/newt">
|
||||
Site and network connector.
|
||||
[Issues](https://github.com/fosrl/newt/issues).
|
||||
</Card>
|
||||
|
||||
<Card title="Gerbil" icon="circle-nodes" href="https://github.com/fosrl/gerbil">
|
||||
WireGuard interface management service.
|
||||
[Issues](https://github.com/fosrl/gerbil/issues).
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
### User Clients
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Windows App" icon="desktop" href="https://github.com/fosrl/windows">
|
||||
Pangolin desktop application for Windows.
|
||||
[Issues](https://github.com/fosrl/windows/issues).
|
||||
</Card>
|
||||
|
||||
<Card title="Android App" icon="desktop" href="https://github.com/fosrl/android">
|
||||
Pangolin mobile application for Android.
|
||||
[Issues](https://github.com/fosrl/android/issues).
|
||||
</Card>
|
||||
|
||||
<Card title="Apple Apps" icon="desktop" href="https://github.com/fosrl/apple">
|
||||
Pangolin application for macOS, iOS, and iPadOS.
|
||||
[Issues](https://github.com/fosrl/apple/issues).
|
||||
</Card>
|
||||
|
||||
<Card title="Pangolin CLI" icon="desktop" href="https://github.com/fosrl/cli">
|
||||
Pangolin CLI client for Linux and macOS environments.
|
||||
[Issues](https://github.com/fosrl/cli/issues).
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Feature Requests
|
||||
|
||||
We encourage you to submit feature requests in the [GitHub Discussions section](https://github.com/fosrl/pangolin/discussions) of the Pangolin repository. This allows the community to:
|
||||
|
||||
- **Upvote features** they want to see implemented
|
||||
- **Provide feedback** and suggestions on proposed features
|
||||
- **Discuss implementation details** and alternatives
|
||||
- **Help prioritize** which features to work on next
|
||||
|
||||
<Note>
|
||||
Feature requests with community support (upvotes and positive feedback) are more likely to be prioritized for development.
|
||||
</Note>
|
||||
|
||||
## Bug Reports
|
||||
|
||||
Bug reports should be submitted in GitHub Issues for the relevant repository:
|
||||
|
||||
### Server Components
|
||||
|
||||
- **Pangolin**: [fosrl/pangolin/issues](https://github.com/fosrl/pangolin/issues)
|
||||
- **Newt**: [fosrl/newt/issues](https://github.com/fosrl/newt/issues)
|
||||
- **Gerbil**: [fosrl/gerbil/issues](https://github.com/fosrl/gerbil/issues)
|
||||
|
||||
### User Clients
|
||||
|
||||
- **Windows App**: [fosrl/windows/issues](https://github.com/fosrl/windows/issues)
|
||||
- **Android App**: [fosrl/android/issues](https://github.com/fosrl/android/issues)
|
||||
- **Apple Apps**: [fosrl/apple/issues](https://github.com/fosrl/apple/issues)
|
||||
- **Pangolin CLI**: [fosrl/cli/issues](https://github.com/fosrl/cli/issues)
|
||||
- **Olm**: [fosrl/olm/issues](https://github.com/fosrl/olm/issues)
|
||||
|
||||
Using the correct repo ensures:
|
||||
|
||||
- **Proper tracking** of bugs through their lifecycle
|
||||
- **Developer visibility** for quick resolution
|
||||
- **Version tracking** and regression testing
|
||||
- **Duplicate detection** and consolidation
|
||||
|
||||
<Warning>
|
||||
Please provide as much detail as possible to help developers reproduce and fix the issue quickly.
|
||||
</Warning>
|
||||
|
||||
## Before Submitting
|
||||
|
||||
- Search existing discussions and issues to avoid duplicates
|
||||
- Provide clear, detailed information
|
||||
- Include steps to reproduce (for bugs)
|
||||
- Test on the latest version of Pangolin
|
||||
- Check if the issue is environment-specific
|
||||
|
||||
## Alternative Channels
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Security Issues" icon="shield">
|
||||
For security vulnerabilities, please email security@pangolin.net instead of posting publicly.
|
||||
</Card>
|
||||
|
||||
<Card title="General Questions" icon="question">
|
||||
For general questions, use [GitHub Discussions](https://github.com/fosrl/pangolin/discussions) with the "Q&A" category, or come chat with us on [Discord](https://pangolin.net/discord).
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,178 @@
|
||||
---
|
||||
title: "System Architecture"
|
||||
description: "A technical overview of how Pangolin's control plane, nodes, connectors, and clients fit together"
|
||||
---
|
||||
|
||||
## Open Source
|
||||
|
||||
Every layer of Pangolin is open source, from the control plane server through site connectors and end-user clients. Each component has its own repository under [fosrl on GitHub](https://github.com/fosrl); the [component reference](#component-reference) at the bottom of this page lists them.
|
||||
|
||||
The software that powers [Pangolin Cloud](https://app.pangolin.net) is open source as well. The SaaS control plane runs the same codebase as self-hosted Pangolin, and that code lives in the main [fosrl/pangolin](https://github.com/fosrl/pangolin) repository.
|
||||
|
||||
Pangolin is split into a **control plane** (configuration, identity, and orchestration) and a **data plane** (encrypted tunnels, ingress, and relay). For [public resources](/manage/resources/understanding-resources#public-resource-types), site connectors initiate **outbound** connections to a Pangolin node so the node can reverse-proxy traffic into the remote network without opening inbound ports there. For [private resources](/manage/resources/understanding-resources#private-resource-types), [clients](/manage/clients/understanding-clients) connect **peer-to-peer** to the site; the node is only in the data path as a relay when a direct path cannot be established. See [Peer-to-Peer and Relay Paths](#peer-to-peer-and-relay-paths).
|
||||
|
||||
For a product-level walkthrough of sites, resources, and clients, see [How Pangolin Works](/about/how-pangolin-works). This page focuses on how those pieces are implemented and how traffic moves through the system.
|
||||
|
||||
<Frame caption="High-level Pangolin architecture">
|
||||
<img src="/images/system-diagram.png" alt="Pangolin system architecture diagram"/>
|
||||
</Frame>
|
||||
|
||||
## Control Plane
|
||||
|
||||
The control plane is the Pangolin server application. It stores organization state in a database, exposes the dashboard and REST API, handles authentication and authorization, and pushes configuration to every node, site connector, and client in real time over WebSocket.
|
||||
|
||||
Responsibilities include:
|
||||
|
||||
- **Policy and identity** — users, roles, clients, machines, and access rules that determine which resources each principal can reach
|
||||
- **Resource and site definitions** — targets, destinations, routing preferences, certificates, and health-check configuration
|
||||
- **Orchestration** — coordinates tunnel peers, DNS records, and ingress routes as you create or change resources
|
||||
- **Telemetry** — connection state, relay status, and operational signals surfaced in the dashboard
|
||||
|
||||
You can run the control plane yourself ([self-hosted](/self-host/quick-install)) or use [Pangolin Cloud](https://app.pangolin.net/auth/signup), where the control plane is fully managed. In both cases, the same orchestration model applies: connectors and nodes pull config from the control plane rather than accepting inbound management connections.
|
||||
|
||||
## Nodes
|
||||
|
||||
A **node** is the networking edge of your Pangolin deployment. It terminates inbound traffic from the public internet, manages WireGuard tunnels, and relays client traffic when a direct peer path is unavailable.
|
||||
|
||||
On a self-hosted deployment, your Pangolin server runs as a single node by default. [Enterprise clustering](/self-host/clustering/understanding-clustering) spreads multiple nodes behind a load balancer with shared database state. On [Pangolin Cloud](/manage/remote-node/understanding-nodes), you can add **remote nodes**: infrastructure you operate that still receives configuration from the cloud control plane.
|
||||
|
||||
Each node runs several cooperating processes:
|
||||
|
||||
| Role | Engineering codename | What it does |
|
||||
| --- | --- | --- |
|
||||
| Ingress / reverse proxy | Traefik | Terminates TLS, routes HTTP(S) and protocol-aware public resources, and forwards authenticated requests into the tunnel fabric |
|
||||
| Tunnel manager | Gerbil | Maintains WireGuard peers for site connectors and clients, performs SNI-based routing, and relays UDP when hole punching fails |
|
||||
| Auth middleware | Badger | Traefik plugin that enforces Pangolin authentication on public resources via forward auth |
|
||||
|
||||
The control plane writes certificates, Traefik router definitions, and WireGuard peer lists that these processes consume. Nodes do not need direct database access from site connectors; they only need reachability to the control plane and open listener ports on the public side.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="DNS and port requirements" icon="network-wired" href="/self-host/dns-and-networking">
|
||||
Required listener ports, firewall rules, and DNS records for a node.
|
||||
</Card>
|
||||
|
||||
<Card title="Clustering for high availability" icon="server" href="/self-host/clustering/understanding-clustering">
|
||||
Multi-node Enterprise deployments with shared state and failover.
|
||||
</Card>
|
||||
|
||||
<Card title="Remote nodes on Pangolin Cloud" icon="circle-nodes" href="/manage/remote-node/understanding-nodes">
|
||||
Run your own node while using the cloud control plane for management.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Site Connectors
|
||||
|
||||
A **site connector** runs on a remote network (server, VM, container, or appliance) and is the bridge between your private LAN and the platform. For public resources it maintains persistent **outbound** connections to a Pangolin node so ingress can reverse-proxy into the LAN. For private resources, clients form a **peer-to-peer** path to the connector; the node is only in that data path as a relay when hole punching fails.
|
||||
|
||||
The primary connector is a **Pangolin Site**. The site opens:
|
||||
|
||||
1. A **WebSocket** to the control plane for configuration, health, and coordination
|
||||
2. A **WireGuard** tunnel to the node's tunnel manager (Gerbil) for encrypted data traffic
|
||||
|
||||
Site connectors are deny-by-default proxies. Deploying one does not expose hosts on the network; traffic is forwarded only for [resources](/manage/resources/understanding-resources) you define and grant access to. The connector resolves backend targets on the remote network and delivers packets there.
|
||||
|
||||
Pangolin Sites support the full feature set: public and private resources, protocol-aware proxies, multi-site routing, health checks, and edge TLS for private HTTP. Other connector types ([local](/manage/sites/understanding-sites#local-site) and [basic WireGuard](/manage/sites/understanding-sites#basic-wireguard-site)) exist for specialized self-hosted cases.
|
||||
|
||||
<Card title="Understanding sites" icon="plug" href="/manage/sites/understanding-sites">
|
||||
Site types, deployment models, and connector capabilities.
|
||||
</Card>
|
||||
|
||||
## Clients
|
||||
|
||||
**Clients** are endpoint agents on user devices or machines. They authenticate to the control plane, receive an access control list, and establish WireGuard tunnels so users can reach [private resources](/manage/resources/understanding-resources#private-resource-types).
|
||||
|
||||
Two client classes share the same tunnel stack:
|
||||
|
||||
- **User devices** — GUI apps on desktop and mobile; users sign in with their Pangolin identity or SSO
|
||||
- **Machines** — CLI clients for servers and automation; authenticate with an ID and secret
|
||||
|
||||
### Shared client stack (Olm)
|
||||
|
||||
Every Pangolin client shares a common networking core called **Olm** (engineering codename). Olm holds the WireGuard tunnel logic, hole punching and relay negotiation, route installation, and DNS overrides that all clients rely on. Platform apps embed or invoke Olm rather than reimplementing that stack themselves.
|
||||
|
||||
How Olm is hosted depends on the client:
|
||||
|
||||
- **macOS and iOS** — Olm runs inside the platform's network extension
|
||||
- **Windows and Android** — Olm runs as the tunnel service behind the native app
|
||||
- **Pangolin CLI** — spawns Olm as a subprocess and manages it over a local API
|
||||
- **Olm CLI** — exposes Olm directly for minimal machine-client deployments
|
||||
|
||||
Olm is an internal building block, not a product surface. Use the [native clients](/manage/clients/install-client) built for each operating system for the best experience, support, and integration with OS networking APIs. Direct Olm usage is limited to advanced machine-client and automation scenarios; see [Olm (Advanced)](/manage/clients/install-client#olm-advanced) if you need that path.
|
||||
|
||||
Once connected, the client installs routes for each authorized destination. Pangolin selects the correct site connector automatically; users connect to resources, not to sites directly. See [multi-site routing](/manage/resources/private/multi-site-routing) for how failover works when a resource spans multiple connectors.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Understanding clients" icon="desktop" href="/manage/clients/understanding-clients">
|
||||
User devices, machine clients, and how access is granted.
|
||||
</Card>
|
||||
|
||||
<Card title="Install clients" icon="download" href="/manage/clients/install-client">
|
||||
Downloads for Mac, Windows, Linux, iOS, iPadOS, and Android.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Peer-to-Peer and Relay Paths
|
||||
|
||||
Client-to-site traffic does not always traverse your node. Pangolin negotiates the best available path for each tunnel.
|
||||
|
||||
### Direct peer-to-peer (hole punching)
|
||||
|
||||
By default, the control plane coordinates **NAT hole punching** so a client and site connector can form a direct WireGuard peer connection. Traffic then flows client ↔ site without passing through the node's relay layer. This path typically offers lower latency and less bandwidth use on the node.
|
||||
|
||||
### Relay through the node
|
||||
|
||||
When hole punching fails (restrictive NAT, symmetric NAT, or blocked UDP), the client and site fall back to **relaying** through the node's tunnel manager (Gerbil). Gerbil listens on UDP port 21820 by default, accepts the client's WireGuard packets, and forwards them into the site connector's existing tunnel. The connection stays encrypted end to end; only the network path changes.
|
||||
|
||||
Site connectors use a separate default port (51820 UDP) for their outbound tunnels to the node. Relay is optional and can be disabled per client if you require direct paths only.
|
||||
|
||||
<Card title="NAT traversal" icon="route" href="/manage/clients/nat-traversal">
|
||||
Hole punching vs relay, how to check `isRelay` status, and tuning options.
|
||||
</Card>
|
||||
|
||||
## Traffic Paths
|
||||
|
||||
Two dominant flows cover most deployments.
|
||||
|
||||
### Inbound: public resources
|
||||
|
||||
Public resources accept traffic from the internet on your node's ingress layer.
|
||||
|
||||
```
|
||||
Internet → Node ingress (Traefik) → Auth (Badger) → Tunnel (Gerbil) → Site connector (Newt) → Backend target
|
||||
```
|
||||
|
||||
HTTP, HTTPS, SSH, RDP, and VNC public resources terminate authentication at the node before traffic enters the tunnel. TCP and UDP public resources bind to node ports and proxy raw traffic without an auth layer. Certificate issuance, router config, and access rules are all driven by the control plane.
|
||||
|
||||
<Card title="Public resources" icon="globe" href="/manage/resources/understanding-resources#public-resource-types">
|
||||
Protocol types, targets, and how ingress maps to backends.
|
||||
</Card>
|
||||
|
||||
### Outbound: private resources via client
|
||||
|
||||
Private resources are reachable only when a client is connected and authorized.
|
||||
|
||||
```
|
||||
Client ↔ (direct peer or relay via Gerbil) ↔ Site connector (Newt) → Destination on remote network
|
||||
```
|
||||
|
||||
The client installs routes for IP, CIDR, or FQDN destinations. The site connector resolves and delivers traffic on the remote LAN. For private HTTP, TLS can terminate at the connector so applications stay off the public internet entirely.
|
||||
|
||||
<Card title="Private destinations" icon="lock" href="/manage/resources/private/destinations">
|
||||
How IP, CIDR, FQDN, and alias destinations are resolved and routed.
|
||||
</Card>
|
||||
|
||||
## Component Reference
|
||||
|
||||
Pangolin's open-source repositories map to the roles above. Codenames are used throughout the codebase and deployment tooling.
|
||||
|
||||
| Component | Repository | Role |
|
||||
| --- | --- | --- |
|
||||
| Pangolin | [fosrl/pangolin](https://github.com/fosrl/pangolin) | Control plane server |
|
||||
| Clients | [fosrl/cli](https://github.com/fosrl/cli), [fosrl/windows](https://github.com/fosrl/windows), [fosrl/apple](https://github.com/fosrl/apple), [fosrl/android](https://github.com/fosrl/android) | Endpoint agents |
|
||||
| Site Connector | [fosrl/cli](https://github.com/fosrl/cli) | Connect remote networks for public and private resources |
|
||||
| Gerbil | [fosrl/gerbil](https://github.com/fosrl/gerbil) | Node tunnel manager and relay |
|
||||
| Badger | [fosrl/badger](https://github.com/fosrl/badger) | Traefik forward-auth middleware |
|
||||
| Newt | [fosrl/newt](https://github.com/fosrl/newt) | Shared site networking stack |
|
||||
| Olm | [fosrl/olm](https://github.com/fosrl/olm) | Shared client networking stack (tunnels, NAT traversal, DNS) |
|
||||
|
||||
For local development setup and how these repositories interact in a dev environment, see [Contributing](/development/contributing).
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: "Introduction to Pangolin"
|
||||
description: "Connect and protect your users with an open-source networking and security platform"
|
||||
---
|
||||
|
||||
Pangolin is an open-source SASE platform with one mission: connect and protect your users, wherever they are.
|
||||
|
||||
It treats networking and security as a single system. Identity, sites, access control, privileged access, and an identity-aware AI gateway share one policy model. The idea is the same as platforms like Cloudflare One, Zscaler, and Prisma Access, but Pangolin is open, self-hostable, and built to stay light enough that administrators actually enjoy running it.
|
||||
|
||||
<Frame caption="Screenshot of resources page from the Pangolin Dashboard.">
|
||||
<img src="/images/hero.png" alt="Pangolin Dashboard"/>
|
||||
</Frame>
|
||||
|
||||
## Why Pangolin Exists
|
||||
|
||||
Legacy SASE platforms got the idea right: connectivity and security belong together. They delivered it as a heavyweight, closed, cloud-locked stack. Pangolin does that unification in the open, on infrastructure you control, and simple enough to deploy yourself.
|
||||
|
||||
- **Open source and auditable.** You can see how traffic is handled and how access decisions get made.
|
||||
- **Networking and security as one platform.** Connecting users and protecting them happen together, with one identity and policy model.
|
||||
- **Lightweight by design.** The control plane runs on a modest server. A user-space connector sits in your private networks.
|
||||
- **Zero trust from day one.** Access is granted per resource, with identity providers, roles, and audit logs.
|
||||
- **Run it your way.** Self-host or use [Pangolin Cloud](https://app.pangolin.net/auth/signup).
|
||||
|
||||
## Core Concepts
|
||||
|
||||
Pangolin is organized around a few pieces you will use everywhere:
|
||||
|
||||
1. A **server** is the control plane. It holds identity, policy, and coordination. You can use [Pangolin Cloud](/about/pangolin-cloud-vs-self-hosted) or [self-host](/self-host/quick-install) the same software.
|
||||
2. **[Sites](/manage/sites/understanding-sites)** connect the networks where your applications and infrastructure live.
|
||||
3. **[Resources](/manage/resources/understanding-resources)** are the specific things users are allowed to reach: web apps, SSH, desktops, private hosts, and AI providers.
|
||||
4. **[Clients](/manage/clients/understanding-clients)** give devices a private path to those resources.
|
||||
|
||||
<Card title="How Pangolin Works" icon="brain" href="/about/how-pangolin-works">
|
||||
Learn the fundamentals: server, sites, resources, clients, and how they fit together.
|
||||
</Card>
|
||||
|
||||
## Explore the Platform
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Sites" icon="plug" href="/manage/sites/understanding-sites">
|
||||
Connect private networks with lightweight connectors so authorized users can reach what lives there.
|
||||
</Card>
|
||||
<Card title="Resources" icon="link" href="/manage/resources/understanding-resources">
|
||||
Define the apps, hosts, SSH sessions, desktops, and AI endpoints users can access.
|
||||
</Card>
|
||||
<Card title="Identity and Access" icon="users" href="/manage/access-control/create-user">
|
||||
Use built-in users or your identity provider. Grant roles per resource and keep an audit trail.
|
||||
</Card>
|
||||
<Card title="AI Gateway" icon="sparkles" href="/manage/ai/overview">
|
||||
Put identity, budgets, and session history in front of cloud and self-hosted model APIs.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Keep Reading
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Cloud vs. Self-Hosted" icon="server" href="/about/pangolin-cloud-vs-self-hosted">
|
||||
Choose a managed control plane or run the same software on your own infrastructure.
|
||||
</Card>
|
||||
<Card title="Pangolin vs. Proxy vs. VPN" icon="book" href="/about/pangolin-vs-reverse-proxy-vs-vpn">
|
||||
How Pangolin relates to traditional reverse proxies and VPNs, and what it does beyond either.
|
||||
</Card>
|
||||
<Card title="Pangolin vs. Bifrost vs. LiteLLM" icon="sparkles" href="/about/pangolin-vs-bifrost-vs-litellm">
|
||||
How Pangolin's identity-aware AI Gateway compares to dedicated LLM gateways.
|
||||
</Card>
|
||||
<Card title="System Architecture" icon="diagram-project" href="/development/system-architecture">
|
||||
A technical picture of the control plane, nodes, connectors, and how traffic moves.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Deployment Options
|
||||
|
||||
See [Cloud vs. Self-Hosted](/about/pangolin-cloud-vs-self-hosted) for the differences, including [remote nodes](/manage/remote-node/understanding-nodes) as a hybrid.
|
||||
|
||||
<Columns cols={2}>
|
||||
<Card title="Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup">
|
||||
A managed control plane. Install sites and clients, then define resources. Optionally self-host a node to keep traffic on your network.
|
||||
</Card>
|
||||
<Card title="Self-host Pangolin" icon="server" href="/self-host/quick-install">
|
||||
Run a fully isolated Pangolin server. Install Community Edition or Enterprise Edition with the quick installer.
|
||||
</Card>
|
||||
</Columns>
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: "Device Approvals"
|
||||
description: "Only allow trusted devices to connect to an organization"
|
||||
---
|
||||
<Note>
|
||||
Only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) and [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
By default, any client configured with valid credentials can connect to an organization. To enhance security, you can enable device approvals, which require each new device to be manually approved by an administrator before it can connect.
|
||||
|
||||
When device approvals are enabled, the first time a user connects a new device to the organization, the device will be marked as "Pending Approval." An administrator must then review and approve the device in the management console before it can access organization resources.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/device_waiting_approval.png" alt="Device marked pending approval in the Pangolin dashboard" />
|
||||
</Frame>
|
||||
|
||||
All approvals can also be managed from a central page as they stream in to allow admins to approve or deny devices quickly.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/approvals_page.png" alt="Approvals page listing pending devices in the Pangolin dashboard" />
|
||||
</Frame>
|
||||
|
||||
## Enabling Device Approvals
|
||||
|
||||
Device approvals are enabled on a per-role basis. To enable device approvals for a role, follow these steps:
|
||||
|
||||
1. Click on the **Roles** tab.
|
||||
2. Select the role you want to enable device approvals for.
|
||||
3. Toggle the **Require Device Approval** option to enable it.
|
||||
4. Save your changes.
|
||||
|
||||
Once enabled, any new user connecting with that role will require approval from an administrator before it can access organization resources.
|
||||
|
||||
<Tip>
|
||||
You cannot enable device approvals for the "Admin" role.
|
||||
</Tip>
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
title: "Change Password"
|
||||
description: "Change or reset your Pangolin account password"
|
||||
---
|
||||
### Change Password
|
||||
|
||||
If you're already logged in, you can change your password by clicking your profile menu (top right) and selecting Change Password. You will be required to confirm your old password and enter a new password.
|
||||
|
||||
<Tip>
|
||||
If you want to require password changes at regular intervals for better security, check out the [password rotation documentation](/manage/access-control/password-rotation).
|
||||
</Tip>
|
||||
|
||||
### Reset Password
|
||||
|
||||
If you forgot your password, you can use the reset password function. On the login page, select Forgot your password?. This will ask for your username or email. A reset code will be sent to that email to complete the reset.
|
||||
|
||||
If you're self‑hosting Pangolin, you will need an SMTP server configured to send emails. If you don't have one configured, the server will log the reset code to the server logs for you to retrieve and use to reset the password.
|
||||
|
||||
### Force Reset Server Admin Password
|
||||
|
||||
For self‑hosted Pangolin, if you need to force reset your server admin account password server‑side, you can use the internal CLI. [See more here](/self-host/advanced/container-cli-tool).
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: "Users and Roles"
|
||||
description: "Add internal or external users to your organization and manage roles"
|
||||
---
|
||||
|
||||
## Users in Organizations
|
||||
|
||||
Users can be added to organizations. When a user is added to Pangolin, there is a global user object and an organization‑specific user object that links that user to the organization. This allows a user to exist in one or more organizations.
|
||||
|
||||
<Tip>
|
||||
Because the global user exists and a per‑organization user exists, a user invited to an organization may be able to create a new organization. You can disable this functionality via a flag in the config file in self‑hosted Pangolin. [Check out the config file documentation](/self-host/advanced/config-file#feature-flags).
|
||||
</Tip>
|
||||
|
||||
When removing a user from an organization, their account still exists. To completely delete their account, visit the server admin panel as the server admin and delete the global user in the users table.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/users-table.png" alt="Users table in the Pangolin dashboard"/>
|
||||
</Frame>
|
||||
|
||||
### Internal Users
|
||||
|
||||
An internal user is an identity managed by Pangolin only. When adding the user, you will receive an invite link. The user needs to use this link to either accept the invite, or create an account for the first time and accept the invite.
|
||||
|
||||
### External Users
|
||||
|
||||
An external user is an identity managed by an external identity provider. When creating an external user, you will need to select an existing identity provider added to Pangolin. [Check out the documentation on adding an IDP](/manage/identity-providers/add-an-idp).
|
||||
|
||||
An identity provider may have auto‑provisioning enabled. This means new users who log in with the IDP are automatically created and you do not need to manually create the user. [Check out the auto‑provisioning documentation](/manage/identity-providers/auto-provisioning).
|
||||
|
||||
Even if auto‑provisioning is enabled, you can still manually create users.
|
||||
|
||||
## Roles
|
||||
|
||||
Roles are how you group users in an organization. A user can belong to more than one role, for example Member, Admin, Contractor, Operations, or any custom roles you define. You use roles with RBAC on resources so access follows those groups: only Operations might reach production resources, while only Contractors might reach test environments, and so on.
|
||||
|
||||
On each resource, you define which roles are allowed to access it. A user’s effective access is the union of all resources their roles can reach: they can use any resource that at least one of their assigned roles is permitted to access.
|
||||
|
||||
You can create as many custom roles as you need in Pangolin. Each role has a name and a description. The name is the display label and also acts as the unique identifier, so two roles cannot share the exact same name.
|
||||
|
||||
To change which roles a user has, open that user’s settings and select the roles they should belong to.
|
||||
|
||||
To see how to configure SSH access on a role see [SSH Access](/manage/ssh#configuring-role-permissions).
|
||||
|
||||
<Note>
|
||||
Assigning more than one role to a user is only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition). In other editions, only one role per user is supported.
|
||||
</Note>
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
title: "Forwarded Headers"
|
||||
description: "Learn how Pangolin forwards user identity information to your backend applications through HTTP headers"
|
||||
---
|
||||
Pangolin can forward user identity information to your backend applications through custom HTTP headers. This allows your applications to receive user details directly from the request headers, enabling integration with Pangolin's authentication system. [AI Gateway](/manage/ai/overview) resources send the same headers to the upstream provider when the caller is a known user. See [Identity Headers](/manage/ai/providers/configuration#identity-headers).
|
||||
|
||||
<Info>
|
||||
Forwarded headers are only available when using authentication methods that provide user identity information.
|
||||
</Info>
|
||||
|
||||
## Supported Headers
|
||||
|
||||
Pangolin forwards the following headers to your backend when user identity is available:
|
||||
|
||||
| Header | Description | Example |
|
||||
|--------|-------------|---------|
|
||||
| `Remote-User` | Unique username or user ID | `user_123` |
|
||||
| `Remote-Email` | User's email address | `john.doe@example.com` |
|
||||
| `Remote-Name` | User's full name | `John Doe` |
|
||||
| `Remote-Role` | User's role or group membership | `admin` |
|
||||
|
||||
## Authentication Methods
|
||||
|
||||
### Headers Available
|
||||
|
||||
These authentication methods provide user identity information and will include the forwarded headers:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Single Sign-On (SSO)" icon="users">
|
||||
Full user identity information including username, email, and name.
|
||||
</Card>
|
||||
|
||||
<Card title="Email-based One Time Passcode (OTP)" icon="envelope">
|
||||
Only `Remote-Email` is provided, set to the whitelisted address the visitor authenticated with. `Remote-User`, `Remote-Name`, and `Remote-Role` are not available since there is no associated user account.
|
||||
</Card>
|
||||
|
||||
<Card title="Shareable Links" icon="link">
|
||||
Only available if the link was created with an associated user account. In that case, full user identity information is forwarded, the same as SSO. Links created without an associated user do not provide identity headers.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
### Headers Not Available
|
||||
|
||||
These authentication methods do not provide user identity information:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="PIN Code" icon="hashtag">
|
||||
No user identity - only access control.
|
||||
</Card>
|
||||
|
||||
<Card title="Password" icon="lock">
|
||||
No user identity - only access control.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## AI Gateway
|
||||
|
||||
[AI Gateway](/manage/ai/overview) resources forward the same `Remote-User`, `Remote-Email`, `Remote-Name`, and `Remote-Role` headers to the upstream model API when Pangolin knows the user:
|
||||
|
||||
- A public resource called with an [identity key](/manage/ai/virtual-api-keys#identity-keys)
|
||||
- A public resource called with a [manual key](/manage/ai/virtual-api-keys#manual-keys) attributed to a user
|
||||
- A private AI Gateway resource called from a connected [Pangolin client](/manage/clients/install-client)
|
||||
|
||||
An unattributed manual key authenticates without sending these headers. Details are in [Identity Headers](/manage/ai/providers/configuration#identity-headers).
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: "Shareable Links"
|
||||
description: "Create Links and use access tokens for browser or programmatic access."
|
||||
---
|
||||
|
||||
Links are special URLs that grant access to one resource without requiring the recipient to sign in as a Pangolin user. Anyone with a web browser on the internet can access the resource if they have a valid Link.
|
||||
|
||||
When you create a Link, Pangolin gives you two ways to use it:
|
||||
|
||||
- **Link**: This is a Pangolin-hosted URL that validates the validity of the Link and then redirects them to the resource.
|
||||
- **Access Token Usage**: Use this only when making direct requests to the resource URL from scripts, tools, or integrations.
|
||||
|
||||
## Create a Link
|
||||
|
||||
From the resource authentication flow, create a Link by:
|
||||
|
||||
1. Choosing the target resource.
|
||||
2. Adding a title if you want the link to be easy to identify later.
|
||||
3. Setting an expiration, or enabling **Never expire** if the link should stay valid until you revoke it.
|
||||
4. Copying the generated link or access-token details immediately after creation.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/links-create-modal.png" alt="Create a Link modal" />
|
||||
</Frame>
|
||||
|
||||
<Warning>
|
||||
Anyone with the Link or access token can use it. Treat both like credentials.
|
||||
</Warning>
|
||||
|
||||
## Use the Access Token
|
||||
|
||||
Pangolin can accept a Link access token in either the query string or request headers.
|
||||
|
||||
If you are sending access to a person, use the copied **Link** shown at the top of the modal.
|
||||
|
||||
Use **Access Token Usage** only when you are calling the resource URL directly on each request.
|
||||
|
||||
This is why the two URLs often look different:
|
||||
|
||||
- The **Link** is usually on your Pangolin domain.
|
||||
- The **Access Token Usage** examples use the resource URL directly.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/links-access-token-usage.png" alt="Access token usage examples for a shareable link" />
|
||||
</Frame>
|
||||
|
||||
### Query Parameter
|
||||
|
||||
Pangolin accepts the access token in the `p_token` query parameter:
|
||||
|
||||
```bash
|
||||
curl "https://resource.example.com/?p_token=<token-id>.<access-token>"
|
||||
```
|
||||
|
||||
The query-string value is the token ID and token joined with a `.`.
|
||||
|
||||
Some deployments may use a different query parameter name.
|
||||
|
||||
The query parameter must be sent in every request to the resource, not just the first time.
|
||||
|
||||
### Request Headers
|
||||
|
||||
By default, Pangolin accepts these headers:
|
||||
|
||||
- `P-Access-Token-Id`
|
||||
- `P-Access-Token`
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl \
|
||||
-H "P-Access-Token-Id: <token-id>" \
|
||||
-H "P-Access-Token: <access-token>" \
|
||||
"https://resource.example.com/"
|
||||
```
|
||||
|
||||
This is the same token data as the query-string form, split into two headers instead of `<token-id>.<access-token>`.
|
||||
|
||||
Some deployments may use different header names.
|
||||
|
||||
The headers must be sent in every request to the resource, not just the first time.
|
||||
|
||||
## Expiration and Revocation
|
||||
|
||||
- Expiring links stop working automatically when their lifetime ends.
|
||||
- Non-expiring links remain valid until you delete them.
|
||||
- Deleting the Link revokes both the Link and its access token.
|
||||
|
||||
## Important Notes
|
||||
|
||||
- Links are best for targeted sharing and automation, not broad long-term access.
|
||||
- Link-based access does not carry per-user identity headers to the upstream app. For identity-aware upstream integrations, see [Forwarded Headers](/manage/access-control/forwarded-headers).
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: "Custom Login Page"
|
||||
description: "Configure a custom authentication page URL for your organization"
|
||||
---
|
||||
<Note>
|
||||
Custom auth pages are only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup).
|
||||
</Note>
|
||||
|
||||
Custom organization authentication pages let you serve the login page at your own domain instead of the default `app.pangolin.net`. This provides better user experience and brand consistency.
|
||||
|
||||
## Benefits
|
||||
|
||||
**For Resource Authentication:**
|
||||
- Users are redirected to your custom domain for login
|
||||
- Familiar domain builds trust and security awareness
|
||||
- Consistent branding throughout the authentication flow
|
||||
|
||||
**For Identity Provider Integration:**
|
||||
- Centralized login page for your organization
|
||||
- Choose between multiple login methods (Google, Azure, etc.)
|
||||
- Platform SSO: login once, access all Pangolin resources
|
||||
- Direct access to the Pangolin management dashboard
|
||||
|
||||
<Frame>
|
||||
<img src="/images/org-auth-page.png" alt="Organization login page with multiple login methods" />
|
||||
</Frame>
|
||||
|
||||
## Configuration
|
||||
|
||||
1. Go to **Settings** in your organization sidebar
|
||||
2. Use the domain picker to select your custom domain
|
||||
3. Save your changes
|
||||
|
||||
<Note>
|
||||
You need to add a custom domain to your organization first. Free domains (`*.tunneled.to`, `*.hostlocal.app`, etc.) cannot be used for auth pages. [Learn how to add domains](/manage/domains)
|
||||
</Note>
|
||||
|
||||
<Frame>
|
||||
<img src="/images/set-org-auth-page-domain.png" alt="Domain picker for the auth page in Pangolin settings" />
|
||||
</Frame>
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
title: "Multi-Factor Authentication"
|
||||
description: "Enable and manage two-factor authentication and enforcement for your organization"
|
||||
---
|
||||
Pangolin supports two‑factor authentication (2FA) for Pangolin user accounts.
|
||||
|
||||
### Enable or Disable 2FA
|
||||
|
||||
- Click your profile menu (top right) to enable two‑factor authentication.
|
||||
- You will need to confirm your password and code before enabling/disabling 2FA.
|
||||
|
||||
### Supported Methods
|
||||
|
||||
- **Time‑based one‑time code (TOTP)**: Use an authenticator app (e.g., 1Password, Google Authenticator).
|
||||
- **Push via email**: Contact sales to enable.
|
||||
- **Push via Duo**: Contact sales to enable.
|
||||
|
||||
### Enforcement
|
||||
|
||||
<Note>
|
||||
Two‑factor enforcement (requiring 2FA at login) is available in [Enterprise Edition](/self-host/enterprise-edition) only.
|
||||
</Note>
|
||||
|
||||
To enable enforcement, go to Organization Settings and toggle 2FA enforcement in the Security section.
|
||||
|
||||
- Enforcement is configured per organization.
|
||||
- MFA enforcement only applies to internal Pangolin user accounts. This policy does not apply to accounts linked to an external identity provider.
|
||||
- When enforced, users must enable 2FA before accessing the organization or its resources.
|
||||
- Users without 2FA will see a prompt directing them to enable it before proceeding.
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
title: "Password Rotation"
|
||||
description: "Configure password expiration and rotation requirements for your organization"
|
||||
---
|
||||
By default, Pangolin does not require passwords to be rotated on a regular basis. However, password rotation can be required on a per‑organization basis.
|
||||
|
||||
### Configuration
|
||||
|
||||
<Note>
|
||||
Password expiry and rotation is an [Enterprise Edition](/self-host/enterprise-edition)-only feature.
|
||||
</Note>
|
||||
|
||||
To enable password rotation, go to Organization Settings and select a maximum password age in the Security section. After the configured period expires, users will be prompted to change their password when accessing the organization or its resources.
|
||||
|
||||
- Password rotation is enforced on a per‑organization basis.
|
||||
- Password rotation only applies to internal Pangolin user accounts. This policy does not apply to accounts linked to an external identity provider.
|
||||
- Users who need to change their password will see a prompt directing them to update it before proceeding.
|
||||
@@ -0,0 +1,108 @@
|
||||
---
|
||||
title: "Rules"
|
||||
description: "Configure rules to allow or deny access to resources without authentication"
|
||||
---
|
||||
Rules allow you to either "allow" and bypass the Pangolin auth system (no pin, login, password), or "deny" and fully reject the request. After you create a resource you can select the "Rules" tab on the sidebar and enable rules. On public resources, you can also define rules in a [resource policy](/manage/resources/public/resource-policies) and share them across multiple resources.
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Bypass Auth" icon="check">
|
||||
Bypass authentication completely for matching requests. Users can access resources without any login or PIN.
|
||||
</Card>
|
||||
|
||||
<Card title="Block Access" icon="x">
|
||||
Completely reject requests that match the rule. Useful for blocking admin paths or sensitive endpoints.
|
||||
</Card>
|
||||
|
||||
<Card title="Pass to Auth" icon="x">
|
||||
Pass requests that match the rule to the next stage for user to authenticate with SSO, password, or pin. Useful for enforcing auth on specific paths while allowing others.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Types of Rules
|
||||
|
||||
Rules are processed from top to bottom in order of their priority. This means you can have multiple rules to bypass auth and to just flat deny users at the end.
|
||||
|
||||
Right now you can match on the following items:
|
||||
|
||||
### Path
|
||||
|
||||
Path match rules allow URL patterns defined with plain text and wildcards (`*`) that match any characters. Patterns and URLs are split into segments (using `/`), and **each segment is matched individually**.
|
||||
|
||||
#### Examples:
|
||||
|
||||
- `blog/posts`
|
||||
Matches the exact path `/blog/posts`.
|
||||
|
||||
- `blog/*`
|
||||
Matches any path under `/blog` (e.g., `/blog/travel`).
|
||||
|
||||
- `*/2023/*`
|
||||
Matches paths with `/2023/` as a middle segment (e.g., `/news/2023/summary`).
|
||||
|
||||
- `article*`
|
||||
Matches **segments** starting with "article" (e.g., `/article-123`).
|
||||
|
||||
- `*admin*`
|
||||
Matches **segments** containing "admin" (e.g., `/my-admin-panel`).
|
||||
|
||||
- `personal-*/*`
|
||||
Matches paths where the first segment starts with `personal-` and is followed by any segment (e.g., `/personal-blog/post`).
|
||||
|
||||
#### Segment-by-Segment Matching
|
||||
|
||||
- **Normalization:**
|
||||
Both patterns and URLs are split into segments. For example, `/blog/journal/entry` becomes `["blog", "journal", "entry"]`, while `/blog*` becomes `["blog*"]`.
|
||||
|
||||
- **Validation:**
|
||||
Each pattern segment must correspond to a URL segment, and wildcards match zero or more characters within that segment. A pattern like `/blog*` only matches the first segment, so URLs with extra segments require additional placeholders (e.g., `/blog*/*`).
|
||||
|
||||
### Country
|
||||
|
||||
Country match rules allow you to specify allowed or denied countries for requests based on their IP address. This is useful for geo-restrictions or compliance with regional regulations.
|
||||
|
||||
We use a IP database to geolocate the IP address but this is not always accurate. Try to keep it updated, but there may be cases where the location is incorrect.
|
||||
|
||||
Select the "ALL" option to match all countries for allowing or denying access.
|
||||
|
||||
To use country rules, follow this guide to set up the geolocation database: [Enable Geo-location](/self-host/advanced/enable-geolocation).
|
||||
|
||||
### Region
|
||||
|
||||
Region match rules allow you to specify allowed or denied regions for requests based on their IP address. This is useful for geo-restrictions or compliance with regional regulations. Regions are made up of a list of countries in that region (e.g. "EU" includes France, Germany, etc.) so this is a more broad match than country.
|
||||
|
||||
To use region rules, follow this guide to set up the geolocation database: [Enable Geo-location](/self-host/advanced/enable-geolocation).
|
||||
|
||||
### CIDR
|
||||
|
||||
CIDR (Classless Inter-Domain Routing) notation specifies IP address ranges using an IP address and a network prefix length. The format is [IP address]/[prefix length].
|
||||
|
||||
**Examples:**
|
||||
|
||||
- `192.168.1.0/0` - Matches all 256 IPs from 192.168.1.0 to 192.168.1.255
|
||||
- `10.0.0.0/8` - Matches any IP starting with 10 (16.7 million addresses)
|
||||
- `2001:db8::/32` - Matches a range of IPv6 addresses
|
||||
- `0.0.0.0/0` - Matches all IPv4 addresses
|
||||
|
||||
<Note>
|
||||
The prefix length (1-32 for IPv4, 1-128 for IPv6) determines how many bits from the left are fixed. Smaller prefix numbers match larger ranges.
|
||||
</Note>
|
||||
|
||||
### IP
|
||||
|
||||
Pretty simple: you can match on simply an IP address like your home IP to bypass auth. This is the same as entering a /32 CIDR.
|
||||
|
||||
### ASN
|
||||
|
||||
ASN (Autonomous System Number) match rules allow you to specify allowed or denied ASNs for requests based on their IP address. This is useful for blocking or allowing traffic from specific ISPs or organizations.
|
||||
|
||||
To use ASN rules, follow this guide to set up the ASN lookup database: [Enable ASN Lookup](/self-host/advanced/enable-asn-lookup).
|
||||
|
||||
**Examples:**
|
||||
|
||||
- `23.234.134.32`
|
||||
- `34.45.245.64`
|
||||
- `192.168.1.1`
|
||||
|
||||
### Community Contributed Rules
|
||||
|
||||
Some common bypass paths for common self hosted apps can be found [in the community contributed rules](/self-host/community-guides/rules).
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
title: "Security Keys"
|
||||
description: "Use security keys for passwordless login to your Pangolin account"
|
||||
---
|
||||
You can log in with security keys, also known as passwordless login. On the login page, there is an option below the login button to Log in with security key.
|
||||
|
||||
### Add a Security Key
|
||||
|
||||
To add a security key, you must first be logged in. Then click your profile menu (top right) and select Add Security Keys. Follow the steps to add your key.
|
||||
|
||||
Once a security key is added to your account, you can select the Continue with security key option the next time you log in.
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: "Session Length"
|
||||
description: "Configure maximum session length and expiration policies for your organization"
|
||||
---
|
||||
By default, Pangolin keeps extending a session indefinitely if a user is actively using it. If a user is not actively using the session, it will expire after 30 days.
|
||||
|
||||
However, you can require users to log in at regular intervals by enforcing maximum session lengths on a per‑organization basis.
|
||||
|
||||
### Configuration
|
||||
|
||||
<Note>
|
||||
Session length enforcement is an [Enterprise Edition](/self-host/enterprise-edition)-only feature.
|
||||
</Note>
|
||||
|
||||
To enable session length enforcement, go to Organization Settings and set a maximum session length in the Security section. After this amount of time, users will be prompted to log back in to acquire a fresh session.
|
||||
|
||||
- Session length enforcement is configured per organization.
|
||||
- Session length enforcement applies to both internal Pangolin users and users linked to external identity providers.
|
||||
- Users whose session has expired will see a prompt directing them to log in again before proceeding.
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: "Budgets"
|
||||
description: "Cap estimated USD spend or token usage on providers, models, resources, roles, and keys"
|
||||
---
|
||||
|
||||
A budget is a cap on estimated USD spend or token count for a rolling window. Pangolin checks every matching enabled budget before it forwards a request. If **any** one is already at or above its maximum, the call is blocked.
|
||||
|
||||
Budgets are estimates used for enforcement, not a copy of the vendor invoice. See [Model Catalog](/manage/ai/model-catalog) for where USD rates come from. The same recorded usage appears in [Usage Analytics](/manage/ai/usage-analytics).
|
||||
|
||||
## Estimates, Not Invoices
|
||||
|
||||
USD cost uses catalog rates (`in`, `out`, `cache`, `reasoning`) times the token buckets Pangolin recorded for the call. Cache-write tokens use the input rate. Missing reasoning rates use the output rate.
|
||||
|
||||
If the model ID is not in the catalog, the request has no USD amount and does not count toward USD budgets. Token budgets still sum tokens for that call.
|
||||
|
||||
[OpenRouter](/manage/ai/providers/open-router), [Vercel AI Gateway](/manage/ai/providers/vercel-ai-gateway), and [Custom](/manage/ai/providers/custom) may match a catalog ID with approximate pricing.
|
||||
|
||||
If the upstream omits usage, Pangolin estimates tokens from the request and response.
|
||||
|
||||
The check uses **prior** recorded usage. A request that would cross the cap can still complete, so usage can slightly overshoot. Reset periods are trailing windows from now, not calendar months. Daily means the last 24 hours.
|
||||
|
||||
## How Usage Is Calculated
|
||||
|
||||
After each call, Pangolin records prompt, cache-read, cache-write, completion, and reasoning tokens.
|
||||
|
||||
- **Token** budgets sum those counts.
|
||||
- **USD** budgets multiply the same counts by catalog rates, then sum dollars.
|
||||
|
||||
Enforcement totals that history for the budget's scope and [period](#fields), then compares the total to **Maximum Spend**.
|
||||
|
||||
## Where to Set a Budget
|
||||
|
||||
Each budget has exactly one scope. You can add more than one budget on the same scope when the unit or period differs, for example a daily USD cap and a monthly token cap on the same provider.
|
||||
|
||||
| Scope | Where | Why |
|
||||
|---|---|---|
|
||||
| **Provider** | **AI Gateway → Providers → Budget** | Cap everything that uses that upstream key |
|
||||
| **Model** | Provider **Models** tab, on an allow-list entry | Cap one key or glob, such as `gpt-4o` or `gpt-*` |
|
||||
| **Public resource** | Resource **Budget** tab | Cap a specific public gateway |
|
||||
| **Private resource** | Resource **Budget** tab | Cap a client-only gateway |
|
||||
| **Role** | Role form | Cap everyone in that role. Usage attributed to those users is summed |
|
||||
| **Virtual API key** | Create or edit the key | Cap a service key or usage attributed to that key |
|
||||
|
||||
All matching scopes apply together. A call can hit a provider budget, a resource budget, a role budget, and a key budget at once. Exceeding any of them blocks the request.
|
||||
|
||||
## Fields
|
||||
|
||||
The editor uses **Spend Type**, **Maximum Spend**, and **Reset Period**.
|
||||
|
||||
| Field | Options |
|
||||
|---|---|
|
||||
| **Spend Type** | `USD` or `Tokens` |
|
||||
| **Maximum Spend** | A positive maximum in that unit |
|
||||
| **Reset Period** | Hourly, Daily (24 hours), Weekly (7 days), Monthly (30 days), Yearly (365 days), or Lifetime |
|
||||
|
||||
Lifetime covers all recorded usage for that scope.
|
||||
|
||||
## Over Budget
|
||||
|
||||
The gateway returns HTTP **429** with the message `AI usage budget exceeded for this request`. The JSON envelope matches the API the client is calling:
|
||||
|
||||
| Family | Shape |
|
||||
|---|---|
|
||||
| OpenAI | `error.type`: `rate_limit_error`, `error.code`: `rate_limit_exceeded` |
|
||||
| Anthropic | `error.type`: `rate_limit_error` |
|
||||
| Gemini | `error.status`: `RESOURCE_EXHAUSTED` |
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: "Claude Desktop"
|
||||
description: "Connect Claude Desktop's third-party inference to a Pangolin AI Gateway resource"
|
||||
---
|
||||
|
||||
Claude Desktop can route its requests through a third-party inference gateway instead of Anthropic directly. Point it at an AI Gateway resource that has an Anthropic-compatible provider attached — Anthropic itself, Bedrock, Vertex AI, or a custom Anthropic-compatible endpoint. See [AI Gateway Overview](/manage/ai/overview) if you haven't set that up yet.
|
||||
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/claude_desktop_landing.png"
|
||||
alt="Claude Desktop third-party inference settings"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
|
||||
You'll need the resource's URL (its `<endpoint>`) and its API key (`<key>`). Both are on the resource's Keys page.
|
||||
|
||||
<include>../../../../snippets/ai-gateway-key.mdx</include>
|
||||
|
||||
## Steps
|
||||
|
||||
1. Open Claude Desktop, go to the menu in the top left and select **Help > Troubleshooting**, and turn on **Developer Mode**.
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/claude_desktop_troubleshooting.png"
|
||||
alt="Claude Desktop third-party inference settings"
|
||||
/>
|
||||
</Frame>
|
||||
2. From the menu bar, open **Developer**, then **Configure Third-Party Inference…**.
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/claude_desktop_developer.png"
|
||||
alt="Claude Desktop third-party inference settings"
|
||||
/>
|
||||
</Frame>
|
||||
3. Select **Gateway**
|
||||
4. Select **Credential kind** > **Static API Key**.
|
||||
5. Fill in:
|
||||
- **Gateway base URL:** `<endpoint>`
|
||||
- **Gateway API Key:** `<key>`
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/claude_desktop_developer_settings.png"
|
||||
alt="Claude Desktop third-party inference settings"
|
||||
/>
|
||||
</Frame>
|
||||
6. Restart Claude Desktop.
|
||||
|
||||
Send a message to confirm it's working — check the resource's activity in Pangolin's logs if you want to verify traffic is actually flowing through the gateway rather than straight to Anthropic.
|
||||
|
||||
For a private resource, put `none` in the API Key field rather than leaving it blank, and make sure the Pangolin client is installed and connected on the same machine as Claude Desktop.
|
||||
@@ -0,0 +1,116 @@
|
||||
---
|
||||
title: "Claude Code"
|
||||
description: "Connect Claude Code to a Pangolin AI Gateway resource"
|
||||
---
|
||||
|
||||
Claude Code talks the Anthropic Messages API, so the resource you point it at needs an Anthropic-compatible provider attached - Anthropic itself, Bedrock, Vertex AI, or a custom Anthropic-compatible endpoint like Kimi K2. See [AI Gateway Overview](/manage/ai/overview) if you haven't set that up yet.
|
||||
|
||||
|
||||
You'll need the resource's URL (its `<endpoint>`) and its API key (`<key>`). Both are on the resource's Keys page.
|
||||
|
||||
<include>../../../../snippets/ai-gateway-key.mdx</include>
|
||||
|
||||
## Fastest: Pangolin CLI
|
||||
|
||||
[Install the Pangolin CLI](/manage/clients/install-client#quick-install-recommended) if you don't have it, then log in:
|
||||
|
||||
```bash
|
||||
pangolin login
|
||||
```
|
||||
|
||||
Configure Claude Code against a resource:
|
||||
|
||||
```bash
|
||||
pangolin configure claude
|
||||
```
|
||||
|
||||
This prompts you to pick an organization and resource if you have more than one, fetches a key for you if the resource needs one, and writes `~/.claude/settings.json`. To skip the prompts:
|
||||
|
||||
```bash
|
||||
pangolin configure claude --resource <resource-niceid-or-domain>
|
||||
```
|
||||
|
||||
To undo it:
|
||||
|
||||
```bash
|
||||
pangolin configure claude --reset
|
||||
```
|
||||
|
||||
## Manual setup
|
||||
|
||||
Pick the preset that matches your provider.
|
||||
|
||||
### Default (Anthropic)
|
||||
|
||||
Write `~/.claude/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"apiKeyHelper": "echo '<key>'",
|
||||
"env": {
|
||||
"ANTHROPIC_BASE_URL": "<endpoint>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or, for a one-off shell session instead of a permanent settings file:
|
||||
|
||||
```bash
|
||||
export ANTHROPIC_BASE_URL=<endpoint>
|
||||
export ANTHROPIC_API_KEY=<key>
|
||||
claude
|
||||
```
|
||||
|
||||
For a private resource, keep `apiKeyHelper`/`ANTHROPIC_API_KEY` in place and set the key to `none`. Claude Code only calls `apiKeyHelper` if it's present, and falls back to whatever account you're already signed into if it isn't - so removing it sends your traffic to Anthropic instead of through the gateway.
|
||||
|
||||
### Amazon Bedrock
|
||||
|
||||
Use this when the resource has a Bedrock provider attached. Write `~/.claude/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"env": {
|
||||
"ANTHROPIC_MODEL": "claude-sonnet-4-6",
|
||||
"ANTHROPIC_BEDROCK_BASE_URL": "<endpoint>/bedrock",
|
||||
"CLAUDE_CODE_USE_BEDROCK": "1",
|
||||
"CLAUDE_CODE_SKIP_BEDROCK_AUTH": "1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Google Vertex AI
|
||||
|
||||
Use this when the resource has a Vertex AI provider attached. Write `~/.claude/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"env": {
|
||||
"CLOUD_ML_REGION": "global",
|
||||
"ANTHROPIC_VERTEX_PROJECT_ID": "<your-gcp-project-id>",
|
||||
"CLAUDE_CODE_USE_VERTEX": "1",
|
||||
"CLAUDE_CODE_SKIP_VERTEX_AUTH": "1",
|
||||
"ANTHROPIC_VERTEX_BASE_URL": "<endpoint>/v1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Replace `<your-gcp-project-id>` with your actual GCP project ID.
|
||||
|
||||
### Kimi K2 (Moonshot AI)
|
||||
|
||||
Use this when the resource has a custom provider pointed at Moonshot AI's Anthropic-compatible endpoint. Write `~/.claude/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"apiKeyHelper": "echo '<key>'",
|
||||
"env": {
|
||||
"ANTHROPIC_BASE_URL": "<endpoint>/anthropic",
|
||||
"ANTHROPIC_MODEL": "kimi-k2",
|
||||
"ANTHROPIC_DEFAULT_OPUS_MODEL": "kimi-k2",
|
||||
"ANTHROPIC_DEFAULT_SONNET_MODEL": "kimi-k2",
|
||||
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "kimi-k2",
|
||||
"CLAUDE_CODE_SUBAGENT_MODEL": "kimi-k2",
|
||||
"ENABLE_TOOL_SEARCH": "false"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: "Codex"
|
||||
description: "Connect Codex to a Pangolin AI Gateway resource"
|
||||
---
|
||||
|
||||
Codex talks the OpenAI API, so the resource you point it at needs an OpenAI-compatible provider attached — OpenAI, OpenRouter, Vercel AI Gateway, Microsoft Foundry, or a custom OpenAI-compatible endpoint. See [AI Gateway Overview](/manage/ai/overview) if you haven't set that up yet.
|
||||
|
||||
|
||||
You'll need the resource's URL (its `<endpoint>`) and its API key (`<key>`). Both are on the resource's Keys page.
|
||||
|
||||
<include>../../../../snippets/ai-gateway-key.mdx</include>
|
||||
|
||||
## Fastest: Pangolin CLI
|
||||
|
||||
[Install the Pangolin CLI](/manage/clients/install-client#quick-install-recommended) if you don't have it, then log in:
|
||||
|
||||
```bash
|
||||
pangolin login
|
||||
```
|
||||
|
||||
Configure Codex against a resource:
|
||||
|
||||
```bash
|
||||
pangolin configure codex
|
||||
```
|
||||
|
||||
This prompts you to pick an organization and resource if you have more than one, fetches a key for you if the resource needs one, and merges a `pangolin` provider into `~/.codex/config.toml` without touching anything else in that file. To skip the prompts:
|
||||
|
||||
```bash
|
||||
pangolin configure codex --resource <resource-niceid-or-domain>
|
||||
```
|
||||
|
||||
To undo it:
|
||||
|
||||
```bash
|
||||
pangolin configure codex --reset
|
||||
```
|
||||
|
||||
## Manual setup
|
||||
|
||||
Merge this into `~/.codex/config.toml` (honors `$CODEX_HOME` if you've set it):
|
||||
|
||||
```toml
|
||||
model_provider = "pangolin"
|
||||
|
||||
[model_providers.pangolin]
|
||||
name = "Pangolin AI Gateway"
|
||||
base_url = "<endpoint>/v1"
|
||||
wire_api = "responses"
|
||||
env_key = "PANGOLIN_API_KEY"
|
||||
```
|
||||
|
||||
Codex reads the key from the environment variable named in `env_key`, not from the file. Before running Codex, export it in your shell:
|
||||
|
||||
```bash
|
||||
export PANGOLIN_API_KEY=<key>
|
||||
```
|
||||
|
||||
Codex is the one client here that also tolerates no key at all: for a private resource you can drop the `env_key` line and skip the export entirely. Exporting `PANGOLIN_API_KEY=none` works too, so use whichever you find clearer.
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: "Gemini CLI"
|
||||
description: "Connect Gemini CLI to a Pangolin AI Gateway resource"
|
||||
---
|
||||
|
||||
Gemini CLI talks Google's native `generateContent` API, so the resource you point it at needs a Google Gemini (or Vertex AI) provider attached. See [AI Gateway Overview](/manage/ai/overview) if you haven't set that up yet.
|
||||
|
||||
|
||||
You'll need the resource's URL (its `<endpoint>`) and its API key (`<key>`). Both are on the resource's Keys page.
|
||||
|
||||
<include>../../../../snippets/ai-gateway-key.mdx</include>
|
||||
|
||||
## Fastest: Pangolin CLI
|
||||
|
||||
[Install the Pangolin CLI](/manage/clients/install-client#quick-install-recommended) if you don't have it, then log in:
|
||||
|
||||
```bash
|
||||
pangolin login
|
||||
```
|
||||
|
||||
Configure Gemini CLI against a resource:
|
||||
|
||||
```bash
|
||||
pangolin configure gemini
|
||||
```
|
||||
|
||||
This prompts you to pick an organization and resource if you have more than one, fetches a key for you if the resource needs one, and writes `~/.gemini/.env` - the file Gemini CLI loads automatically on every run, regardless of which directory you start it from. To skip the prompts:
|
||||
|
||||
```bash
|
||||
pangolin configure gemini --resource <resource-niceid-or-domain>
|
||||
```
|
||||
|
||||
To undo it:
|
||||
|
||||
```bash
|
||||
pangolin configure gemini --reset
|
||||
```
|
||||
|
||||
## Manual setup
|
||||
|
||||
Write `~/.gemini/.env`:
|
||||
|
||||
```bash
|
||||
GOOGLE_GEMINI_BASE_URL=<endpoint>
|
||||
GEMINI_API_KEY=<key>
|
||||
```
|
||||
|
||||
Gemini CLI also checks a project-local `.env` first (in the current directory or a parent, up to `.git` or your home directory) before falling back to `~/.gemini/.env`, so a project-level file with the same two lines overrides this for just that project.
|
||||
|
||||
Or, for a one-off shell session instead of a permanent file:
|
||||
|
||||
```bash
|
||||
export GOOGLE_GEMINI_BASE_URL=<endpoint>
|
||||
export GEMINI_API_KEY=<key>
|
||||
gemini
|
||||
```
|
||||
|
||||
For a private resource, keep `GEMINI_API_KEY` in place and set it to `none`. Gemini CLI won't start without it set to something.
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: "Open WebUI"
|
||||
description: "Connect Open WebUI to a Pangolin AI Gateway resource"
|
||||
---
|
||||
|
||||
Open WebUI is a self-hosted chat interface. It talks the OpenAI API, so point it at an AI Gateway resource that has an OpenAI-compatible provider attached (OpenAI, OpenRouter, Vercel AI Gateway, Microsoft Foundry, or custom). See [AI Gateway Overview](/manage/ai/overview) if you haven't set that up yet.
|
||||
|
||||
|
||||
You'll need the resource's URL (its `<endpoint>`) and its API key (`<key>`). Both are on the resource's Keys page.
|
||||
|
||||
<include>../../../../snippets/ai-gateway-key.mdx</include>
|
||||
|
||||
## If you're running Open WebUI for the first time
|
||||
|
||||
```bash
|
||||
docker run -d -p 3000:8080 \
|
||||
-e OPENAI_API_BASE_URL=<endpoint>/v1 \
|
||||
-e OPENAI_API_KEY=<key> \
|
||||
-v open-webui:/app/backend/data \
|
||||
--name open-webui \
|
||||
--restart always \
|
||||
ghcr.io/open-webui/open-webui:main
|
||||
```
|
||||
|
||||
Open `http://localhost:3000`, finish the first-run account setup, and your Pangolin gateway is already the active connection.
|
||||
|
||||
## If Open WebUI is already running
|
||||
|
||||
Go to **Settings → Connections** and add a connection:
|
||||
|
||||
- **URL:** `<endpoint>/v1`
|
||||
- **Key:** `<key>`
|
||||
|
||||
{/*
|
||||
Screenshot: `/images/ai/openwebui-connections.png`
|
||||
Open WebUI's Settings → Connections page with the Pangolin gateway URL and key filled in.
|
||||
<Frame>
|
||||
<img src="/images/ai/openwebui-connections.png" alt="Open WebUI connection settings" />
|
||||
</Frame>
|
||||
*/}
|
||||
|
||||
Save, then open a new chat - the models exposed by your resource's attached provider(s) show up in the model picker.
|
||||
|
||||
{/*
|
||||
Screenshot: `/images/ai/openwebui-model-picker.png`
|
||||
The Open WebUI model picker showing models served through the Pangolin gateway.
|
||||
<Frame>
|
||||
<img src="/images/ai/openwebui-model-picker.png" alt="Open WebUI model picker" />
|
||||
</Frame>
|
||||
*/}
|
||||
|
||||
For a private resource, set the key to `none` rather than leaving it blank. Open WebUI treats a blank key as "no connection configured" and won't list the models.
|
||||
|
||||
Note that Open WebUI runs as a server, not on your laptop - so it's the Open WebUI host that needs the Pangolin client installed and connected to reach a private resource, not the browser you're viewing it from.
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: "OpenClaw"
|
||||
description: "Connect OpenClaw to a Pangolin AI Gateway resource"
|
||||
---
|
||||
|
||||
[OpenClaw](https://openclaw.ai) is an open-source agent gateway that can run against any OpenAI- or Anthropic-compatible endpoint. Point it at an AI Gateway resource with a matching provider attached - OpenAI-compatible (OpenAI, OpenRouter, Vercel AI Gateway, Microsoft Foundry, custom) or Anthropic-compatible (Anthropic, Bedrock, Vertex AI, custom). See [AI Gateway Overview](/manage/ai/overview) if you haven't set that up yet.
|
||||
|
||||
|
||||
You'll need the resource's URL (its `<endpoint>`) and its API key (`<key>`), both on the resource's Keys page, plus the name of a model your attached provider actually serves (e.g. `gpt-4o`, `claude-sonnet-4-6`) - that's `<model-id>` below.
|
||||
|
||||
<include>../../../../snippets/ai-gateway-key.mdx</include>
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
# macOS/Linux
|
||||
curl -fsSL https://openclaw.ai/install.sh | bash
|
||||
|
||||
# Windows PowerShell
|
||||
iwr -useb https://openclaw.ai/install.ps1 | iex
|
||||
```
|
||||
|
||||
## Connect it to your resource
|
||||
|
||||
```bash
|
||||
openclaw onboard --non-interactive --accept-risk \
|
||||
--auth-choice custom-api-key \
|
||||
--custom-base-url "<endpoint>/v1" \
|
||||
--custom-model-id "<model-id>" \
|
||||
--custom-api-key "<key>" \
|
||||
--custom-compatibility openai \
|
||||
--install-daemon --skip-channels --skip-skills
|
||||
```
|
||||
|
||||
Set `--custom-compatibility anthropic` instead of `openai` if the resource's provider is Anthropic/Bedrock/Vertex rather than OpenAI-compatible, and drop `--custom-base-url`'s trailing `/v1` in that case (Anthropic-style resources are addressed at the endpoint root).
|
||||
|
||||
Prefer the interactive wizard instead:
|
||||
|
||||
```bash
|
||||
openclaw onboard --install-daemon
|
||||
```
|
||||
|
||||
When prompted, choose **QuickStart or Manual mode**, select the custom/OpenAI-compatible provider option, and enter the base URL and key.
|
||||
|
||||
## Manual setup
|
||||
|
||||
Onboarding writes `~/.openclaw/openclaw.json`. You can edit it directly instead - changes reload automatically:
|
||||
|
||||
```json
|
||||
{
|
||||
"models": {
|
||||
"providers": {
|
||||
"pangolin": {
|
||||
"baseUrl": "<endpoint>/v1",
|
||||
"apiKey": "<key>",
|
||||
"api": "openai-completions",
|
||||
"models": [
|
||||
{
|
||||
"id": "<model-id>",
|
||||
"name": "<model-id> via Pangolin"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"model": { "primary": "pangolin/<model-id>" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To switch models later without re-editing the file:
|
||||
|
||||
```bash
|
||||
openclaw models set pangolin/<model-id>
|
||||
```
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
openclaw health
|
||||
openclaw models status
|
||||
openclaw agent --agent main -m "Hello, what model are you?"
|
||||
```
|
||||
|
||||
For a private resource, keep `--custom-api-key`/`apiKey` and set it to `none`, and make sure the Pangolin client is connected on whichever machine runs the OpenClaw daemon.
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: "OpenCode"
|
||||
description: "Connect OpenCode to a Pangolin AI Gateway resource"
|
||||
---
|
||||
|
||||
OpenCode configures each model provider separately, so it can talk to whichever API formats your resource supports — Anthropic Messages, OpenAI Chat/Responses, or both, depending on which providers are attached. See [AI Gateway Overview](/manage/ai/overview) if you haven't set that up yet.
|
||||
|
||||
|
||||
You'll need the resource's URL (its `<endpoint>`) and its API key (`<key>`). Both are on the resource's Keys page.
|
||||
|
||||
<include>../../../../snippets/ai-gateway-key.mdx</include>
|
||||
|
||||
## Fastest: Pangolin CLI
|
||||
|
||||
[Install the Pangolin CLI](/manage/clients/install-client#quick-install-recommended) if you don't have it, then log in:
|
||||
|
||||
```bash
|
||||
pangolin login
|
||||
```
|
||||
|
||||
Configure OpenCode against a resource:
|
||||
|
||||
```bash
|
||||
pangolin configure opencode
|
||||
```
|
||||
|
||||
This prompts you to pick an organization and resource (if you have more than one), then asks which OpenCode provider ID(s) should point at this gateway — defaults to `anthropic,openai`, but you can enter any comma-separated list OpenCode recognizes (e.g. `openrouter,google`). It fetches a key for you if needed and writes `opencode.json` and `auth.json`. To skip the org/resource prompts:
|
||||
|
||||
```bash
|
||||
pangolin configure opencode --resource <resource-niceid-or-domain>
|
||||
```
|
||||
|
||||
To undo it:
|
||||
|
||||
```bash
|
||||
pangolin configure opencode --reset
|
||||
```
|
||||
|
||||
## Manual setup
|
||||
|
||||
Merge this into your global `opencode.json` (`~/.config/opencode/opencode.json`, or `$XDG_CONFIG_HOME/opencode/opencode.json` if set):
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"provider": {
|
||||
"anthropic": {
|
||||
"options": {
|
||||
"baseURL": "<endpoint>/v1"
|
||||
}
|
||||
},
|
||||
"openai": {
|
||||
"options": {
|
||||
"baseURL": "<endpoint>/v1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then merge your key into `auth.json` (`~/.local/share/opencode/auth.json`, or `$XDG_DATA_HOME/opencode/auth.json` if set):
|
||||
|
||||
```json
|
||||
{
|
||||
"anthropic": {
|
||||
"type": "api",
|
||||
"key": "<key>"
|
||||
},
|
||||
"openai": {
|
||||
"type": "api",
|
||||
"key": "<key>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Warning>
|
||||
`auth.json` is required even for a private resource. OpenCode refuses to start a provider with no key at all and fails with `OpenAI API key is missing. Pass it using the 'apiKey' parameter or the OPENAI_API_KEY environment variable.` Set the key to `none` rather than leaving the entry out.
|
||||
</Warning>
|
||||
|
||||
Only add entries for the providers your resource actually supports. To point a different OpenCode provider (`openrouter`, `google`, `groq`, etc.) at this gateway, add a matching block under `provider` in `opencode.json` and a matching key in `auth.json`.
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: "Model Catalog"
|
||||
description: "Known models, pricing for budgets, and how to load a custom catalog"
|
||||
---
|
||||
|
||||
The model catalog is Pangolin's list of known model ids, which provider type owns them, and token pricing. It is used for:
|
||||
|
||||
- The **Known Models** picker on a provider's Models tab
|
||||
- Expanding wildcard allows in `GET /v1/models`
|
||||
- [Provider selection](/manage/ai/providers/model-routing#provider-selection) when more than one attached provider allows the same id
|
||||
- **Budgets and usage cost**, which look up `in` / `out` / `cache` / `reasoning` rates for a model key
|
||||
|
||||
See [Model Routing](/manage/ai/providers/model-routing) for allow lists, inherit vs select, and how those lists combine with the catalog at request time.
|
||||
|
||||
## Catalog Providers
|
||||
|
||||
Default source: `https://api.fossorial.io/api/v1/models`. Typed AI provider types map as follows:
|
||||
|
||||
| Provider type | Catalog |
|
||||
|---|---|
|
||||
| OpenAI | `openai` |
|
||||
| Anthropic | `anthropic` |
|
||||
| Google Gemini | `gemini` |
|
||||
| Vertex AI | `vertex` |
|
||||
| Amazon Bedrock | `bedrock` |
|
||||
| Microsoft Foundry | `azure` |
|
||||
| OpenRouter, Vercel AI Gateway, Custom | (not mapped) |
|
||||
|
||||
Unmapped types skip Known Models and wildcard expansion against the catalog. Enter exact keys if you need them listed in discovery.
|
||||
|
||||
## Pricing and Budgets
|
||||
|
||||
Each catalog entry can include token rates: `in`, `out`, `cache`, and `reasoning`. [Budget](/manage/ai/budgets) enforcement and usage analytics use those rates when the requested model matches a catalog id. Unknown keys have no catalog price, so they do not count toward USD budgets. Add custom models to the catalog if you need cost tracking for them. Token budgets still sum usage for unknown ids. Cost is per token.
|
||||
|
||||
## Loading a Custom Catalog
|
||||
|
||||
Self-hosted Pangolin loads and refreshes the catalog from [`config.yml`](/self-host/advanced/config-file#ai-model-catalog):
|
||||
|
||||
```yaml
|
||||
ai:
|
||||
model_catalog:
|
||||
upstream_url: "https://api.fossorial.io/api/v1/models"
|
||||
file: "config/ai-models.json"
|
||||
merge_file: "config/ai-models-extra.json"
|
||||
refresh_interval_min_hours: 6
|
||||
refresh_interval_max_hours: 12
|
||||
```
|
||||
|
||||
| Key | Purpose |
|
||||
|---|---|
|
||||
| `upstream_url` | HTTP GET for the catalog JSON. Defaults to the Fossorial API. Point this at your own endpoint to serve a custom catalog. |
|
||||
| `file` | Local JSON file that **replaces** the HTTP catalog. |
|
||||
| `merge_file` | Local JSON merged on top of the base catalog. Base entries win on duplicates; the merge file only adds missing models. |
|
||||
| `refresh_interval_min_hours` / `max_hours` | Background refresh window. The interval is jittered between min and max so many instances do not hit the upstream at the same moment. Defaults are 6 and 12. |
|
||||
|
||||
If both `file` and `upstream_url` are set, `file` is used as the base. `merge_file` still applies.
|
||||
|
||||
Catalog files and API responses are JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"model": "gpt-4o",
|
||||
"provider": "openai",
|
||||
"pricing": {
|
||||
"in": 0.0000188,
|
||||
"out": 0.0000188,
|
||||
"cache": null,
|
||||
"reasoning": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`provider` must be `openai`, `anthropic`, `gemini`, `vertex`, `azure`, or `bedrock`. Prefixes `bedrock*`, `vertex*`, and `azure*` are accepted and normalized to those catalogs. Unknown providers are skipped.
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: "Multiple Gateway Resources"
|
||||
description: "Give different users and roles their own providers, model lists, and budgets with more than one AI Gateway resource"
|
||||
---
|
||||
|
||||
You can create more than one [public](/manage/resources/public/ai-gateway) or [private](/manage/resources/private/ai-gateway) AI Gateway resource. Each one has its own users and roles, attached [providers](/manage/ai/providers/overview), model lists, and [budgets](/manage/ai/budgets). That is how the Employees role gets OpenAI and Anthropic while Contractors get a tighter set, without sharing one allow list.
|
||||
|
||||
This is Pangolin's existing access control, applied to AI. A gateway resource is still a resource: you assign [users and roles](/manage/access-control/create-user) to it the same way as [public HTTPS](/manage/resources/public/authentication) or a [private host](/manage/resources/private/authentication). Assign a role when a set of people should share access. Assign individual users when the set is smaller or does not match a role. What differs per resource is which providers, models, and budgets it exposes.
|
||||
|
||||
## Users and Roles
|
||||
|
||||
Create a resource for the people who should share providers. Assign those users, or a role they belong to, then give the resource its own hostname. Clients point at a URL that already means that resource's providers.
|
||||
|
||||
For example:
|
||||
|
||||
- Resource **Staff AI**: FQDN `ai-staff.example.com`, role Employees, providers OpenAI and Anthropic.
|
||||
- Resource **Contractor AI**: FQDN `ai-contractors.example.com`, role Contractors, OpenRouter (or a custom endpoint) with a narrower allow list.
|
||||
|
||||
Staff point Claude Code, Codex, or another client at `https://ai-staff.example.com`. Contractors use `https://ai-contractors.example.com`. An [identity key](/manage/ai/virtual-api-keys) is still per user; the hostname is what selects the resource.
|
||||
|
||||
The same pattern works across public and private: a public resource for agents on the internet, a private resource for people on the Pangolin client.
|
||||
|
||||
## Sharing a Hostname
|
||||
|
||||
HTTP and HTTPS resources each need their own fully qualified domain name, because Pangolin would otherwise not know which target to send traffic to. AI Gateway resources can share a FQDN. Every one of them routes to the gateway running inside Pangolin, so they always go to the same place.
|
||||
|
||||
The hostname is an entrypoint, not a unique backend. Use a shared name when you want the same split as [users and roles](#users-and-roles), but everyone configures one URL.
|
||||
|
||||
You can overlap:
|
||||
|
||||
- Several public AI Gateway resources
|
||||
- Several private AI Gateway resources
|
||||
- Public and private together
|
||||
|
||||
An [HTTP / HTTPS](/manage/resources/public/http-https) resource still cannot use that same name.
|
||||
|
||||
### How Pangolin Picks a Resource
|
||||
|
||||
On each request, Pangolin lists the enabled AI Gateway resources whose FQDN matches the host, then keeps the ones the caller is allowed to use.
|
||||
|
||||
- **Public.** [Users and roles](/manage/resources/public/authentication) on the resource control an [identity key](/manage/ai/virtual-api-keys). A [manual key](/manage/ai/virtual-api-keys#manual-keys) must be scoped to that resource, or to all resources.
|
||||
- **Private.** [Users, roles, or machines](/manage/resources/private/authentication) granted on the resource, using the connected Pangolin client.
|
||||
|
||||
A user or role can be granted on more than one resource in the overlapping set. After access filtering, Pangolin picks among the remaining resources the same way it picks a [provider](/manage/ai/providers/model-routing#provider-selection) on a single resource:
|
||||
|
||||
1. **Capability.** The path must match a capability an attached provider advertises.
|
||||
2. **Allow and block.** The requested model must pass the resource's effective lists.
|
||||
3. **Specificity, catalog ownership, and class.** Exact allow keys beat patterns. A typed catalog owner beats an aggregator. Native typed providers beat aggregators, which beat Custom.
|
||||
|
||||
The chosen resource's attached providers, model lists, and resource-scoped budgets apply. Someone granted on both Staff AI and Contractor AI, both at `ai.example.com`, is routed by the model and API they called.
|
||||
|
||||
If more than one resource still matches after those steps, the gateway returns the same ambiguous error as overlapping providers: `Model "<id>" is ambiguous across multiple AI providers on this resource`.
|
||||
|
||||
When a public resource and a private resource share a host, calls from the internet use the public path (virtual API key). Calls over the Pangolin client tunnel use the private path (client identity).
|
||||
|
||||
The steps above assume [users and roles](/manage/resources/public/authentication) decide who can use each overlapping resource, together with virtual API keys or the connected client. If you also add [access rules](/manage/access-control/rules) (IP, path, geolocation, and similar) on those resources, which overlapping resource a request lands on is undefined. Put access rules on resources that have their own hostname.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
client[Client] --> fqdn["ai.example.com"]
|
||||
fqdn --> gw[Gateway in Pangolin]
|
||||
gw --> pick{Access then model}
|
||||
pick --> eng[Staff AI]
|
||||
pick --> fin[Contractor AI]
|
||||
eng --> openai[OpenAI and Anthropic]
|
||||
fin --> cheap[Restricted providers]
|
||||
```
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
title: "AI Gateway Overview"
|
||||
description: "Route coding agents and AI clients through Pangolin with centralized providers and access control"
|
||||
---
|
||||
|
||||
An AI Gateway resource is a normal Pangolin resource that proxies requests to one or more upstream AI providers (OpenAI, Anthropic, Google Gemini, Bedrock, Vertex AI, or a custom endpoint). Point a client at the resource's URL instead of the provider's, and Pangolin handles auth, access control, and logging in front of it. See [Pangolin vs. Bifrost vs. LiteLLM](/about/pangolin-vs-bifrost-vs-litellm) if you are comparing Pangolin to a traditional LLM gateway.
|
||||
|
||||
## How It Fits Together
|
||||
|
||||
- **[Providers](/manage/ai/providers/overview)** are configured once per organization - the upstream URL, API key, and capabilities for OpenAI, Anthropic, etc.
|
||||
- **Resources** (type `AI Gateway`) attach one or more of those providers and get a normal Pangolin domain.
|
||||
- **[Virtual API Keys](/manage/ai/virtual-api-keys)** are what clients send to a **public** AI Gateway resource. Private resources are reached through the Pangolin network, so the gateway does not check a key.
|
||||
- The **[Model Catalog](/manage/ai/model-catalog)** is the known-model list and token pricing used by [model routing](/manage/ai/providers/model-routing) and budgets.
|
||||
- **[Budgets](/manage/ai/budgets)** cap estimated USD spend or token usage on a provider, model, resource, role, or key.
|
||||
- **[Session Logs](/manage/ai/session-logs)** store prompt and response transcripts for each call.
|
||||
- **[Usage Analytics](/manage/ai/usage-analytics)** charts cost, tokens, and request volume across providers, resources, roles, users, and keys.
|
||||
|
||||
A resource only understands the API format(s) its attached providers support. An Anthropic provider makes the resource speak the Anthropic Messages API; an OpenAI provider makes it speak Chat Completions/Responses; a Gemini provider makes it speak Gemini's `generateContent` API. Attach whichever providers match the clients you plan to connect. See [AI Providers](/manage/ai/providers/overview) for per-type setup and [Provider Configuration](/manage/ai/providers/configuration) for capabilities, auth, and model lists.
|
||||
|
||||
## 1. Add a Provider
|
||||
|
||||
Create an org-level provider so the gateway has an upstream to call. Sidebar → **AI Gateway** → **Providers** → **Create**, or follow the [AI Providers](/manage/ai/providers/overview) guide. Pick a type, paste in its API key, and save. You can add as many as you need.
|
||||
|
||||
## 2. Create a Resource
|
||||
|
||||
Resources → **Create** → set **Type** to **AI Gateway**, pick a domain, and attach the provider(s) from step 1. You can also attach providers to an existing resource later from its **AI Gateway** settings tab. How public and private AI Gateway resources reach users is covered on the [public](/manage/resources/public/ai-gateway) and [private](/manage/resources/private/ai-gateway) resource type pages.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/ai/create-ai-gateway-resource.png" alt="Create Public Resource form with Type set to AI Gateway" />
|
||||
</Frame>
|
||||
|
||||
You can create more than one AI Gateway resource so different users and roles get their own providers. Distinct hostnames are the usual approach; they can also share a FQDN. See [Multiple Gateway Resources](/manage/ai/multiple-gateway-resources).
|
||||
|
||||
## 3. Authenticate to the Gateway
|
||||
|
||||
How a client authenticates depends on whether the resource is public or private.
|
||||
|
||||
### Private Resources
|
||||
|
||||
Reachable only on devices connected with the [Pangolin client](/manage/clients/install-client). Identity comes from that connection, so you do not issue a [virtual API key](/manage/ai/virtual-api-keys). The desktop client already proved who is calling. Details are on the [private AI Gateway](/manage/resources/private/ai-gateway) resource page.
|
||||
|
||||
### Public Resources
|
||||
|
||||
Reachable from anywhere. The gateway checks a [virtual API key](/manage/ai/virtual-api-keys) on every call. Details are on the [public AI Gateway](/manage/resources/public/ai-gateway) resource page.
|
||||
|
||||
## 4. Connect a Coding Agent, Agent, or AI Client to the Gateway
|
||||
|
||||
Every resource has ready-to-copy setup for common clients, and `pangolin configure <client>` can write the config for you. Full instructions per client:
|
||||
|
||||
| Client | Needs a provider that speaks | Guide |
|
||||
|---|---|---|
|
||||
| Claude Code | Anthropic Messages | [Setup guide](/manage/ai/configure-ai-clients/claude) |
|
||||
| Codex | OpenAI Chat/Responses | [Setup guide](/manage/ai/configure-ai-clients/codex) |
|
||||
| OpenCode | Anthropic Messages and/or OpenAI Chat | [Setup guide](/manage/ai/configure-ai-clients/opencode) |
|
||||
| Gemini CLI | Gemini `generateContent` | [Setup guide](/manage/ai/configure-ai-clients/gemini) |
|
||||
|
||||
There are also guides for pointing other tools at a Pangolin AI Gateway resource:
|
||||
|
||||
- [Open WebUI](/manage/ai/configure-ai-clients/open-webui)
|
||||
- [Claude Desktop](/manage/ai/configure-ai-clients/claude-desktop)
|
||||
- [OpenClaw](/manage/ai/configure-ai-clients/openclaw)
|
||||
|
||||
## Supported Providers
|
||||
|
||||
Each type has its own configuration guide. Capabilities (which API formats a provider speaks) are explained in [Provider Configuration](/manage/ai/providers/configuration#capabilities).
|
||||
|
||||
| Provider | Default capabilities | Guide |
|
||||
|---|---|---|
|
||||
| OpenAI | Chat Completions, Responses | [Configure](/manage/ai/providers/openai) |
|
||||
| Anthropic | Messages | [Configure](/manage/ai/providers/anthropic) |
|
||||
| Google Gemini | `generateContent` | [Configure](/manage/ai/providers/google-gemini) |
|
||||
| Vertex AI | Google `generateContent`/`rawPredict` | [Configure](/manage/ai/providers/vertex-ai) |
|
||||
| Amazon Bedrock | Converse | [Configure](/manage/ai/providers/bedrock) |
|
||||
| Microsoft Foundry | Chat Completions, Responses, Messages | [Configure](/manage/ai/providers/microsoft-foundry) |
|
||||
| OpenRouter | Chat Completions | [Configure](/manage/ai/providers/open-router) |
|
||||
| Vercel AI Gateway | Chat Completions, Responses | [Configure](/manage/ai/providers/vercel-ai-gateway) |
|
||||
| Custom | Whatever you select | [Configure](/manage/ai/providers/custom) |
|
||||
|
||||
Use [Custom](/manage/ai/providers/custom) for any upstream that isn't in the typed list. Assign whichever capabilities it speaks - OpenAI, Anthropic, Gemini, Vertex, Bedrock, or a mix. A Moonshot AI (Kimi) endpoint is one example: select Anthropic Messages and point at its Anthropic-compatible URL.
|
||||
|
||||
<Card title="Pangolin vs. Bifrost vs. LiteLLM" icon="book" href="/about/pangolin-vs-bifrost-vs-litellm">
|
||||
How Pangolin's identity-aware AI Gateway compares to traditional LLM gateways.
|
||||
</Card>
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: "Anthropic"
|
||||
description: "Connect Anthropic as an AI Gateway provider"
|
||||
---
|
||||
|
||||
An Anthropic provider forwards Messages requests to Anthropic's API. Attach it to an AI Gateway resource when you want Claude Code, Claude Desktop, OpenCode, OpenClaw, or any other Anthropic-compatible client to use your org's Anthropic key.
|
||||
|
||||
See [AI Providers](/manage/ai/providers/overview) if you haven't created a provider before. Field options are in [Provider Configuration](/manage/ai/providers/configuration).
|
||||
|
||||
## Defaults
|
||||
|
||||
| Setting | Value |
|
||||
|---|---|
|
||||
| Upstream URL | `https://api.anthropic.com` |
|
||||
| Auth type | `x-api-key` |
|
||||
| Capabilities | Anthropic Messages, Anthropic Models |
|
||||
|
||||
Anthropic Models (`GET /v1/models`) is answered from this provider's allow and block lists. See [Capabilities](/manage/ai/providers/configuration#capabilities).
|
||||
|
||||
## Configure
|
||||
|
||||
1. Sidebar → **AI Gateway** → **Providers** → **Create**.
|
||||
2. Set **Provider Type** to **Anthropic**. Name, capabilities, upstream URL, and auth type fill in from the defaults above.
|
||||
3. Paste your Anthropic API key.
|
||||
4. Set **Allow** and **Block** lists. `*` allows every model key; `claude-*` is a typical narrower pattern.
|
||||
5. Save, then attach the provider to an [AI Gateway resource](/manage/ai/overview).
|
||||
|
||||
Leave the upstream URL as the default unless you are pointing at a compatible Anthropic-style base URL.
|
||||
|
||||
<Tip>
|
||||
Clients can send `anthropic-version` themselves. You can also set it as a [custom header](/manage/ai/providers/configuration#headers-and-tls) on the provider (`anthropic-version: 2023-06-01`). A header set on the provider is always sent upstream and overrides the same header from the client.
|
||||
</Tip>
|
||||
|
||||
## Clients
|
||||
|
||||
| Client | Guide |
|
||||
|---|---|
|
||||
| Claude Code | [Setup guide](/manage/ai/configure-ai-clients/claude) |
|
||||
| Claude Desktop | [Setup guide](/manage/ai/configure-ai-clients/claude-desktop) |
|
||||
| OpenCode | [Setup guide](/manage/ai/configure-ai-clients/opencode) |
|
||||
| OpenClaw | [Setup guide](/manage/ai/configure-ai-clients/openclaw) |
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: "Amazon Bedrock"
|
||||
description: "Connect Amazon Bedrock as an AI Gateway provider"
|
||||
---
|
||||
|
||||
An Amazon Bedrock provider forwards Converse requests to Bedrock Runtime in the region you configure. Use it when clients call Bedrock's Converse API.
|
||||
|
||||
See [AI Providers](/manage/ai/providers/overview) if you haven't created a provider before. Field options are in [Provider Configuration](/manage/ai/providers/configuration).
|
||||
|
||||
## Defaults
|
||||
|
||||
| Setting | Value |
|
||||
|---|---|
|
||||
| Upstream URL | You enter the regional Bedrock Runtime base URL |
|
||||
| Auth type | Bearer |
|
||||
| Capabilities | Bedrock Converse |
|
||||
|
||||
Bedrock Model Invoke (`InvokeModel` / `invoke-with-response-stream`) is available as an extra capability if the client uses that API instead of Converse.
|
||||
|
||||
## Configure
|
||||
|
||||
1. Sidebar → **AI Gateway** → **Providers** → **Create**.
|
||||
2. Set **Provider Type** to **Amazon Bedrock**. Capabilities and auth type fill in from the defaults above.
|
||||
3. Set **Upstream URL** to your regional Bedrock Runtime host, for example `https://bedrock-runtime.us-east-1.amazonaws.com`.
|
||||
4. Paste a Bearer credential the upstream accepts. Pangolin sends `Authorization: Bearer <key>` on every request.
|
||||
5. Set **Allow** and **Block** lists. Use Bedrock model ids (for example `anthropic.claude-sonnet-4-20250514-v1:0` or an inference profile id).
|
||||
6. Save, then attach the provider to an [AI Gateway resource](/manage/ai/overview).
|
||||
|
||||
Add **Bedrock Model Invoke** on the **General** tab if the client calls `/model/{id}/invoke` rather than Converse. Add other capabilities only if this endpoint actually speaks those API formats.
|
||||
|
||||
## Clients
|
||||
|
||||
Clients that call Bedrock Converse work with the default capability. Claude Code talks Anthropic Messages (`POST /v1/messages`), so it needs a provider that advertises Anthropic Messages, such as [Anthropic](/manage/ai/providers/anthropic), [Microsoft Foundry](/manage/ai/providers/microsoft-foundry), or a [custom](/manage/ai/providers/custom) Anthropic-compatible endpoint.
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
title: "Provider Configuration"
|
||||
description: "Capabilities, auth, routing, and model lists for AI Gateway providers"
|
||||
---
|
||||
|
||||
This page is the field reference for every AI provider type. Type-specific defaults (URL, auth header, capabilities) live on each provider page. See [AI Providers](/manage/ai/providers/overview) for what a provider is and how it attaches to a resource.
|
||||
|
||||
Sidebar → **AI Gateway** → **Providers** → **Create**. After you save, the provider has tabs for **General**, **Network**, **Models**, **Authentication**, and **Budget**.
|
||||
|
||||
## Capabilities
|
||||
|
||||
A capability is an API format the gateway will accept and proxy. The incoming request path selects a capability, and only attached providers that advertise that capability can handle the request. That is why Claude Code needs a provider with Anthropic Messages, and Codex needs a provider with OpenAI Chat Completions or Responses.
|
||||
|
||||
| Capability | What the gateway accepts |
|
||||
|---|---|
|
||||
| OpenAI Chat Completions | `POST /v1/chat/completions` |
|
||||
| OpenAI Responses | `POST /v1/responses` |
|
||||
| Anthropic Messages | `POST /v1/messages` |
|
||||
| Anthropic Models | `GET /v1/models` (answered from the gateway's allow and block lists, not proxied upstream) |
|
||||
| Gemini Generate Content | Gemini `generateContent` / `streamGenerateContent` |
|
||||
| Vertex Generate Content | Vertex AI Gemini format |
|
||||
| Vertex Raw Predict | Vertex AI `rawPredict` for Anthropic models |
|
||||
| Bedrock Converse | Amazon Bedrock Converse API |
|
||||
| Bedrock Model Invoke | Amazon Bedrock InvokeModel |
|
||||
|
||||
Typed providers start with recommended capabilities. Custom providers need at least one selected. You can change capabilities later on the provider's **General** tab. Select every format the upstream actually speaks. Custom providers can mix any combination.
|
||||
|
||||
If two attached providers share a capability for the same model, the gateway picks one at request time. See [Model Routing](/manage/ai/providers/model-routing#provider-selection).
|
||||
|
||||
## Auth Type
|
||||
|
||||
How the gateway authenticates to the upstream. Typed providers prefill the header that upstream expects.
|
||||
|
||||
| Auth type | What Pangolin sends |
|
||||
|---|---|
|
||||
| Bearer | `Authorization: Bearer <key>`. Used by OpenAI and most compatible APIs. |
|
||||
| `x-api-key` | `x-api-key` header. Used by Anthropic and Anthropic-compatible APIs. |
|
||||
| `x-goog-api-key` | `x-goog-api-key` header. Used by Google Gemini. |
|
||||
| Cloudflare AI Gateway | `cf-aig-authorization: Bearer <key>`. |
|
||||
| Splunk HEC | `Authorization: Splunk <key>`. |
|
||||
| Passthrough | Forward the caller's remaining API key headers to the upstream (after Pangolin strips its own virtual key). |
|
||||
| No Auth | Send the request without authentication headers. |
|
||||
|
||||
Bearer, `x-api-key`, `x-goog-api-key`, Cloudflare AI Gateway, and Splunk HEC all require an API key on the provider.
|
||||
|
||||
## Routing
|
||||
|
||||
Where the gateway sends the request after it has selected this provider.
|
||||
|
||||
| Mode | When to use |
|
||||
|---|---|
|
||||
| **Upstream URL** | Call a public or private API base URL. Typed providers with a public API prefill this. Vertex AI, Amazon Bedrock, and Microsoft Foundry ask you to enter a regional or resource base URL. |
|
||||
| **Site Targets** | Route through HTTP targets on your Pangolin sites, the same way a public resource reaches an internal service. Available on [Custom](/manage/ai/providers/custom) providers only. |
|
||||
|
||||
If you choose **Site Targets**, configure the targets on the provider's **Network** tab after you save.
|
||||
|
||||
See [Ollama](/manage/ai/providers/custom/ollama), [vLLM](/manage/ai/providers/custom/vllm), [Bifrost](/manage/ai/providers/custom/bifrost), and [Cloud APIs Through a Site](/manage/ai/providers/custom/cloud-apis-through-a-site) for Site Targets walkthroughs.
|
||||
|
||||
## Models
|
||||
|
||||
Allow and block lists, inherit vs select, and provider selection are covered in [Model Routing](/manage/ai/providers/model-routing). Known Models, ownership scoring, and token pricing for budgets come from the [Model Catalog](/manage/ai/model-catalog).
|
||||
|
||||
## Budget
|
||||
|
||||
The provider **Budget** tab caps estimated USD spend or token usage for every call that uses this provider. You can also set a budget on an individual allow-list model from the **Models** tab. See [Budgets](/manage/ai/budgets) for scopes, periods, and the 429 clients receive when a cap is hit.
|
||||
|
||||
## Headers and TLS
|
||||
|
||||
On **Network** you can also set:
|
||||
|
||||
- **Custom headers** sent on every request to this provider (`Header-Name: value`, one per line)
|
||||
- **Skip TLS verification** for the upstream connection
|
||||
|
||||
### Identity Headers
|
||||
|
||||
When Pangolin knows which user made the request, it forwards that identity to the upstream on every call:
|
||||
|
||||
| Header | Value |
|
||||
|---|---|
|
||||
| `Remote-User` | Username |
|
||||
| `Remote-Email` | Email, when set |
|
||||
| `Remote-Name` | Display name, when set |
|
||||
| `Remote-Role` | Org role names, when the user has roles |
|
||||
|
||||
The user is known when:
|
||||
|
||||
- A public resource is called with an [identity key](/manage/ai/virtual-api-keys#identity-keys)
|
||||
- A public resource is called with a [manual key](/manage/ai/virtual-api-keys#manual-keys) attributed to a user
|
||||
- A private AI Gateway resource is called from a connected [Pangolin client](/manage/clients/install-client), and that client maps to a user
|
||||
|
||||
An unattributed manual key still authenticates, but these headers are omitted. Empty values are omitted rather than sent blank.
|
||||
|
||||
These are the same names as [Forwarded Headers](/manage/access-control/forwarded-headers) on HTTPS resources. A Custom upstream such as [Bifrost](/manage/ai/providers/custom/bifrost) can use them for downstream access control. They are sent in addition to any custom headers you configure on the provider.
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: "Custom"
|
||||
description: "Connect any upstream endpoint and assign whichever API capabilities it speaks"
|
||||
---
|
||||
|
||||
A custom provider is for an upstream that is not in the typed list: a self-hosted model server, a vendor API, a proxy, or an internal service reached through a Pangolin site. You assign any mix of [capabilities](/manage/ai/providers/configuration#capabilities). The gateway will accept those API formats on resources that attach this provider.
|
||||
|
||||
Custom providers start blank. You choose capabilities, [routing](/manage/ai/providers/configuration#routing), and [auth](/manage/ai/providers/configuration#auth-type). At least one capability is required. Site Targets routing is available on Custom only.
|
||||
|
||||
See [AI Providers](/manage/ai/providers/overview) if you haven't created a provider before, and [Provider Configuration](/manage/ai/providers/configuration) for the field reference.
|
||||
|
||||
## Configure
|
||||
|
||||
1. Sidebar → **AI Gateway** → **Providers** → **Create**.
|
||||
2. Set **Provider Type** to **Custom** and give it a name.
|
||||
3. Select every **API capability** the upstream speaks. You can add any combination.
|
||||
4. Choose **Routing Mode**. For **Upstream URL**, paste the base URL. For **Site Targets**, add targets after save.
|
||||
5. Pick **Auth Type** and paste a key if that type needs one.
|
||||
6. Set **Allow** and **Block** lists. Add exact model keys if you want them listed in `GET /v1/models`. See [Model Routing](/manage/ai/providers/model-routing).
|
||||
7. Save, then attach the provider to an [AI Gateway resource](/manage/ai/overview).
|
||||
|
||||
## Reach a Self-Hosted API
|
||||
|
||||
Site Targets send the request over a Pangolin tunnel to a host the site can reach. [Install a site](/manage/sites/install-site) on the same network as the model server so the gateway can route to that API over the tunnel. See [Understanding Sites](/manage/sites/understanding-sites).
|
||||
|
||||
After the site is connected, add the process as a target on the provider's **Network** tab (host, port, HTTP or HTTPS). Worked examples: [Ollama](/manage/ai/providers/custom/ollama), [vLLM](/manage/ai/providers/custom/vllm), and [Bifrost](/manage/ai/providers/custom/bifrost).
|
||||
|
||||
<Note>
|
||||
AI Gateway providers are only routed to the Pangolin server nodes. Sites
|
||||
that are connected to remote nodes will not be accessible to be routed to as
|
||||
targets on providers. DNS resolution of gateway resources will not point at
|
||||
remote nodes. Users using gateway resources with custom targets on sites may
|
||||
wish to create a separate org for these resources so sites are able to
|
||||
connect to regular nodes.
|
||||
</Note>
|
||||
|
||||
## Examples
|
||||
|
||||
Worked Custom setups:
|
||||
|
||||
- [Ollama](/manage/ai/providers/custom/ollama): local models on a site, OpenAI Chat Completions
|
||||
- [vLLM](/manage/ai/providers/custom/vllm): a vLLM OpenAI server on a site
|
||||
- [Bifrost](/manage/ai/providers/custom/bifrost): downstream model routing with Pangolin identity headers
|
||||
- [Cloud APIs Through a Site](/manage/ai/providers/custom/cloud-apis-through-a-site): send OpenAI or Anthropic out through a site so the vendor sees that site's IP
|
||||
|
||||
The same Custom pattern works for llama.cpp server, LM Studio, LocalAI, and other OpenAI-compatible listeners: pick Chat Completions, point Site Targets at the process, and allow the model ids it serves.
|
||||
|
||||
## Example: Kimi K2
|
||||
|
||||
This is one custom setup: Moonshot AI's Kimi K2 endpoint speaks the Anthropic API, so you select Anthropic capabilities. A different upstream would get a different capability set.
|
||||
|
||||
1. Create a **Custom** provider.
|
||||
2. Select **Anthropic Messages** and **Anthropic Models**.
|
||||
3. Set routing to **Upstream URL** and use Moonshot's Anthropic-compatible base URL.
|
||||
4. Set auth to **`x-api-key`** and paste the Moonshot key.
|
||||
5. Allow the Kimi model ids you plan to call.
|
||||
6. Attach the provider to a resource, then point [Claude Code](/manage/ai/configure-ai-clients/claude) or [Claude Desktop](/manage/ai/configure-ai-clients/claude-desktop) at that resource.
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: "Bifrost"
|
||||
description: "Point a Custom provider at Bifrost for downstream model routing and identity-aware access control"
|
||||
---
|
||||
|
||||
[Bifrost](https://www.getmaxim.ai/bifrost) is an OpenAI-compatible LLM gateway that routes to many upstream model providers. Use it as a Custom AI Gateway provider when Pangolin should authenticate clients and Bifrost should pick models, fail over, or apply its own access controls.
|
||||
|
||||
Pangolin talks to Bifrost at `/v1/chat/completions` (default port `8080`). Add [Anthropic Messages](/manage/ai/providers/configuration#capabilities) as well if clients will call Bifrost's Anthropic-compatible paths.
|
||||
|
||||
See [Capabilities](/manage/ai/providers/configuration#capabilities) for the full capability list.
|
||||
|
||||
[Install a site](/manage/sites/install-site) on the same network as Bifrost so Pangolin can reach it over the tunnel. See [Reach a Self-Hosted API](/manage/ai/providers/custom#reach-a-self-hosted-api).
|
||||
|
||||
## Configure
|
||||
|
||||
1. Sidebar → **AI Gateway** → **Providers** → **Create**.
|
||||
2. Set **Provider Type** to **Custom** and give it a name.
|
||||
3. Select **OpenAI Chat Completions** (and any other formats Bifrost should accept).
|
||||
4. Set **Routing Mode** to **Site Targets** if Bifrost runs on a site's network. Use **Upstream URL** (`http://<host>:8080`) only when the Pangolin node can reach Bifrost directly.
|
||||
5. Set **Auth Type** to **No Auth** on a private LAN. Use **Bearer** if Bifrost requires a virtual key or other API key.
|
||||
6. Allow the model ids Bifrost should serve, including provider-prefixed keys such as `openai/gpt-4o-mini` if that is how Bifrost names them. See [Model Routing](/manage/ai/providers/model-routing).
|
||||
7. Save. On the **Network** tab, add a target: the Bifrost host, port `8080`, method HTTP.
|
||||
8. Attach the provider to an [AI Gateway resource](/manage/ai/overview).
|
||||
|
||||
Then point a client at that resource: [Codex](/manage/ai/configure-ai-clients/codex), [OpenCode](/manage/ai/configure-ai-clients/opencode), or [Open WebUI](/manage/ai/configure-ai-clients/open-webui). Clients still call the Pangolin resource. Bifrost only sees the request after Pangolin has authenticated it.
|
||||
|
||||
When the caller is a known user, Pangolin forwards [`Remote-User`, `Remote-Email`, `Remote-Name`, and `Remote-Role`](/manage/ai/providers/configuration#identity-headers). Use those in Bifrost (or any Custom upstream) for downstream access control. An unattributed [manual key](/manage/ai/virtual-api-keys#manual-keys) authenticates without sending them.
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: "Cloud APIs Through a Site"
|
||||
description: "Send OpenAI or Anthropic traffic out through a Pangolin site so the vendor sees a fixed egress IP"
|
||||
---
|
||||
|
||||
Typed [OpenAI](/manage/ai/providers/openai) and [Anthropic](/manage/ai/providers/anthropic) providers send requests from the Pangolin node's own egress. A [Custom](/manage/ai/providers/custom) provider with **Site Targets** sends the same APIs out through a chosen site, so OpenAI or Anthropic see that site's public IP.
|
||||
|
||||
Use this when the vendor allowlists IPs, or all inference must leave from one network. The site is only the egress path. Auth still lives on the provider: paste the real vendor key.
|
||||
|
||||
Those typed provider types use **Upstream URL** only. This walkthrough uses Custom plus targets.
|
||||
|
||||
See [Capabilities](/manage/ai/providers/configuration#capabilities) for the full capability list.
|
||||
|
||||
## Configure
|
||||
|
||||
1. Pick a [site](/manage/sites/understanding-sites) that can reach the public internet. That box's public IP is what the vendor will see.
|
||||
2. Sidebar → **AI Gateway** → **Providers** → **Create**. Set **Provider Type** to **Custom**.
|
||||
3. Select capabilities that match the cloud API:
|
||||
- OpenAI: **OpenAI Chat Completions** and **OpenAI Responses**
|
||||
- Anthropic: **Anthropic Messages** and **Anthropic Models**
|
||||
4. Set **Routing Mode** to **Site Targets**.
|
||||
5. Set **Auth Type** to **Bearer** for OpenAI, or **`x-api-key`** for Anthropic, and paste the vendor API key.
|
||||
6. Allow the model ids you plan to call, then save.
|
||||
7. On the **Network** tab, add a target on that site. Use the API hostname as the address so TLS Host and SNI match:
|
||||
|
||||
| Vendor | Address | Port | Method |
|
||||
|---|---|---|---|
|
||||
| OpenAI | `api.openai.com` | `443` | HTTPS |
|
||||
| Anthropic | `api.anthropic.com` | `443` | HTTPS |
|
||||
|
||||
The target address is sent as the Host header.
|
||||
8. Attach the provider to an [AI Gateway resource](/manage/ai/overview).
|
||||
|
||||
The site must resolve and reach that hostname. Clients still point at the resource URL, not at OpenAI or Anthropic directly: [Codex](/manage/ai/configure-ai-clients/codex), [Claude Code](/manage/ai/configure-ai-clients/claude), and the other [client guides](/manage/ai/overview#4-connect-a-client).
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: "Ollama"
|
||||
description: "Point a Custom provider at Ollama on a Pangolin site"
|
||||
---
|
||||
|
||||
[Ollama](https://ollama.com) serves local models with an OpenAI-compatible HTTP API. Create a [Custom](/manage/ai/providers/custom) provider and route to that API so Codex, OpenCode, Open WebUI, and other Chat Completions clients can use models running on a site.
|
||||
|
||||
Pangolin talks to Ollama at `/v1/chat/completions`. Use that OpenAI-compatible listener, not Ollama's native `/api/chat` path.
|
||||
|
||||
See [Capabilities](/manage/ai/providers/configuration#capabilities) for the full capability list.
|
||||
|
||||
[Install a site](/manage/sites/install-site) on the same network as Ollama so Pangolin can reach it over the tunnel. See [Reach a Self-Hosted API](/manage/ai/providers/custom#reach-a-self-hosted-api).
|
||||
|
||||
## Configure
|
||||
|
||||
1. Sidebar > **AI Gateway** > **Providers** > **Create**.
|
||||
2. Set **Provider Type** to **Custom** and give it a name.
|
||||
3. Select **OpenAI Chat Completions**.
|
||||
4. Set **Routing Mode** to **Site Targets** if Ollama runs on a site's network. Use **Upstream URL** (`http://<host>:11434`) only when the Pangolin node can reach Ollama directly.
|
||||
5. Set **Auth Type** to **No Auth** on a private LAN. Use **Bearer** if you set an Ollama API key.
|
||||
6. Allow the exact model ids you plan to call, for example `llama3.2`. See [Model Routing](/manage/ai/providers/model-routing).
|
||||
7. Save. On the **Network** tab, add a target: the Ollama host, port `11434`, method HTTP.
|
||||
8. Attach the provider to an [AI Gateway resource](/manage/ai/overview).
|
||||
|
||||
Then point a client at that resource: [Codex](/manage/ai/configure-ai-clients/codex), [OpenCode](/manage/ai/configure-ai-clients/opencode), or [Open WebUI](/manage/ai/configure-ai-clients/open-webui).
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: "vLLM"
|
||||
description: "Point a Custom provider at a vLLM OpenAI server on a Pangolin site"
|
||||
---
|
||||
|
||||
[vLLM](https://docs.vllm.ai) can expose an OpenAI-compatible HTTP server for local or GPU-hosted models. Create a [Custom](/manage/ai/providers/custom) provider and route to that server so Codex, OpenCode, Open WebUI, and other Chat Completions clients can use it.
|
||||
|
||||
Pangolin talks to vLLM at `/v1/chat/completions` on the OpenAI server (default port `8000`).
|
||||
|
||||
See [Capabilities](/manage/ai/providers/configuration#capabilities) for the full capability list.
|
||||
|
||||
[Install a site](/manage/sites/install-site) on the same network as vLLM so Pangolin can reach it over the tunnel. See [Reach a Self-Hosted API](/manage/ai/providers/custom#reach-a-self-hosted-api).
|
||||
|
||||
## Configure
|
||||
|
||||
1. Sidebar → **AI Gateway** → **Providers** → **Create**.
|
||||
2. Set **Provider Type** to **Custom** and give it a name.
|
||||
3. Select **OpenAI Chat Completions**.
|
||||
4. Set **Routing Mode** to **Site Targets** if vLLM runs on a site's network. Use **Upstream URL** (`http://<host>:8000`) only when the Pangolin node can reach vLLM directly.
|
||||
5. Set **Auth Type** to **No Auth** if the server has no key. Use **Bearer** and paste the key if you started vLLM with `--api-key`.
|
||||
6. Allow the exact served model id (the `--model` name or `--served-model-name`). See [Model Routing](/manage/ai/providers/model-routing).
|
||||
7. Save. On the **Network** tab, add a target: the vLLM host, port `8000`, method HTTP.
|
||||
8. Attach the provider to an [AI Gateway resource](/manage/ai/overview).
|
||||
|
||||
Then point a client at that resource: [Codex](/manage/ai/configure-ai-clients/codex), [OpenCode](/manage/ai/configure-ai-clients/opencode), or [Open WebUI](/manage/ai/configure-ai-clients/open-webui).
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: "Google Gemini"
|
||||
description: "Connect Google Gemini as an AI Gateway provider"
|
||||
---
|
||||
|
||||
A Google Gemini provider forwards `generateContent` requests to Google's Gemini API. Attach it to an AI Gateway resource when you want Gemini CLI (or another client that speaks the Gemini API) to use your org's Gemini key.
|
||||
|
||||
See [AI Providers](/manage/ai/providers/overview) if you haven't created a provider before. Field options are in [Provider Configuration](/manage/ai/providers/configuration).
|
||||
|
||||
This type is the Gemini Developer API (`generativelanguage.googleapis.com`). For Vertex AI's `generateContent` / `rawPredict` paths, use [Vertex AI](/manage/ai/providers/vertex-ai) instead.
|
||||
|
||||
## Defaults
|
||||
|
||||
| Setting | Value |
|
||||
|---|---|
|
||||
| Upstream URL | `https://generativelanguage.googleapis.com` |
|
||||
| Auth type | `x-goog-api-key` |
|
||||
| Capabilities | Gemini Generate Content |
|
||||
|
||||
## Configure
|
||||
|
||||
1. Sidebar → **AI Gateway** → **Providers** → **Create**.
|
||||
2. Set **Provider Type** to **Google Gemini**. Name, capabilities, upstream URL, and auth type fill in from the defaults above.
|
||||
3. Paste your Gemini API key.
|
||||
4. Set **Allow** and **Block** lists. `*` allows every model key; `gemini-*` is a typical narrower pattern.
|
||||
5. Save, then attach the provider to an [AI Gateway resource](/manage/ai/overview).
|
||||
|
||||
Leave the upstream URL as the default unless you are pointing at a compatible Gemini API base URL.
|
||||
|
||||
## Clients
|
||||
|
||||
| Client | Guide |
|
||||
|---|---|
|
||||
| Gemini CLI | [Setup guide](/manage/ai/configure-ai-clients/gemini) |
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
title: "Microsoft Foundry"
|
||||
description: "Connect Microsoft Foundry as an AI Gateway provider"
|
||||
---
|
||||
|
||||
A Microsoft Foundry provider forwards OpenAI-format and Anthropic-format requests to your Azure AI Foundry (or Azure OpenAI) resource. One Foundry provider can serve both Codex-style clients and Claude Code, because the defaults include Chat Completions, Responses, and Anthropic Messages.
|
||||
|
||||
See [AI Providers](/manage/ai/providers/overview) if you haven't created a provider before. Field options are in [Provider Configuration](/manage/ai/providers/configuration).
|
||||
|
||||
## Defaults
|
||||
|
||||
| Setting | Value |
|
||||
|---|---|
|
||||
| Upstream URL | You enter your Foundry or Azure OpenAI resource base URL |
|
||||
| Auth type | Bearer |
|
||||
| Capabilities | OpenAI Chat Completions, OpenAI Responses, Anthropic Messages, Anthropic Models |
|
||||
|
||||
## Configure
|
||||
|
||||
1. Sidebar → **AI Gateway** → **Providers** → **Create**.
|
||||
2. Set **Provider Type** to **Microsoft Foundry**. Capabilities and auth type fill in from the defaults above.
|
||||
3. Set **Upstream URL** to the resource base, for example `https://<resource-name>.services.ai.azure.com` or `https://<resource-name>.openai.azure.com`.
|
||||
4. Paste a Bearer credential the resource accepts (typically the Azure API key).
|
||||
5. Set **Allow** and **Block** lists. Use the deployment or model names the Foundry resource exposes.
|
||||
6. Save, then attach the provider to an [AI Gateway resource](/manage/ai/overview).
|
||||
|
||||
Leave capabilities at the defaults if you want both OpenAI-compatible and Anthropic-compatible clients on the same resource. Trim them if this Foundry resource only hosts one API family.
|
||||
|
||||
## Clients
|
||||
|
||||
| Client | Guide |
|
||||
|---|---|
|
||||
| Codex | [Setup guide](/manage/ai/configure-ai-clients/codex) |
|
||||
| Claude Code | [Setup guide](/manage/ai/configure-ai-clients/claude) |
|
||||
| OpenCode | [Setup guide](/manage/ai/configure-ai-clients/opencode) |
|
||||
| Open WebUI | [Setup guide](/manage/ai/configure-ai-clients/open-webui) |
|
||||
| Claude Desktop | [Setup guide](/manage/ai/configure-ai-clients/claude-desktop) |
|
||||
| OpenClaw | [Setup guide](/manage/ai/configure-ai-clients/openclaw) |
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: "Model Routing"
|
||||
description: "Allow and block lists, inherit vs select, and how the gateway picks a provider"
|
||||
---
|
||||
|
||||
Model routing is how Pangolin decides which attached provider handles a request, and which model keys that provider may serve. Configure lists on the provider, optionally narrow them on the resource, then the gateway picks a provider per request.
|
||||
|
||||
See [AI Providers](/manage/ai/providers/overview) for what a provider is, and [Provider Configuration](/manage/ai/providers/configuration) for capabilities, auth, and routing mode. The [Model Catalog](/manage/ai/model-catalog) supplies Known Models, wildcard discovery, ownership scoring, and pricing for budgets.
|
||||
|
||||
## Choosing Models on a Provider
|
||||
|
||||
On the provider's **Models** tab, every request must match an **allow** entry and must not match a **block** entry. An empty allow list denies all traffic. A key cannot sit on both lists.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/ai/public-resource-models.png" alt="Provider Models tab with allow and block lists for Anthropic" />
|
||||
</Frame>
|
||||
|
||||
You add keys three ways:
|
||||
|
||||
| How | What it is | Example |
|
||||
|---|---|---|
|
||||
| **[Catalog (Known Models)](/manage/ai/model-catalog)** | Pick from the catalog for that provider type | `gpt-4o`, `claude-sonnet-4-6` |
|
||||
| **Custom key** | Type an exact model id the upstream accepts | `llama3.2`, `openai/gpt-4o-mini` |
|
||||
| **Pattern** | Glob with `*` and `?` | `gpt-*`, `claude-3-5-sonnet-?`, `*` |
|
||||
|
||||
`*` matches every model key. Typed providers (OpenAI, Anthropic, Google Gemini, Vertex AI, Amazon Bedrock, Microsoft Foundry) show Known Models from the catalog. [OpenRouter](/manage/ai/providers/open-router), [Vercel AI Gateway](/manage/ai/providers/vercel-ai-gateway), and [Custom](/manage/ai/providers/custom) skip that list; enter custom keys or patterns.
|
||||
|
||||
### Allow vs Block
|
||||
|
||||
Allow is the positive set. Block subtracts even when allow matched. Allow `*` and block `gpt-4o-mini` to permit every key except that one. Allow `gpt-*` and leave block empty to permit only keys that start with `gpt-`.
|
||||
|
||||
### Model Discovery
|
||||
|
||||
`GET /v1/models` is answered from these lists plus the catalog. The gateway does not proxy that listing call upstream.
|
||||
|
||||
- Exact allow keys are listed as themselves.
|
||||
- Patterns expand against the catalog for typed providers (`claude-*` becomes every matching catalog id).
|
||||
- On OpenRouter, Vercel AI Gateway, and Custom, add exact allow keys to make those models show up in discovery. A wildcard still matches at request time.
|
||||
|
||||
A block pattern hides a model from discovery the same way it would reject the request.
|
||||
|
||||
## Inherit vs Select
|
||||
|
||||
On an AI Gateway resource, attach providers from the **AI Gateway** settings tab. Each attachment has an access mode:
|
||||
|
||||
| Mode | What the resource uses |
|
||||
|---|---|
|
||||
| **Inherit** | The provider's allow and block lists as configured on the provider |
|
||||
| **Select** | A subset of that provider's allow-list models, chosen on this resource |
|
||||
|
||||
The same provider can be inherited on one resource and selected on another. You can disable an attachment without removing the provider.
|
||||
|
||||
Select only offers models already on the provider's allow list. Use inherit when every resource should see the same keys. Use select when one gateway should expose a narrower set (for example a public resource with `gpt-4o` only, while the provider still allows `gpt-*`).
|
||||
|
||||
## Provider Selection
|
||||
|
||||
When a resource has more than one attached provider, the gateway picks one per request. Overlapping allows are allowed at save time. Collisions are resolved, or rejected, when the request arrives.
|
||||
|
||||
Steps, in order. Later steps run only while more than one candidate remains:
|
||||
|
||||
1. **Capability.** The path must match a [capability](/manage/ai/providers/configuration#capabilities) the provider advertises.
|
||||
2. **Allow and block.** The effective lists (inherit or select) must pass.
|
||||
3. **Specificity.** The most specific matching allow wins: exact keys beat patterns, fewer wildcards win, longer literals win. `gpt-4o` beats `gpt-*` beats `*`.
|
||||
4. **Catalog ownership.** A typed provider whose catalog contains the id beats an aggregator that also allows it.
|
||||
5. **Class.** Native typed providers beat aggregators (OpenRouter, Vercel AI Gateway), which beat Custom.
|
||||
6. **Ambiguous.** If more than one distinct provider remains, the gateway returns `403` with `Model "<id>" is ambiguous across multiple AI providers on this resource`.
|
||||
|
||||
Typical ambiguous cases: two OpenAI providers both allowing `*`, or two aggregators both allowing `*` with no native owner on the resource.
|
||||
|
||||
`GET /v1/models` skips steps 3-6. There is no requested model to disambiguate, so the gateway returns the union of what every attached provider advertising Anthropic Models would accept.
|
||||
|
||||
### Examples
|
||||
|
||||
**OpenAI + Anthropic, both `*`.** `POST /v1/chat/completions` with `gpt-4o` goes to OpenAI. Anthropic never reaches scoring; capability alone decides. `POST /v1/messages` with a Claude model goes to Anthropic.
|
||||
|
||||
**OpenAI + OpenRouter, both `*`.** `gpt-4o` on Chat Completions matches both. Specificity is a tie (`*` vs `*`). Catalog ownership sends it to OpenAI.
|
||||
|
||||
**Two OpenAI providers, both `*`.** Capability, lists, specificity, catalog, and class all leave both candidates. Result is the ambiguous error. Narrow at least one allow list, disable one attachment, or split them across resources.
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: "OpenRouter"
|
||||
description: "Connect OpenRouter as an AI Gateway provider"
|
||||
---
|
||||
|
||||
An OpenRouter provider forwards Chat Completions requests to OpenRouter. Use it as an aggregator when you want one key to reach many upstream models through OpenAI Chat Completions.
|
||||
|
||||
See [AI Providers](/manage/ai/providers/overview) if you haven't created a provider before. Field options are in [Provider Configuration](/manage/ai/providers/configuration).
|
||||
|
||||
## Defaults
|
||||
|
||||
| Setting | Value |
|
||||
|---|---|
|
||||
| Upstream URL | `https://openrouter.ai/api/v1` |
|
||||
| Auth type | Bearer |
|
||||
| Capabilities | OpenAI Chat Completions |
|
||||
|
||||
OpenRouter's default capability is Chat Completions only. Clients that require Responses or Anthropic Messages need a different provider, extra capabilities this OpenRouter account actually supports, or a [custom](/manage/ai/providers/custom) endpoint.
|
||||
|
||||
## Configure
|
||||
|
||||
1. Sidebar → **AI Gateway** → **Providers** → **Create**.
|
||||
2. Set **Provider Type** to **OpenRouter**. Name, capabilities, upstream URL, and auth type fill in from the defaults above.
|
||||
3. Paste your OpenRouter API key.
|
||||
4. Set **Allow** and **Block** lists. OpenRouter model keys are often vendor-prefixed, for example `openai/gpt-4o` or `anthropic/claude-sonnet-4`.
|
||||
5. Save, then attach the provider to an [AI Gateway resource](/manage/ai/overview).
|
||||
|
||||
## Models
|
||||
|
||||
OpenRouter is an aggregator. See [Model Routing](/manage/ai/providers/model-routing) for how allow lists and `GET /v1/models` discovery work. Add exact keys such as `openai/gpt-4o` or `anthropic/claude-sonnet-4`.
|
||||
|
||||
## Clients
|
||||
|
||||
| Client | Guide |
|
||||
|---|---|
|
||||
| Codex | [Setup guide](/manage/ai/configure-ai-clients/codex) |
|
||||
| OpenCode | [Setup guide](/manage/ai/configure-ai-clients/opencode) |
|
||||
| Open WebUI | [Setup guide](/manage/ai/configure-ai-clients/open-webui) |
|
||||
| OpenClaw | [Setup guide](/manage/ai/configure-ai-clients/openclaw) |
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: "OpenAI"
|
||||
description: "Connect OpenAI as an AI Gateway provider"
|
||||
---
|
||||
|
||||
An OpenAI provider forwards Chat Completions and Responses requests to OpenAI's API. Attach it to an AI Gateway resource when you want Codex, OpenCode, Open WebUI, OpenClaw, or any other OpenAI-compatible client to use your org's OpenAI key.
|
||||
|
||||
See [AI Providers](/manage/ai/providers/overview) if you haven't created a provider before. Field options are in [Provider Configuration](/manage/ai/providers/configuration).
|
||||
|
||||
## Defaults
|
||||
|
||||
| Setting | Value |
|
||||
|---|---|
|
||||
| Upstream URL | `https://api.openai.com/v1` |
|
||||
| Auth type | Bearer |
|
||||
| Capabilities | OpenAI Chat Completions, OpenAI Responses |
|
||||
|
||||
## Configure
|
||||
|
||||
1. Sidebar → **AI Gateway** → **Providers** → **Create**.
|
||||
2. Set **Provider Type** to **OpenAI**. Name, capabilities, upstream URL, and auth type fill in from the defaults above.
|
||||
3. Paste your OpenAI API key.
|
||||
4. Set **Allow** and **Block** lists. `*` allows every model key; `gpt-*` is a typical narrower pattern.
|
||||
5. Save, then attach the provider to an [AI Gateway resource](/manage/ai/overview).
|
||||
|
||||
Leave the upstream URL as the default unless you are pointing at a compatible OpenAI-style base URL.
|
||||
|
||||
## Clients
|
||||
|
||||
| Client | Guide |
|
||||
|---|---|
|
||||
| Codex | [Setup guide](/manage/ai/configure-ai-clients/codex) |
|
||||
| OpenCode | [Setup guide](/manage/ai/configure-ai-clients/opencode) |
|
||||
| Open WebUI | [Setup guide](/manage/ai/configure-ai-clients/open-webui) |
|
||||
| OpenClaw | [Setup guide](/manage/ai/configure-ai-clients/openclaw) |
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: "AI Providers"
|
||||
description: "Connect upstream model APIs once per organization, then attach them to AI Gateway resources"
|
||||
---
|
||||
|
||||
A provider is an organization-level connection to an upstream model API. It stores the provider type, credentials, base URL, API capabilities, and optional model allow and block lists. Create providers once, then attach them to any AI Gateway resource in the org. Clients call the resource's domain, not the provider directly.
|
||||
|
||||
See [AI Gateway Overview](/manage/ai/overview) for creating a resource and issuing keys. See [Provider Configuration](/manage/ai/providers/configuration) for capabilities, auth, and routing. See [Model Routing](/manage/ai/providers/model-routing) for allow lists, inherit vs select, and how the gateway picks a provider. See [Model Catalog](/manage/ai/model-catalog) for known models and pricing used by routing and budgets.
|
||||
|
||||
## What a Provider Is
|
||||
|
||||
Sidebar → **AI Gateway** → **Providers** → **Create**.
|
||||
|
||||
Each provider is:
|
||||
|
||||
- A **type** (OpenAI, Anthropic, Google Gemini, and others) that fills in recommended defaults
|
||||
- **Credentials** and a **base URL** the gateway uses when it forwards a request
|
||||
- **Capabilities** that declare which API formats the provider can handle
|
||||
- Optional **allow** and **block** lists that restrict which model keys may be used
|
||||
|
||||
Providers are reused across resources. The resource still owns the public URL and the keys clients authenticate with.
|
||||
|
||||
## Attach to a Resource
|
||||
|
||||
On an AI Gateway resource, attach one or more providers from the **AI Gateway** settings tab. For each attachment you can inherit the provider's model lists or select a subset. Details are in [Model Routing](/manage/ai/providers/model-routing#inherit-vs-select).
|
||||
|
||||
The resource only speaks the API formats its attached providers advertise. Attach whichever providers match the [clients](/manage/ai/overview#4-connect-a-client) you plan to connect.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/ai/public-resource-providers.png" alt="AI Gateway resource settings with attached providers" />
|
||||
</Frame>
|
||||
|
||||
## Provider Types
|
||||
|
||||
| Provider | Default capabilities | Guide |
|
||||
|---|---|---|
|
||||
| OpenAI | Chat Completions, Responses | [Configure OpenAI](/manage/ai/providers/openai) |
|
||||
| Anthropic | Messages, Models | [Configure Anthropic](/manage/ai/providers/anthropic) |
|
||||
| Google Gemini | Gemini Generate Content | [Configure Google Gemini](/manage/ai/providers/google-gemini) |
|
||||
| Vertex AI | Vertex Generate Content, Raw Predict | [Configure Vertex AI](/manage/ai/providers/vertex-ai) |
|
||||
| Amazon Bedrock | Bedrock Converse | [Configure Amazon Bedrock](/manage/ai/providers/bedrock) |
|
||||
| Microsoft Foundry | Chat Completions, Responses, Messages, Models | [Configure Microsoft Foundry](/manage/ai/providers/microsoft-foundry) |
|
||||
| OpenRouter | Chat Completions | [Configure OpenRouter](/manage/ai/providers/open-router) |
|
||||
| Vercel AI Gateway | Chat Completions, Responses | [Configure Vercel AI Gateway](/manage/ai/providers/vercel-ai-gateway) |
|
||||
| Custom | Whatever you select | [Configure Custom](/manage/ai/providers/custom) |
|
||||
|
||||
Custom also covers local engines, downstream routers, and cloud APIs sent through a site. See [Ollama](/manage/ai/providers/custom/ollama), [vLLM](/manage/ai/providers/custom/vllm), [Bifrost](/manage/ai/providers/custom/bifrost), and [Cloud APIs Through a Site](/manage/ai/providers/custom/cloud-apis-through-a-site).
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
title: "Vercel AI Gateway"
|
||||
description: "Connect Vercel AI Gateway as an AI Gateway provider"
|
||||
---
|
||||
|
||||
A Vercel AI Gateway provider forwards Chat Completions and Responses requests to Vercel's AI Gateway. Use it as an aggregator when you want one key to reach models Vercel exposes through OpenAI-compatible APIs.
|
||||
|
||||
See [AI Providers](/manage/ai/providers/overview) if you haven't created a provider before. Field options are in [Provider Configuration](/manage/ai/providers/configuration).
|
||||
|
||||
## Defaults
|
||||
|
||||
| Setting | Value |
|
||||
|---|---|
|
||||
| Upstream URL | `https://ai-gateway.vercel.sh/v1` |
|
||||
| Auth type | Bearer |
|
||||
| Capabilities | OpenAI Chat Completions, OpenAI Responses |
|
||||
|
||||
## Configure
|
||||
|
||||
1. Sidebar → **AI Gateway** → **Providers** → **Create**.
|
||||
2. Set **Provider Type** to **Vercel AI Gateway**. Name, capabilities, upstream URL, and auth type fill in from the defaults above.
|
||||
3. Paste your Vercel AI Gateway API key.
|
||||
4. Set **Allow** and **Block** lists. Use the model ids Vercel documents for the gateway.
|
||||
5. Save, then attach the provider to an [AI Gateway resource](/manage/ai/overview).
|
||||
|
||||
## Models
|
||||
|
||||
Vercel AI Gateway is an aggregator. See [Model Routing](/manage/ai/providers/model-routing) for how allow lists and `GET /v1/models` discovery work. Add exact model keys to the allow list.
|
||||
|
||||
## Clients
|
||||
|
||||
| Client | Guide |
|
||||
|---|---|
|
||||
| Codex | [Setup guide](/manage/ai/configure-ai-clients/codex) |
|
||||
| OpenCode | [Setup guide](/manage/ai/configure-ai-clients/opencode) |
|
||||
| Open WebUI | [Setup guide](/manage/ai/configure-ai-clients/open-webui) |
|
||||
| OpenClaw | [Setup guide](/manage/ai/configure-ai-clients/openclaw) |
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: "Vertex AI"
|
||||
description: "Connect Google Vertex AI as an AI Gateway provider"
|
||||
---
|
||||
|
||||
A Vertex AI provider forwards Vertex `generateContent` and `rawPredict` requests to your Google Cloud project. Use it when clients call Vertex AI publisher-model paths rather than the Gemini Developer API.
|
||||
|
||||
See [AI Providers](/manage/ai/providers/overview) if you haven't created a provider before. Field options are in [Provider Configuration](/manage/ai/providers/configuration).
|
||||
|
||||
For Google's Gemini Developer API, use [Google Gemini](/manage/ai/providers/google-gemini) instead.
|
||||
|
||||
## Defaults
|
||||
|
||||
| Setting | Value |
|
||||
|---|---|
|
||||
| Upstream URL | You enter the regional Vertex AI base URL |
|
||||
| Auth type | Bearer |
|
||||
| Capabilities | Vertex Generate Content, Vertex Raw Predict |
|
||||
|
||||
Vertex Generate Content covers Gemini-format calls on Vertex. Vertex Raw Predict covers Anthropic models hosted on Vertex (`rawPredict` / `streamRawPredict`).
|
||||
|
||||
## Configure
|
||||
|
||||
1. Sidebar > **AI Gateway** > **Providers** > **Create**.
|
||||
2. Set **Provider Type** to **Vertex AI**. Capabilities and auth type fill in from the defaults above.
|
||||
3. Set **Upstream URL** to your regional Vertex host, for example `https://us-central1-aiplatform.googleapis.com`. The host includes the location.
|
||||
4. Paste a Bearer credential the Vertex endpoint accepts (typically a Google OAuth access token).
|
||||
5. Set **Allow** and **Block** lists. Use the model ids Vertex expects, including publisher-qualified keys when that is how the client sends them.
|
||||
6. Save, then attach the provider to an [AI Gateway resource](/manage/ai/overview).
|
||||
|
||||
The gateway authenticates with `Authorization: Bearer <key>`. Add extra capabilities on the **General** tab only if this Vertex endpoint actually speaks those API formats.
|
||||
|
||||
## Clients
|
||||
|
||||
Clients that call Vertex `generateContent` or `rawPredict` paths work with the default capabilities. Gemini CLI talks the Gemini Developer API (`gemini_generate_content`), so it needs a [Google Gemini](/manage/ai/providers/google-gemini) provider rather than this one.
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: "AI Session Logs"
|
||||
description: "Prompt and response transcripts for AI Gateway requests in an organization"
|
||||
---
|
||||
|
||||
Session logs store the prompt and response for each AI Gateway call. Open **AI Gateway > Session Logs** to browse them. Use them to see what a client sent, what the upstream returned, and which user, key, provider, and resource handled the call.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/ai/session-logs.png" alt="AI Gateway session logs table in the Pangolin dashboard" />
|
||||
</Frame>
|
||||
|
||||
<Note>
|
||||
Session logs are only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
Token counts and estimated USD live in [Usage Analytics](/manage/ai/usage-analytics). Those records are separate. Changing session log retention does not delete usage data.
|
||||
|
||||
<Tip>
|
||||
Set **AI Gateway Session Log Retention** in org settings before you expect rows. The default is 0 days, which leaves logging off.
|
||||
</Tip>
|
||||
|
||||
## Enable Retention
|
||||
|
||||
In org security settings, set **AI Gateway Session Log Retention** to how long Pangolin should keep transcripts. Logging starts only after that value is greater than zero. Older rows are removed when they pass the retention window.
|
||||
|
||||
## What Each Row Contains
|
||||
|
||||
After a gateway call that belongs to an organization, Pangolin stores:
|
||||
|
||||
- Session id (shared with the usage record for that call)
|
||||
- Time, HTTP status, and whether the response was streamed
|
||||
- [Capability](/manage/ai/providers/configuration#capabilities), [provider](/manage/ai/providers/overview), and requested model
|
||||
- Public or private [resource](/manage/ai/multiple-gateway-resources)
|
||||
- User and [virtual API key](/manage/ai/virtual-api-keys), when they are known
|
||||
- Raw request and response bodies
|
||||
- Normalized chat messages used for the transcript view
|
||||
|
||||
Expand a row to see session id, status, catalog cost and tokens (joined from usage), and the transcript. Toggle between the chat view and raw JSON.
|
||||
|
||||
Very large bodies are truncated at about 200,000 characters. Truncated rows are marked in the UI.
|
||||
|
||||
## Filters
|
||||
|
||||
Narrow the table by date range, then:
|
||||
|
||||
- Provider
|
||||
- Capability
|
||||
- Resource
|
||||
- User
|
||||
- Virtual API key
|
||||
- Model
|
||||
- Streaming or not
|
||||
|
||||
## Export
|
||||
|
||||
Export the current filters as CSV from the table, the same way as other organization logs.
|
||||
|
||||
## Streaming
|
||||
|
||||
Session logs can be streamed (like all other logs) to upstream SIEM providers or other data sources. You can set this up following the [streaming](/manage/analytics/streaming) documentation.
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: "AI Usage Analytics"
|
||||
description: "Cost, token usage, and request volume across providers, resources, roles, users, and keys"
|
||||
---
|
||||
|
||||
Usage analytics rolls up every AI Gateway call: how many requests ran, how many tokens they used, and estimated USD. Open **AI Gateway → Usage Analytics**. The numbers come from the same recorded usage that [budgets](/manage/ai/budgets) enforce.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/ai/analytics-overview.png" alt="Usage analytics overview with cost, token, and request charts" />
|
||||
</Frame>
|
||||
|
||||
These records are independent of session log retention. For prompt and response text, see [Session Logs](/manage/ai/session-logs). The two share a session id per call.
|
||||
|
||||
## How Numbers Are Calculated
|
||||
|
||||
USD uses [catalog](/manage/ai/model-catalog) rates (`in`, `out`, `cache`, `reasoning`) when the model id is known. Unknown models have no USD amount. Tokens still sum.
|
||||
|
||||
Token buckets are prompt, cache-read, cache-write, completion, and reasoning. If the upstream omits usage, Pangolin estimates tokens from the request and response. Overview shows **Estimated**: the share of requests that used that path.
|
||||
|
||||
A non-2xx response from the provider still counts as a request with zero tokens, so volume charts include failed attempts.
|
||||
|
||||
These figures are estimates for the dashboard and for budgets, not a copy of the vendor invoice. See [Budgets](/manage/ai/budgets) for the same calculation used at enforcement time.
|
||||
|
||||
## Filters
|
||||
|
||||
Every tab uses the same filters. The default date range is the last seven days.
|
||||
|
||||
- Provider
|
||||
- Model
|
||||
- Resource
|
||||
- Role
|
||||
- User
|
||||
- Virtual API key
|
||||
|
||||
## Tabs
|
||||
|
||||
**Overview** shows totals for requests, tokens, cost, and estimated percent, daily charts for each, and top models by cost.
|
||||
|
||||
**Providers**, **Resources**, **Roles**, **Users**, and **Virtual API Keys** each show cost and token trends plus a ranked list for that dimension.
|
||||
|
||||
Role is derived from the caller's org roles. Usage rows store the user id, not the role. A [manual key](/manage/ai/virtual-api-keys#manual-keys) with no attributed user appears as an unknown user.
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
title: "Virtual API Keys"
|
||||
description: "Authenticate AI clients to public AI Gateway resources without sharing upstream provider keys"
|
||||
---
|
||||
|
||||
A virtual API key is a Pangolin credential that AI clients send to a public AI Gateway resource. Users retrieve their **identity key** by logging in with their existing Pangolin account or [identity provider](/manage/identity-providers/add-an-idp). That key is bound to their Pangolin identity, so calls are attributed to them for auditing, tracking, and access enforcement. When the user is known, Pangolin also forwards that identity upstream as [`Remote-*` headers](/manage/ai/providers/configuration#identity-headers).
|
||||
|
||||
The real OpenAI, Anthropic, Gemini, or other provider key stays on the [provider](/manage/ai/providers/overview). Pangolin checks the virtual key, then forwards the request with the upstream secret.
|
||||
|
||||
These keys are what coding agents put in their API key field. They are not [Integration API](/manage/integration-api) organization or root keys.
|
||||
|
||||
## Why Virtual API Keys
|
||||
|
||||
Agents and SDKs expect an API key. Virtual keys let many people and services call the gateway while the org keeps a single upstream provider key.
|
||||
|
||||
- Revoke or rotate one person or agent's access without rotating the provider key
|
||||
- Attribute usage to a user or a named key
|
||||
- Use the same Pangolin credential in clients that send `Authorization: Bearer`, `x-api-key`, or `x-goog-api-key`
|
||||
|
||||
Pangolin accepts the virtual key in those headers and strips it before the upstream call. Keys look like `pangolin-key-<id>.<secret>`.
|
||||
|
||||
Logging into Pangolin in a browser is how you **retrieve** a key. Model calls still need the virtual key in the request. A dashboard session cookie cannot proxy through the gateway.
|
||||
|
||||
## Public Resources Only
|
||||
|
||||
Virtual keys apply to **public** AI Gateway resources. [Private resources](/manage/resources/understanding-resources) are reached through the [Pangolin client](/manage/clients/install-client), so the gateway does not check a key. Clients still need a placeholder in the key field; use the literal string `none`. Deleting the field usually breaks the client.
|
||||
|
||||
## Authentication Is Always On
|
||||
|
||||
Public AI Gateway resources always require authentication. Unlike [HTTPS public resources](/manage/resources/public/authentication), you cannot turn Platform SSO off.
|
||||
|
||||
Assign [users and roles](/manage/access-control/create-user) on the resource the same way you would for a public HTTPS resource. Those grants control who can use an **identity key**.
|
||||
|
||||
HTTPS resources can add PIN, passcode, header auth, shareable links, or email OTP for browser access. AI clients authenticate programmatically, so the gateway uses virtual API keys instead of those methods.
|
||||
|
||||
## Identity Keys vs Manual Keys
|
||||
|
||||
Both kinds are `pangolin-key-…` secrets. They differ in how access is granted.
|
||||
|
||||
### Identity Keys
|
||||
|
||||
Every organization user already has an identity key. It is unique to their account and authenticates them as that user.
|
||||
|
||||
Access follows the resource's **Users** and **Roles**, the same way a public HTTPS resource does. Grant the user or their role on the AI Gateway resource, or their identity key cannot call it.
|
||||
|
||||
Admins do not mint identity keys; they already exist. To send them out, see [Email Keys to Users](#email-keys-to-users).
|
||||
|
||||
### Manual Keys
|
||||
|
||||
Admins create manual keys at **AI Gateway → Virtual API Keys → Virtual Keys**. Scope a key to selected public AI gateways, or to every public AI gateway in the org.
|
||||
|
||||
Creating the key immediately grants access to those resources, regardless of the users and roles set on the resource. Use this for services, CI, and shared agents that should not depend on a person's resource grants.
|
||||
|
||||
You can optionally associate a user. That association is for usage tracking and so the key appears on that user's keys page. It does not change which resources the key can call.
|
||||
|
||||
When you create or edit a key, you can attach a [budget](/manage/ai/budgets) so spend or tokens on that key are capped.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/ai/create-virtual-api-key.png" alt="Create Virtual API Key dialog with name, description, and public AI gateway access" />
|
||||
</Frame>
|
||||
|
||||
## Where to Get a Key
|
||||
|
||||
Your identity key and any manual keys attributed to you are available in three places:
|
||||
|
||||
1. Visit the public resource URL in a browser and log in with your Pangolin identity. After login, Pangolin shows the key.
|
||||
2. Open the Resource Launcher, select the resource, and use the more-info side panel.
|
||||
3. Go to `https://app.pangolin.net/<org-id>/keys` (the org **Your API Keys** page). On self-hosted Pangolin, use the same path on your dashboard URL, for example `https://pangolin.example.com/<org-id>/keys`.
|
||||
|
||||
See [Organization ID](/manage/organizations/org-id) if you need to look up `<org-id>`.
|
||||
|
||||
Put that key in the AI client. Visiting the resource in a browser only reveals the key; it does not authenticate the agent's requests.
|
||||
|
||||
## Email Keys to Users
|
||||
|
||||
Admins can email keys so users do not have to find the dashboard page themselves. The message includes the secret and public AI gateway URLs they can call.
|
||||
|
||||
### Email Identity Keys
|
||||
|
||||
**AI Gateway → Virtual API Keys → Identity Keys → Email Identity Keys.** Send every organization member their identity key, or pick specific users and roles. Recipients without an account email are skipped.
|
||||
|
||||
### Email Manual Keys
|
||||
|
||||
When you create or edit a manual key, enable **Email key upon generation** (or **Email this key**). Send it to the associated user and to extra addresses you add.
|
||||
|
||||
Self-hosted Pangolin needs [SMTP](/self-host/advanced/config-file#email-configuration) configured before either flow can send mail.
|
||||
@@ -0,0 +1,214 @@
|
||||
---
|
||||
title: "Alert Rules"
|
||||
description: "Subscribe to Pangolin events on sites, resources, and health checks and deliver email, webhooks, or integrations"
|
||||
---
|
||||
|
||||
<Note>
|
||||
Only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) and [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
Alert rules let you react to changes in Pangolin: you pick a source (what to watch), a trigger (which change matters), and one or more actions (what to do). For example, when a site moves from online to offline, email your admins and support; when a [health check](/manage/alerting/health-checks) or resource becomes unhealthy, call a webhook so a tool like Zapier can fan out the event.
|
||||
|
||||
## Where to create rules
|
||||
|
||||
Create and manage rules from the Alert rules page under Alerting for your organization.
|
||||
|
||||
You can also start a rule from a site or resource detail page: use Create alert rule near the uptime graph to prefill context and keep the flow short.
|
||||
|
||||
## Actions
|
||||
|
||||
When a trigger fires, Pangolin can:
|
||||
|
||||
- Send email (recipients described below).
|
||||
- Call a webhook with a JSON payload (see [Webhook payloads](#webhook-payloads)).
|
||||
- Open an incident or ticket in PagerDuty, Opsgenie, ServiceNow, or incident.io.
|
||||
|
||||
You can attach several actions to the same rule (for example email plus a webhook).
|
||||
|
||||
### Email
|
||||
|
||||
Choose users in your Pangolin organization, entire roles, and/or arbitrary email addresses that should receive the message when the condition is met.
|
||||
|
||||
### Webhooks
|
||||
|
||||
Webhook actions issue an HTTP request to your endpoint when the trigger runs. Payloads are JSON and follow the shapes in [Webhook payloads](#webhook-payloads).
|
||||
|
||||
## Creating an alert rule
|
||||
|
||||
<Frame caption="Create alert rule: source (e.g. all sites), trigger (e.g. site status changes), and actions (email)">
|
||||
<img src="/images/create-alert-rule.png" alt="Create alert rule wizard in the Pangolin dashboard" />
|
||||
</Frame>
|
||||
|
||||
### 1. Source
|
||||
|
||||
Choose what entity the rule watches:
|
||||
|
||||
| Source type | Meaning |
|
||||
| --- | --- |
|
||||
| Site | One or more [sites](/manage/sites/understanding-sites) |
|
||||
| Resource | One or more resources in the org |
|
||||
| Health check | One or more [health checks](/manage/alerting/health-checks) |
|
||||
|
||||
For each type, decide whether the rule applies to all of that kind (for example all sites) or only specific sites, resources, or health checks you select.
|
||||
|
||||
### 2. Trigger
|
||||
|
||||
Available triggers depend on the source type. For sites, options include coming online, going offline, or any status change. For resources and health checks, you get healthy, unhealthy, and combined toggle-style triggers that match how those entities change state-the dashboard only lists combinations that apply to what you selected.
|
||||
|
||||
Pick the condition that should fire the rule (for example site status changes when you care about both online and offline transitions).
|
||||
|
||||
### 3. Actions
|
||||
|
||||
Configure what happens when the trigger runs: add one or more actions (email, webhook, or a vendor integration). Use Add action to stack multiple destinations for the same rule.
|
||||
|
||||
## Webhook payloads
|
||||
|
||||
Webhook bodies are JSON. Every event includes `event`, ISO-8601 `timestamp`, and a `data` object. The event name tells you what changed; `data` always includes `orgId` and entity-specific fields.
|
||||
|
||||
`{{data}}` is an object and must be treated as such. If you can not support a object, you can also use the flattened fields shown in the examples below. For example `{{orgId}}`, `{{siteId}}`, and `{{siteName}}` are all available on a site alert.
|
||||
|
||||
### Site events
|
||||
|
||||
#### `site_online`
|
||||
|
||||
A site came back online.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "site_online",
|
||||
"timestamp": "2025-06-15T12:34:56.789Z",
|
||||
"data": {
|
||||
"orgId": "org_abc123",
|
||||
"siteId": 42,
|
||||
"siteName": "us-east-prod"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `site_offline`
|
||||
|
||||
A site went offline.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "site_offline",
|
||||
"timestamp": "2025-06-15T12:34:56.789Z",
|
||||
"data": {
|
||||
"orgId": "org_abc123",
|
||||
"siteId": 42,
|
||||
"siteName": "us-east-prod"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `site_toggle`
|
||||
|
||||
Fires when site connectivity changes, alongside both `site_online` and `site_offline`. Use this when you only care that status flipped, not which direction. `siteId` is always present in `data`.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "site_toggle",
|
||||
"timestamp": "2025-06-15T12:34:56.789Z",
|
||||
"data": {
|
||||
"orgId": "org_abc123",
|
||||
"siteId": 42,
|
||||
"siteName": "us-east-prod"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Health check events
|
||||
|
||||
#### `health_check_healthy`
|
||||
|
||||
A health check recovered.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "health_check_healthy",
|
||||
"timestamp": "2025-06-15T12:34:56.789Z",
|
||||
"data": {
|
||||
"orgId": "org_abc123",
|
||||
"healthCheckName": "API /healthz"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `health_check_unhealthy`
|
||||
|
||||
A health check is failing.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "health_check_unhealthy",
|
||||
"timestamp": "2025-06-15T12:34:56.789Z",
|
||||
"data": {
|
||||
"orgId": "org_abc123",
|
||||
"healthCheckName": "API /healthz"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `health_check_toggle`
|
||||
|
||||
Fires alongside healthy and unhealthy transitions. `healthCheckId` is included in `data` for this combined event.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "health_check_toggle",
|
||||
"timestamp": "2025-06-15T12:34:56.789Z",
|
||||
"data": {
|
||||
"orgId": "org_abc123",
|
||||
"healthCheckId": 7,
|
||||
"healthCheckName": "API /healthz"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Resource events
|
||||
|
||||
#### `resource_healthy`
|
||||
|
||||
A resource recovered.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "resource_healthy",
|
||||
"timestamp": "2025-06-15T12:34:56.789Z",
|
||||
"data": {
|
||||
"orgId": "org_abc123",
|
||||
"resourceName": "internal-dashboard"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `resource_unhealthy`
|
||||
|
||||
A resource is unhealthy.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "resource_unhealthy",
|
||||
"timestamp": "2025-06-15T12:34:56.789Z",
|
||||
"data": {
|
||||
"orgId": "org_abc123",
|
||||
"resourceName": "internal-dashboard"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `resource_toggle`
|
||||
|
||||
Fires alongside healthy and unhealthy transitions, or when a resource is enabled or disabled. `resourceId` is included in `data`.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "resource_toggle",
|
||||
"timestamp": "2025-06-15T12:34:56.789Z",
|
||||
"data": {
|
||||
"orgId": "org_abc123",
|
||||
"resourceId": 15,
|
||||
"resourceName": "internal-dashboard"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: "Health Checks"
|
||||
description: "Monitor reachability and response for public resource targets and arbitrary endpoints from your sites"
|
||||
---
|
||||
|
||||
A health check is a periodic probe that decides whether something on your network is up and responding the way you expect. Pangolin runs these checks from your sites so they reflect reachability from the connector’s perspective, not only from wherever an administrator happens to be.
|
||||
|
||||
## Health Checks on Public Resource Targets
|
||||
|
||||
You can attach health checks to individual targets on public resources. When a target fails its health check, Pangolin treats it as unhealthy: it is removed from rotation and load balancing until it passes again, so traffic is not sent to a broken upstream. That behavior is configured per target alongside your proxy settings.
|
||||
|
||||
For step-by-step setup, states (healthy / unhealthy / unknown), and routing implications, see [Health checks & failover](/manage/resources/public/healthchecks-failover).
|
||||
|
||||
## Health Checks in Alerting
|
||||
|
||||
Under Alerting → Health checks for your organization, you get a single view of health checks tied to public resource targets, so you can see status across resources without opening each resource separately.
|
||||
|
||||
<Frame caption="Create health check in the Pangolin dashboard">
|
||||
<img src="/images/create-healthcheck.png" alt="Create health check form in the Pangolin dashboard" />
|
||||
</Frame>
|
||||
|
||||
### Arbitrary Health Checks
|
||||
|
||||
<Note>
|
||||
Arbitrary health checks are only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) and [Enterprise Edition](/self-host/enterprise-edition). Health checks attached to public resource targets are available in all editions.
|
||||
</Note>
|
||||
|
||||
In addition to target-linked checks, you can create standalone health checks that are not attached to any routable resource target. They work the same way at the probe layer, with the same protocols and timing concepts, but only represent reachability for an address your sites can reach (for example an IP or hostname on a remote LAN). You choose which site runs the check so it stays within an addressable range for that connector.
|
||||
|
||||
These are useful when you care about whether a system is up, even if it is not modeled as a Pangolin resource like a network printer, an IP camera, a legacy server, or anything else that should be watched from the site’s network. Pair them with [Alert rules](/manage/alerting/alert-rules) to send notifications when something goes unhealthy or recovers.
|
||||
|
||||
## Check Types
|
||||
|
||||
There are two kinds of checks: HTTP and TCP.
|
||||
|
||||
### HTTP
|
||||
|
||||
An HTTP health check issues an HTTP or HTTPS request to a URL you specify. You can tune the scheme (`http` or `https`), HTTP method (for example `GET` or `POST`), path, headers, expected status codes, and anything else needed to match how the service exposes a liveness endpoint. Success means the response satisfies your criteria (including status code and optional body rules, depending on configuration).
|
||||
|
||||
### TCP
|
||||
|
||||
A TCP health check does not speak application data: it tries to open a TCP connection to a host and port. If the TCP handshake completes, the check is treated as passing; if nothing answers or the connection is refused or times out, it fails. That is ideal for services that only expose a plain port (databases, cameras, PLCs) or when you only care that the host is reachable on a given port.
|
||||
|
||||
## Timing and Thresholds
|
||||
|
||||
Both HTTP and TCP checks support configuration for how often probes run when things are healthy versus when they are failing, how many successes or failures are required before flipping state, and related tuning (for example healthy interval, unhealthy interval, healthy threshold, unhealthy threshold). Exact field names appear in the dashboard; the intent is to avoid flapping—brief blips should not instantly mark a host down, and recovery should be confirmed before treating it as fully healthy again.
|
||||
|
||||
## How the pieces fit together
|
||||
|
||||
- Target-linked health checks on public resources drive routing: unhealthy targets drop out of the pool until they recover.
|
||||
- Arbitrary checks track reachability for addresses your sites can reach—dashboard visibility and [Alert rules](/manage/alerting/alert-rules)—even when there is no Pangolin resource for that system.
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: "Authentication Logs"
|
||||
description: "Authentication logs are a record of each authenticated access attempt to a resource"
|
||||
---
|
||||
Authentication logs provide detailed information about each access attempt made to your Pangolin resources. These logs help you monitor and analyze user activity each time they attempt to authenticate.
|
||||
|
||||
<Note>
|
||||
Authentication logs are only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
## What are Authentication Logs?
|
||||
|
||||
Authentication logs capture authentication events when users or API keys attempt to access a resource. They record whether the authentication was successful or failed, along with contextual information about the attempt. These logs are useful for:
|
||||
|
||||
- Monitoring authentication patterns and login attempts
|
||||
- Tracking which users are accessing which resources
|
||||
- Identifying failed authentication attempts for security analysis
|
||||
- Understanding geographic distribution of access attempts
|
||||
- Analyzing user agent and device information
|
||||
|
||||
<Frame>
|
||||
<img src="/images/access_logs.png" alt="Authentication logs table in the Pangolin dashboard"/>
|
||||
</Frame>
|
||||
|
||||
<Tip>Make sure to enable authentication logs in the org settings</Tip>
|
||||
|
||||
## Authentication Log Fields
|
||||
|
||||
Each authentication log entry contains the following fields:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `timestamp` | number | Unix timestamp (in seconds) when the access attempt occurred |
|
||||
| `action` | boolean | Whether the access was allowed (`true`) or denied (`false`) |
|
||||
| `type` | string | The type of authentication event (e.g., "login", "password", "pincode") |
|
||||
| `actorType` | string | The type of actor making the access attempt ("user" or "apiKey") |
|
||||
| `actor` | string | The display name of the actor (username or API key name) |
|
||||
| `actorId` | string | The unique identifier for the actor (user ID or API key ID) |
|
||||
| `resourceId` | number | The ID of the resource being accessed (if applicable) |
|
||||
| `ip` | string | The IP address of the client making the access attempt |
|
||||
| `location` | string | The geographic location (country code) based on IP address |
|
||||
| `userAgent` | string | The user agent string of the client browser or application |
|
||||
| `metadata` | string | Additional contextual information in JSON format |
|
||||
|
||||
## Log Retention
|
||||
|
||||
Authentication log retention is controlled by the organization setting. By default, authentication logs are retained for 0 days (disabled).
|
||||
|
||||
## Exporting
|
||||
|
||||
Logs can be exported into CSV format for external analysis and archival. Logs can be exported from the table view in the Pangolin dashboard or via the Pangolin API. When exporting, you can specify date ranges and filters to narrow down the logs you need.
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: "Admin Action Logs"
|
||||
description: "Admin Action logs are a record of each event taken by users in the organization"
|
||||
---
|
||||
Admin Action logs provide an audit trail of administrative actions and configuration changes made within your Pangolin organization. These logs help you track who made what changes and when.
|
||||
|
||||
<Note>
|
||||
Admin Action logs are only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
## What are Admin Action Logs?
|
||||
|
||||
Admin Action logs capture administrative events and configuration changes performed by users or API keys in the Pangolin dashboard. They record management operations such as creating resources, modifying settings, managing users, and other organizational changes. These logs are useful for:
|
||||
|
||||
- Maintaining an audit trail of configuration changes
|
||||
- Tracking administrative actions for compliance
|
||||
- Identifying who made specific changes to your infrastructure
|
||||
- Troubleshooting configuration issues by reviewing recent changes
|
||||
- Meeting security and compliance requirements
|
||||
|
||||
<Frame>
|
||||
<img src="/images/action_logs.png" alt="Admin action logs table in the Pangolin dashboard"/>
|
||||
</Frame>
|
||||
|
||||
<Tip>Make sure to enable access logs in the org settings</Tip>
|
||||
|
||||
## Admin Action Log Fields
|
||||
|
||||
Each action log entry contains the following fields:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `timestamp` | number | Unix timestamp (in seconds) when the action was performed |
|
||||
| `action` | string | The specific action that was performed (e.g., "createResource", "updateUser", "deleteTarget") |
|
||||
| `actorType` | string | The type of actor performing the action ("user" or "apiKey") |
|
||||
| `actor` | string | The display name of the actor (username or API key name) |
|
||||
| `actorId` | string | The unique identifier for the actor (user ID or API key ID) |
|
||||
| `metadata` | string | Additional contextual information about the action in JSON format (often contains request parameters) |
|
||||
|
||||
## Log Retention
|
||||
|
||||
Admin Action log retention is controlled by the organization settings. By default, admin action logs are retained for 0 days (disabled).
|
||||
|
||||
## Exporting
|
||||
|
||||
Logs can be exported into CSV format for external analysis and archival. Logs can be exported from the table view in the Pangolin dashboard or via the Pangolin API. When exporting, you can specify date ranges and filters to narrow down the logs you need.
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: "Network Logs"
|
||||
description: "Network logs are a record of TCP and UDP sessions between clients and private resources on sites"
|
||||
---
|
||||
|
||||
Network logs record each TCP and UDP session that traverses the tunnel between Pangolin clients and resources on your sites. They apply to private resources reached through the Pangolin client (and related tunnel traffic), not to public resources served only through the reverse proxy. You can see which clients and users opened sessions to which private resources, the source and destination addresses and protocols (TCP and UDP), the start and end times of the sessions, and more.
|
||||
|
||||
<Note>
|
||||
Network logs are only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
## What are Network Logs?
|
||||
|
||||
Network logs capture tunnel sessions from clients to private resources. They are useful for:
|
||||
|
||||
- Observing which clients and users opened sessions to which private resources
|
||||
- Reviewing source and destination addresses and protocols (TCP and UDP)
|
||||
- Measuring traffic volume with transmitted and received byte counts
|
||||
- Auditing session start and end times for troubleshooting and compliance
|
||||
|
||||
Network logs are synchronized to the cloud every 30–60 seconds. A brief delay before entries appear in the table is expected.
|
||||
|
||||
<Tip>Make sure to enable network logging in the org settings</Tip>
|
||||
|
||||
## Network Log Fields
|
||||
|
||||
Each network log entry contains the following fields:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `timestamp` | number | Unix timestamp (in seconds) when the session started |
|
||||
| `protocol` | string | Transport protocol for the session (`tcp` or `udp`) |
|
||||
| `siteResourceId` | number \| null | The ID of the [private resource](/manage/resources/understanding-resources) for the session (if applicable) |
|
||||
| `clientId` | number \| null | The Pangolin [client ID](/manage/clients/understanding-clients) for the session |
|
||||
| `clientEndpoint` | string \| null | The client-side endpoint for the session (e.g. `123.123.123.123:12345`) |
|
||||
| `userId` | string \| null | The user ID when the session is tied to an authenticated user |
|
||||
| `sourceAddr` | string | Source address for the session (typically the client-side endpoint) |
|
||||
| `destAddr` | string | Destination address for the session (typically the resource-side endpoint) |
|
||||
| `duration` | number \| null | How long the session lasted (in seconds), when the session has ended |
|
||||
| `bytesTx` | number \| null | Bytes transmitted in the session |
|
||||
| `bytesRx` | number \| null | Bytes received in the session |
|
||||
|
||||
## Log Retention
|
||||
|
||||
Network log retention is controlled by the organization setting. By default, network logs are retained for 0 days (disabled).
|
||||
|
||||
<Note>
|
||||
Network logs can generate significant data volume depending on session churn and traffic. Consider your storage capacity when configuring retention periods.
|
||||
</Note>
|
||||
|
||||
## Exporting
|
||||
|
||||
Logs can be exported into CSV format for external analysis and archival. Logs can be exported from the table view in the Pangolin dashboard or via the Pangolin API. When exporting, you can specify date ranges and filters to narrow down the logs you need.
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: "HTTPS Request Logs"
|
||||
description: "Request logs are a record of each HTTP request to a resource"
|
||||
---
|
||||
HTTPS Request logs provide detailed information about every HTTP request made to your Pangolin resources. These logs capture both successful and denied requests along with comprehensive request metadata.
|
||||
|
||||
## What are HTTPS Request Logs?
|
||||
|
||||
HTTPS Request logs capture every HTTPS request that passes through a reverse proxy, including the request details, the decision made (allow or deny), and the reason for that decision. These logs are useful for:
|
||||
|
||||
- Monitoring traffic patterns and request volumes
|
||||
- Debugging access issues and rule configurations
|
||||
- Analyzing API usage and endpoint popularity
|
||||
- Understanding geographic distribution of requests
|
||||
- Identifying potential security threats or unusual traffic patterns
|
||||
- Troubleshooting connectivity and routing issues
|
||||
|
||||
<Frame>
|
||||
<img src="/images/request_logs.png" alt="HTTPS request logs table in the Pangolin dashboard"/>
|
||||
</Frame>
|
||||
|
||||
## HTTPS Request Log Fields
|
||||
|
||||
Each HTTPS request log entry contains the following fields:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `timestamp` | number | Unix timestamp (in seconds) when the request was made |
|
||||
| `action` | boolean | Whether the request was allowed (`true`) or denied (`false`) |
|
||||
| `reason` | number | Numeric code indicating the reason for the decision |
|
||||
| `actorType` | string | The type of actor making the request ("user", "apiKey", or null for anonymous) |
|
||||
| `actor` | string | The display name of the authenticated actor (username or API key name) |
|
||||
| `actorId` | string | The unique identifier for the authenticated actor |
|
||||
| `resourceId` | number | The ID of the resource that received the request |
|
||||
| `ip` | string | The IP address of the client making the request |
|
||||
| `location` | string | The geographic location (country code) based on IP address |
|
||||
| `userAgent` | string | The user agent string of the client browser or application |
|
||||
| `metadata` | string | Additional contextual information in JSON format |
|
||||
| `headers` | string | HTTP request headers in JSON format |
|
||||
| `query` | string | URL query parameters in JSON format |
|
||||
| `originalRequestURL` | string | The full original URL of the request |
|
||||
| `scheme` | string | The protocol scheme (http or https) |
|
||||
| `host` | string | The hostname from the request |
|
||||
| `path` | string | The URL path of the request |
|
||||
| `method` | string | The HTTP method (GET, POST, PUT, DELETE, etc.) |
|
||||
| `tls` | boolean | Whether the connection used TLS/SSL |
|
||||
|
||||
## Log Retention
|
||||
|
||||
HTTPS Request log retention is controlled by the organization setting. By default, HTTPS request logs are retained for 7 days.
|
||||
|
||||
<Note>
|
||||
HTTPS Request logs can generate significant data volume depending on your traffic. Consider your storage capacity when configuring retention periods.
|
||||
</Note>
|
||||
|
||||
## Exporting
|
||||
|
||||
Logs can be exported into CSV format for external analysis and archival. Logs can be exported from the table view in the Pangolin dashboard or via the Pangolin API. When exporting, you can specify date ranges and filters to narrow down the logs you need.
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: "Event Streaming"
|
||||
description: "Stream Pangolin log events to external collectors and SIEM tools"
|
||||
---
|
||||
|
||||
Log streaming forwards your organization's audit logs to external data collectors such as Datadog, Splunk, Microsoft Sentinel, Elastic, or any HTTP endpoint you operate. You add a **destination** (how events are delivered), choose which **log types** to include, and Pangolin pushes new events as they are recorded.
|
||||
|
||||
<Note>
|
||||
Event streaming is only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
## In the dashboard
|
||||
|
||||
Open **Organization > Logs & Analytics > Streaming** to add destinations and monitor delivery status. Each destination has its own connection settings, optional body customization (where supported), and log-type selection.
|
||||
|
||||
## Log types
|
||||
|
||||
You choose which categories each destination receives. Only log types enabled for your organization can be streamed.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/streaming-log-types.png" alt="Log type selection for a streaming destination" />
|
||||
</Frame>
|
||||
|
||||
## Destination types
|
||||
|
||||
Each destination type has its own configuration and payload behavior. Select **Add destination** and pick a delivery method.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/streaming-add-destination.png" alt="Add destination dialog in the Pangolin dashboard" />
|
||||
</Frame>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="HTTP webhook" icon="globe" href="/manage/analytics/streaming/http">
|
||||
POST JSON or NDJSON to any URL. Supports custom body templates, authentication, and payload formats for SIEMs and generic webhooks.
|
||||
</Card>
|
||||
<Card title="Amazon S3" icon="bucket" href="/manage/analytics/streaming/s3">
|
||||
Upload batched audit logs to S3 or S3-compatible storage. JSON array, NDJSON, or CSV with optional gzip.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
### Other destinations
|
||||
|
||||
Amazon S3 and HTTP webhooks are documented above. For Datadog, Microsoft Sentinel, or other vendor-specific setups, contact [sales@pangolin.net](mailto:sales@pangolin.net).
|
||||
|
||||
- **No backfill:** New destinations start from the current log cursor. Historical logs already in Pangolin are not replayed.
|
||||
- **Per-log-type cursors:** Each enabled log type on a destination is tracked independently.
|
||||
- **Errors in the UI:** When delivery fails, the destination's last error is shown in the dashboard so you can fix configuration or endpoint issues.
|
||||
@@ -0,0 +1,199 @@
|
||||
---
|
||||
title: "HTTP webhook"
|
||||
description: "Forward audit logs to any HTTP endpoint with optional custom body templates"
|
||||
---
|
||||
|
||||
HTTP destinations POST your organization’s audit logs to a URL you control. Use them for generic webhooks, Splunk HEC, Elastic or OpenSearch ingest, Grafana Loki push endpoints, or any receiver that accepts JSON over HTTP.
|
||||
|
||||
<Note>
|
||||
Event streaming is only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
## Overview
|
||||
|
||||
An HTTP destination sends **POST** requests to your endpoint. Configure:
|
||||
|
||||
1. **Settings:** Name, URL, and authentication.
|
||||
2. **Headers:** Optional static headers on every request.
|
||||
3. **Body:** Default JSON shape or a custom body template, plus payload format (how batches are packaged).
|
||||
4. **Logs:** Which log types are forwarded.
|
||||
|
||||
Enable **Custom body template** when your receiver expects a different JSON layout than Pangolin’s default. Leave it off to send the standard `{ event, timestamp, data }` object per log record.
|
||||
|
||||
## Configure the connection
|
||||
|
||||
On the **Settings** tab, set a display name, the endpoint URL, and authentication:
|
||||
|
||||
| Auth type | Behavior |
|
||||
| --- | --- |
|
||||
| None | No `Authorization` header |
|
||||
| Bearer token | `Authorization: Bearer <token>` |
|
||||
| Basic auth | `Authorization: Basic <base64(user:password)>` |
|
||||
| Custom header | A single header name and value (for example an API key header) |
|
||||
|
||||
<Frame>
|
||||
<img src="/images/streaming-http-settings.png" alt="HTTP destination settings with URL and authentication options" />
|
||||
</Frame>
|
||||
|
||||
All delivery uses **POST**. Requests time out after 30 seconds.
|
||||
|
||||
## Authentication and headers
|
||||
|
||||
On the **Headers** tab, add optional static headers sent with every request, for example a vendor-specific API key or a non-default `Content-Type`. When you do not override it, Pangolin sends `Content-Type: application/json` (or `application/x-ndjson` when using the NDJSON payload format).
|
||||
|
||||
<Frame>
|
||||
<img src="/images/streaming-http-headers.png" alt="Headers tab for adding static HTTP headers" />
|
||||
</Frame>
|
||||
|
||||
## Default payload (template off)
|
||||
|
||||
When custom body template is disabled, each log event is serialized as:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "request",
|
||||
"timestamp": "2025-06-15T12:34:56.789Z",
|
||||
"data": {
|
||||
"timestamp": 1718454896,
|
||||
"action": true,
|
||||
"method": "GET",
|
||||
"path": "/api/health"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Meaning |
|
||||
| --- | --- |
|
||||
| `event` | Log type: `request`, `access`, `action`, or `connection` |
|
||||
| `timestamp` | Event time as ISO-8601 UTC |
|
||||
| `data` | The **complete stored log row** for that record, not a curated subset |
|
||||
|
||||
The field set inside `data` depends on the log type. The same destination can stream multiple types; batches may contain heterogeneous `data` shapes. See [Log type reference](#log-type-reference) below and the dedicated log docs for full field lists.
|
||||
|
||||
<Warning>
|
||||
Some columns are stored as JSON strings in the database (`headers`, `query`, and `metadata` on request logs, for example). In `data`, they appear as **string values**, not nested JSON objects. Parse them on the receiver if you need structured fields.
|
||||
</Warning>
|
||||
|
||||
## Custom body template
|
||||
|
||||
On the **Body** tab, enable **Custom body template** and provide a JSON template string. Pangolin performs simple placeholder substitution, **not** a full templating language like Handlebars.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/streaming-http-body.png" alt="Body tab with custom body template editor" />
|
||||
</Frame>
|
||||
|
||||
### Template variables
|
||||
|
||||
Only these three placeholders are supported:
|
||||
|
||||
| Variable | Source | How to use in the template |
|
||||
| --- | --- | --- |
|
||||
| `{{event}}` | Log type (`request`, `access`, `action`, `connection`) | Inside JSON **string quotes** |
|
||||
| `{{timestamp}}` | Event time (ISO-8601 UTC) | Inside JSON **string quotes** |
|
||||
| `{{data}}` | Full log row as JSON | **Never wrap in quotes**; inlined as raw JSON |
|
||||
|
||||
**Canonical example** (equivalent to the default payload):
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "{{event}}",
|
||||
"timestamp": "{{timestamp}}",
|
||||
"data": {{data}}
|
||||
}
|
||||
```
|
||||
|
||||
**Remapping property names** for a downstream schema:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "{{event}}",
|
||||
"ts": "{{timestamp}}",
|
||||
"payload": {{data}}
|
||||
}
|
||||
```
|
||||
|
||||
You may use the same token multiple times and nest placeholders at any depth in your JSON structure. Nested objects and arrays **inside** the substituted `{{data}}` value are preserved from the log row.
|
||||
|
||||
### Rules and constraints
|
||||
|
||||
- **Simple substitution only:** No conditionals, loops, filters, or expressions.
|
||||
- **No field paths:** Placeholders like `{{data.orgId}}`, `{{orgId}}`, or `{{ip}}` do **not** work. To use a single field, read it from the full `data` object on the receiver or transform after ingest.
|
||||
- **Quote `{{data}}` correctly:** `"field": {{data}}` is valid; `"field": "{{data}}"` stringifies the object incorrectly and produces invalid or useless JSON.
|
||||
- **One template per destination:** The same template applies to every log type enabled on that destination. You cannot define different templates per log type on one HTTP destination.
|
||||
- **String escaping:** `{{event}}` and `{{timestamp}}` are JSON-escaped for safe use inside quoted strings.
|
||||
- **Invalid JSON:** Pangolin does not validate templates at save time. If the rendered body is not valid JSON, delivery may still occur but your receiver may reject it. Validate templates with a JSON linter before saving.
|
||||
- **Not available on other destination types:** Body templates apply to HTTP streaming only, not S3 or Datadog destinations.
|
||||
|
||||
## Payload format
|
||||
|
||||
Payload format is separate from the body template. The template defines the shape of **one event**; payload format controls **how many events** are sent per HTTP request.
|
||||
|
||||
| Format | HTTP body | Content-Type |
|
||||
| --- | --- | --- |
|
||||
| **JSON array** (default) | One POST per batch: `[{…}, {…}, …]` | `application/json` |
|
||||
| **NDJSON** | One JSON object per line, no outer array | `application/x-ndjson` |
|
||||
| **One event per request** | Separate POST for each event | `application/json` |
|
||||
|
||||
The template is applied once per event, then results are batched into an array, joined as NDJSON lines, or sent individually, depending on the format you select.
|
||||
|
||||
Choose **NDJSON** for aggregators that expect newline-delimited ingest (Splunk HEC, Elastic/OpenSearch bulk-style HTTP inputs, Loki). Choose **one event per request** when the endpoint cannot accept batches.
|
||||
|
||||
## Log type reference
|
||||
|
||||
The `data` object in each streamed event is the full stored log row. Field sets differ by log type. See the documentation for that log type under **Logs & Analytics** for the complete `data` shape.
|
||||
|
||||
## Integration examples
|
||||
|
||||
### Generic webhook (default shape, JSON array)
|
||||
|
||||
Leave custom body template disabled. Select **JSON array** payload format. Point the destination at your webhook URL with bearer or custom-header auth.
|
||||
|
||||
Each batch POST body looks like:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"event": "action",
|
||||
"timestamp": "2025-06-15T12:34:56.789Z",
|
||||
"data": { "action": "updateUser", "actor": "admin@example.com" }
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Log aggregator (NDJSON, minimal template)
|
||||
|
||||
Enable a custom template and select **NDJSON**:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "{{event}}",
|
||||
"ts": "{{timestamp}}",
|
||||
"payload": {{data}}
|
||||
}
|
||||
```
|
||||
|
||||
Each line in the POST body is one rendered event. Set any vendor-required headers on the **Headers** tab.
|
||||
|
||||
### Vendor schema remapping
|
||||
|
||||
If a tool expects your log row under a specific key, wrap `{{data}}` without quotes:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": "pangolin",
|
||||
"sourcetype": "_json",
|
||||
"time": "{{timestamp}}",
|
||||
"event": {{data}}
|
||||
}
|
||||
```
|
||||
|
||||
Adjust property names to match the vendor; field extraction beyond the three template variables happens on the receiver.
|
||||
|
||||
## Limitations and troubleshooting
|
||||
|
||||
- **Field selection:** Cannot pick individual columns in the template. Use full `{{data}}` or transform after delivery.
|
||||
- **Mixed log types:** Enabling multiple log types on one destination produces heterogeneous `data` in the same batch. Enable one type per destination if your pipeline expects a uniform schema.
|
||||
- **Historical logs:** New destinations do not backfill. Only events recorded after the destination is created are streamed.
|
||||
- **Delivery errors:** Check the destination’s **last error** in the dashboard. Common causes: wrong URL, auth failure, TLS issues, or receiver rejecting malformed JSON.
|
||||
- **Quoting `{{data}}`:** `"payload": "{{data}}"` treats the entire row as a string, which is almost always wrong. Use `"payload": {{data}}`.
|
||||
- **Splunk field extraction:** Pangolin does not emit Splunk-style indexed fields in the template. Parse `data` or use a receiver-side pipeline.
|
||||
@@ -0,0 +1,221 @@
|
||||
---
|
||||
title: "Amazon S3"
|
||||
description: "Archive audit logs to S3 or S3-compatible object storage"
|
||||
---
|
||||
|
||||
S3 destinations upload batches of your organization's audit logs as objects in a bucket you control. Use them for long-term archival, data lakes (Athena, Glue, BigQuery), or S3-compatible stores such as MinIO and Cloudflare R2.
|
||||
|
||||
<Note>
|
||||
Event streaming is only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
## Overview
|
||||
|
||||
An S3 destination writes **one object per batch** via `PutObject`. Each object contains up to 250 events of a **single log type**. There is no custom body template or field mapping; Pangolin serializes every event in a fixed shape and chooses the object key automatically.
|
||||
|
||||
Configure:
|
||||
|
||||
1. **Settings:** Name, credentials, region, bucket, optional prefix and custom endpoint.
|
||||
2. **Format:** File format (JSON array, NDJSON, or CSV) and optional gzip compression.
|
||||
3. **Logs:** Which log types are forwarded.
|
||||
|
||||
## Settings tab
|
||||
|
||||
| Field | Required | Description |
|
||||
| --- | --- | --- |
|
||||
| Name | Yes | Display label for this destination |
|
||||
| AWS Access Key ID | Yes | Static access key for the S3 client |
|
||||
| AWS Secret Access Key | Yes | Secret for the access key |
|
||||
| AWS Region | Yes | S3 client region (UI default: `us-east-1`) |
|
||||
| Bucket name | Yes | Target bucket |
|
||||
| Key prefix | No | Prepended to every object key; trailing slashes are stripped |
|
||||
| Custom endpoint | No | Base URL for MinIO, R2, etc.; leave blank for AWS S3 |
|
||||
|
||||
Pangolin uses static access keys only. There is no IAM role, instance profile, or OIDC picker in the UI.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/streaming-s3-settings.png" alt="S3 destination settings with credentials, region, and bucket" />
|
||||
</Frame>
|
||||
|
||||
Uploads time out after 60 seconds per object.
|
||||
|
||||
## Format tab
|
||||
|
||||
**Gzip compression** (optional): When enabled, the object body is gzip-compressed before upload, `Content-Encoding: gzip` is set, and the object key gets a `.gz` suffix (for example `….json.gz`). Decompress before parsing unless your tool handles gzip automatically.
|
||||
|
||||
**File format:**
|
||||
|
||||
| Format | Description |
|
||||
| --- | --- |
|
||||
| **JSON array** (default) | One array per object: `[{…}, {…}, …]` |
|
||||
| **NDJSON** | One JSON object per line, no outer array |
|
||||
| **CSV** | RFC-4180 CSV with a header row; see [CSV format](#csv-format) |
|
||||
|
||||
<Frame>
|
||||
<img src="/images/streaming-s3-format.png" alt="Format tab with file format and gzip options" />
|
||||
</Frame>
|
||||
|
||||
## Logs tab
|
||||
|
||||
Choose which log categories are uploaded. Each enabled type is written to its own key prefix (`request/`, `action/`, etc.). Only log types enabled for your organization can be streamed.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/streaming-s3-logs.png" alt="Logs tab for selecting streamed log types" />
|
||||
</Frame>
|
||||
|
||||
## Object key layout
|
||||
|
||||
Every upload gets a unique key:
|
||||
|
||||
```
|
||||
{prefix}/{logType}/{YYYY}/{MM}/{DD}/{HH-mm-ss-uuid}.{ext}[.gz]
|
||||
```
|
||||
|
||||
| Segment | Meaning |
|
||||
| --- | --- |
|
||||
| `prefix` | Your optional key prefix; omitted when empty |
|
||||
| `logType` | `request`, `action`, `access`, or `connection` |
|
||||
| `YYYY/MM/DD` | **Upload time (UTC)**, not the event timestamp |
|
||||
| `HH-mm-ss-uuid` | Upload time plus a UUID so keys never collide |
|
||||
| `ext` | `json` (JSON array), `ndjson`, or `csv` |
|
||||
| `.gz` | Present when gzip is enabled |
|
||||
|
||||
**Without prefix:**
|
||||
|
||||
```
|
||||
request/2026/06/04/14-30-45-a1b2c3d4-e5f6-7890-abcd-ef1234567890.json
|
||||
```
|
||||
|
||||
**With prefix `pangolin/audit` and gzip:**
|
||||
|
||||
```
|
||||
pangolin/audit/action/2026/06/04/14-30-45-a1b2c3d4-e5f6-7890-abcd-ef1234567890.json.gz
|
||||
```
|
||||
|
||||
Enabling multiple log types on one destination produces **separate object streams** under different `logType/` segments. A single object never mixes log types.
|
||||
|
||||
## Event record shape
|
||||
|
||||
Each event in JSON and NDJSON objects uses this fixed structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "request",
|
||||
"timestamp": "2026-06-04T12:00:00.000Z",
|
||||
"data": {
|
||||
"timestamp": 1717492800,
|
||||
"action": true,
|
||||
"method": "GET",
|
||||
"path": "/api/health"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Meaning |
|
||||
| --- | --- |
|
||||
| `event` | Log type: `request`, `access`, `action`, or `connection` |
|
||||
| `timestamp` | Event time as ISO-8601 UTC (connection logs use session start) |
|
||||
| `data` | The **complete stored log row** for that record, not a curated subset |
|
||||
|
||||
<Warning>
|
||||
Some columns are stored as JSON strings in the database (`headers`, `query`, and `metadata` on request logs, for example). In `data`, they appear as **string values**, not nested JSON objects. Parse them in your pipeline if you need structured fields.
|
||||
</Warning>
|
||||
|
||||
## File formats
|
||||
|
||||
### JSON array (default)
|
||||
|
||||
- One S3 object per batch; body is `[{…}, {…}, …]`.
|
||||
- Up to 250 events per object.
|
||||
- `Content-Type: application/json`.
|
||||
|
||||
### NDJSON
|
||||
|
||||
- One S3 object per batch; body is one JSON record per line with no outer array.
|
||||
- Good for Athena, BigQuery load jobs, Spark, and similar line-oriented pipelines.
|
||||
- `Content-Type: application/x-ndjson`.
|
||||
|
||||
### CSV format
|
||||
|
||||
- Header row: `event`, `timestamp`, then **all field names** found in `data` across that batch (union of keys, in insertion order).
|
||||
- Each data row flattens `event`, `timestamp`, and spreads `data` fields into columns. There is **no** nested `data` column.
|
||||
- Missing fields in a given row leave an empty cell.
|
||||
- Object or array values in `data` are written as `JSON.stringify` strings inside the cell.
|
||||
- `Content-Type: text/csv; charset=utf-8`.
|
||||
|
||||
The column set can grow as new fields appear in later batches. Order is not guaranteed to stay identical across all objects over time.
|
||||
|
||||
## Batching and throughput
|
||||
|
||||
- Objects are written **per batch** (up to ~250 events), not one object per log line.
|
||||
- Pangolin polls for new logs on a regular interval and may write multiple objects during catch-up after a pause.
|
||||
- **No backfill:** New destinations start from the current log cursor. Historical logs already in Pangolin are not uploaded.
|
||||
- **Extended outage:** If the destination is unreachable for about 24 hours, the backlog may be discarded and streaming resumes from the present cursor (same behavior as [HTTP streaming](/manage/analytics/streaming/http)).
|
||||
|
||||
## Gzip
|
||||
|
||||
When gzip is enabled:
|
||||
|
||||
1. The serialized body is compressed before upload.
|
||||
2. The object key includes `.gz` (for example `….ndjson.gz`).
|
||||
3. S3 stores `Content-Encoding: gzip`.
|
||||
|
||||
Consumers must decompress before parsing unless the tool auto-detects gzip (many Athena and Spark setups do when `Content-Encoding` is set). NDJSON plus gzip is a common choice for cost-sensitive archival.
|
||||
|
||||
## S3-compatible storage
|
||||
|
||||
Set **Custom endpoint** to your vendor's S3 API URL and provide access key credentials per that vendor's documentation.
|
||||
|
||||
| Store | Notes |
|
||||
| --- | --- |
|
||||
| **AWS S3** | Leave custom endpoint blank; use a bucket in the configured region |
|
||||
| **MinIO** | Set endpoint to your MinIO server URL; use MinIO access keys |
|
||||
| **Cloudflare R2** | Set endpoint to your R2 S3 API URL; use R2 access keys |
|
||||
|
||||
Pangolin does not expose path-style vs virtual-hosted addressing, ACLs, SSE-KMS, storage class, or multipart tuning. Configure those in the vendor console or bucket policy.
|
||||
|
||||
## IAM and bucket policy
|
||||
|
||||
Grant the access key permission to write under your prefix. A minimal AWS example:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": ["s3:PutObject"],
|
||||
"Resource": "arn:aws:s3:::your-bucket/pangolin/audit/*"
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": ["s3:ListBucket"],
|
||||
"Resource": "arn:aws:s3:::your-bucket",
|
||||
"Condition": {
|
||||
"StringLike": { "s3:prefix": ["pangolin/audit/*"] }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Adjust bucket name and prefix to match your configuration. `ListBucket` is optional but useful when debugging missing objects.
|
||||
|
||||
Block public access, encryption at rest, lifecycle rules, and object tags are configured in AWS or your vendor console, not in Pangolin.
|
||||
|
||||
## Log type reference
|
||||
|
||||
The `data` object in each streamed event is the full stored log row. Field sets differ by log type. See the documentation for that log type under **Logs & Analytics** for the complete `data` shape.
|
||||
|
||||
## Limitations and troubleshooting
|
||||
|
||||
- **No custom JSON shape:** Fixed event record only. Use an HTTP destination if you need body templates or field remapping.
|
||||
- **No per-event objects:** Always batched (up to ~250 events per object).
|
||||
- **No mixed log types in one object:** Each upload contains a single log type.
|
||||
- **Upload-time partitioning:** Key date folders use upload time (UTC), not the event's `timestamp`.
|
||||
- **CSV columns:** Automatic from batch contents; not user-selectable; column set may change over time.
|
||||
- **Static credentials only:** Rotate keys by updating the destination; credentials are stored encrypted server-side.
|
||||
- **Historical logs:** New destinations do not backfill.
|
||||
- **Delivery errors:** Check the destination's **last error** in the dashboard. Common causes: `AccessDenied`, wrong bucket or region, bad endpoint URL, TLS issues, or expired credentials.
|
||||
- **Missing objects:** Confirm prefix, lifecycle rules, and that the log type is enabled on the **Logs** tab.
|
||||
- **Athena/Glue parse errors:** Verify format (JSON array vs NDJSON), gzip handling, and that the crawler/table schema matches flattened CSV columns if using CSV.
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: "ASN Blocking"
|
||||
description: "Configure ASN blocking to restrict access based on Autonomous System Numbers"
|
||||
---
|
||||
<Note>
|
||||
ASN blocking is available in Pangolin Community Edition. It depends on Pangolin's ASN lookup database. Follow [Enable ASN Lookup](/self-host/advanced/enable-asn-lookup) before creating ASN rules.
|
||||
</Note>
|
||||
|
||||
## Benefits of ASN Blocking
|
||||
|
||||
ASN blocking provides several important security and operational advantages:
|
||||
|
||||
### Security Benefits
|
||||
- **Block Malicious Networks**: Prevent access from autonomous systems known for hosting malicious activity, botnets, or spam operations
|
||||
- **Control Cloud Provider Access**: Restrict or allow access from specific cloud providers (AWS, Azure, GCP, etc.)
|
||||
- **Block VPN/Proxy Services**: Deny access from commercial VPN and proxy service providers to prevent anonymous access
|
||||
- **Datacenter Filtering**: Block traffic from datacenter networks while allowing residential ISPs
|
||||
- **Compliance Requirements**: Meet regulatory requirements that restrict access from certain network types or providers
|
||||
|
||||
## Implementing ASN Blocking with Bypass Rules
|
||||
|
||||
ASN blocking in Pangolin is implemented using [bypass rules](/manage/access-control/rules) with ASN-based matching. You can create rules that either allow or deny access based on the visitor's Autonomous System Number. To apply the same ASN rules to multiple public resources, define them in a [resource policy](/manage/resources/public/resource-policies) and attach that policy to each resource.
|
||||
|
||||
<Frame caption="Screenshot of ASN rules from the Pangolin Dashboard.">
|
||||
<img src="/images/asn_rules.png" alt="Pangolin Dashboard"/>
|
||||
</Frame>
|
||||
|
||||
### Setting Up ASN Blocking Rules
|
||||
|
||||
1. Navigate to your target resource and select the **Rules** tab
|
||||
2. Create a new rule and select **ASN** as the match type
|
||||
3. Choose an ASN from the dropdown of common providers, or manually enter a specific ASN number
|
||||
4. Choose your rule action:
|
||||
- **Allow**: Bypass authentication for users from specific ASNs
|
||||
- **Deny**: Block all access from specific ASNs
|
||||
- **Pass to Auth**: Let users from specific ASNs proceed to authentication
|
||||
|
||||
### Common ASNs
|
||||
|
||||
The dropdown includes many commonly-used ASNs such as:
|
||||
|
||||
- **Cloud Providers**: Amazon (AS16509), Google Cloud (AS15169), Microsoft Azure (AS8075), DigitalOcean (AS14061)
|
||||
- **Major ISPs**: Comcast (AS7922), AT&T (AS7018), Verizon (AS701), Deutsche Telekom (AS3320)
|
||||
- **VPN/Proxy Services**: NordVPN (various), ExpressVPN (various), Mullvad (AS42831)
|
||||
- **CDN Providers**: Cloudflare (AS13335), Fastly (AS54113), Akamai (various)
|
||||
|
||||
If the ASN you need isn't in the dropdown, you can manually enter the ASN number (e.g., AS12345 or just 12345).
|
||||
|
||||
### Common ASN Blocking Patterns
|
||||
|
||||
#### Block VPN and Proxy Services
|
||||
Create deny rules for known VPN and proxy ASNs to prevent anonymous access:
|
||||
|
||||
1. Create **Deny** rules for each VPN/proxy provider ASN
|
||||
2. Select ASNs from the dropdown or enter them manually
|
||||
3. Set appropriate priorities
|
||||
|
||||
#### Block Datacenter Traffic
|
||||
Block access from datacenter and hosting provider ASNs while allowing residential users:
|
||||
|
||||
1. Create **Deny** rules for major cloud and hosting provider ASNs
|
||||
2. Include providers like AWS, GCP, Azure, DigitalOcean, etc.
|
||||
3. This helps ensure only real users from residential ISPs can access your resources
|
||||
|
||||
#### Allow Only Specific Networks
|
||||
Create a default deny rule and explicitly allow only approved ASNs:
|
||||
|
||||
1. Create a **Deny** rule matching all traffic with priority 100
|
||||
2. Create **Allow** rules for specific approved ASNs with higher priority (e.g., 10, 20, 30)
|
||||
|
||||
#### Regional ISP Control
|
||||
Allow access only from specific country ISPs while blocking others:
|
||||
|
||||
1. **Combine with Country Rules**: Use ASN rules to specify which ISPs are allowed
|
||||
2. Create **Allow** rules for major residential ISPs in your target countries
|
||||
3. Block datacenter and VPN ASNs that might circumvent country restrictions
|
||||
|
||||
### Best Practices
|
||||
|
||||
<Warning>
|
||||
ASN blocking affects all users from that network. Be careful when blocking large ISPs or cloud providers, as legitimate users or your own infrastructure may be affected.
|
||||
</Warning>
|
||||
|
||||
|
||||
### Finding ASN Numbers
|
||||
|
||||
If you need to find the ASN for a specific network or provider:
|
||||
|
||||
1. Use online tools like [bgp.he.net](https://bgp.he.net/) or [ipinfo.io](https://ipinfo.io/)
|
||||
2. Search by company name, IP address, or ASN number
|
||||
3. Enter the ASN in the rule configuration (with or without the "AS" prefix)
|
||||
|
||||
### Rule Priority Example
|
||||
|
||||
```
|
||||
Priority 1: Allow - ASN: AS7922 (Comcast)
|
||||
Priority 2: Allow - ASN: AS7018 (AT&T)
|
||||
Priority 3: Deny - ASN: AS13335 (Cloudflare - VPN)
|
||||
Priority 4: Deny - ASN: AS16509 (Amazon - Datacenter)
|
||||
```
|
||||
|
||||
This configuration allows access from residential users on Comcast and AT&T while blocking Cloudflare's VPN service and Amazon datacenters.
|
||||
|
||||
### Advanced Patterns
|
||||
|
||||
#### Block Bot Networks
|
||||
Identify and block ASNs associated with automated bot traffic:
|
||||
|
||||
1. Monitor your access logs for suspicious ASNs
|
||||
2. Create **Deny** rules for ASNs showing bot-like behavior
|
||||
3. Regularly review and update your blocklist
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,304 @@
|
||||
---
|
||||
title: "Branding"
|
||||
icon: "brush"
|
||||
description: "Learn how to customize the look your Pangolin dashboard and login pages with custom branding"
|
||||
---
|
||||
<Note>
|
||||
Branding is only available in [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
Pangolin allows you to customize the appearance of your dashboard with your own branding, including colors, logos, and custom text for authentication pages. Branding is configured through the `privateConfig.yml` file.
|
||||
|
||||
## Organization Branding
|
||||
|
||||
In the settings of each organization, there is an Authentication Page branding settings section. These settings enable you to brand the resource authentication page and organization authentication page for that specific organization. These settings will override anything set in the configuration file.
|
||||
|
||||
## Setting up Branding
|
||||
|
||||
To get started, create a `privateConfig.yml` file next to your existing `config.yml` file:
|
||||
|
||||
```yaml title="privateConfig.yml"
|
||||
branding:
|
||||
app_name: "MyApp"
|
||||
|
||||
logo:
|
||||
light_path: "/branding/logo-light.svg"
|
||||
dark_path: "/branding/logo-dark.svg"
|
||||
|
||||
colors:
|
||||
light:
|
||||
primary: "hsl(212, 65%, 35%)"
|
||||
ring: "hsl(212, 65%, 35%)"
|
||||
primary-foreground: "hsl(0 0% 98%)"
|
||||
dark:
|
||||
primary: "hsl(212, 65%, 45%)"
|
||||
ring: "hsl(212, 65%, 35%)"
|
||||
primary-foreground: "hsl(0 0% 98%)"
|
||||
```
|
||||
|
||||
## Mounting Branding Assets
|
||||
|
||||
For image assets like logos and backgrounds, you need to mount a volume to the Pangolin container to make them accessible to the application:
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
- ./branding:/app/public/branding
|
||||
```
|
||||
|
||||
Your folder structure should look like this:
|
||||
|
||||
```
|
||||
.
|
||||
├── config/
|
||||
│ ├── config.yml
|
||||
│ └── privateConfig.yml
|
||||
└── branding/
|
||||
├── logo-light.svg
|
||||
├── logo-dark.svg
|
||||
└── favicon.ico
|
||||
```
|
||||
|
||||
## Reference
|
||||
|
||||
All branding configuration options are optional. Only specify the sections you want to customize.
|
||||
|
||||
### Application Name
|
||||
|
||||
<ResponseField name="app_name" type="string">
|
||||
The name of your application that appears in various places throughout the UI.
|
||||
|
||||
**Example**: `"MyApp"`
|
||||
</ResponseField>
|
||||
|
||||
### Favicon
|
||||
|
||||
To customize the favicon, mount your favicon to `/app/public/favicon.ico` in the container.
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
- ./branding/favicon.ico:/app/public/favicon.ico
|
||||
```
|
||||
|
||||
### Background Image
|
||||
|
||||
<ResponseField name="background_image_path" type="string">
|
||||
Path to a custom background image used on authentication pages.
|
||||
|
||||
**Example**: `"/branding/backgrounds/bg.png"`
|
||||
|
||||
<Note>
|
||||
Requires mounting a volume to `/app/public`.
|
||||
</Note>
|
||||
</ResponseField>
|
||||
|
||||
### Colors
|
||||
|
||||
<ResponseField name="colors" type="object">
|
||||
Custom color scheme that overrides the default Tailwind classes for shadcn components.
|
||||
|
||||
Colors can be provided in OKLCH or HSL format for example and apply to both light and dark modes.
|
||||
|
||||
**Color Reference**: Available color properties include `background`, `foreground`, `card`, `card-foreground`, `popover`, `popover-foreground`, `primary`, `primary-foreground`, `secondary`, `secondary-foreground`, `muted`, `muted-foreground`, `accent`, `accent-foreground`, `destructive`, `destructive-foreground`, `border`, `input`, `ring`, `radius`, and `chart-1` through `chart-5`.
|
||||
|
||||
**Example**:
|
||||
```yaml
|
||||
colors:
|
||||
light:
|
||||
primary: "hsl(212, 65%, 35%)"
|
||||
background: "hsl(212, 65%, 35%)"
|
||||
foreground: "hsl(0 0% 98%)"
|
||||
dark:
|
||||
primary: "hsl(212, 65%, 45%)"
|
||||
background: "hsl(212, 65%, 35%)"
|
||||
foreground: "hsl(0 0% 98%)"
|
||||
```
|
||||
|
||||
<Tip>
|
||||
For a complete list of Tailwind color classes used by shadcn components, refer to the [shadcn/ui documentation](https://ui.shadcn.com/docs/theming).
|
||||
</Tip>
|
||||
</ResponseField>
|
||||
|
||||
### Logo
|
||||
|
||||
<ResponseField name="logo" type="object">
|
||||
Logo configuration for different parts of the application.
|
||||
|
||||
<Expandable title="Logo">
|
||||
<ResponseField name="light_path" type="string">
|
||||
Path to the logo image used in light mode.
|
||||
|
||||
**Example**: `"/branding/logo-light.svg"`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="dark_path" type="string">
|
||||
Path to the logo image used in dark mode.
|
||||
|
||||
**Example**: `"/branding/logo-dark.svg"`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="auth_page" type="object">
|
||||
Logo dimensions for authentication pages.
|
||||
|
||||
<Expandable title="Auth Page">
|
||||
<ResponseField name="width" type="integer">
|
||||
Logo width in pixels.
|
||||
|
||||
**Example**: `200`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="height" type="integer">
|
||||
Logo height in pixels.
|
||||
|
||||
**Example**: `80`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="navbar" type="object">
|
||||
Logo dimensions for the navigation bar.
|
||||
|
||||
<Expandable title="Navbar">
|
||||
<ResponseField name="width" type="integer">
|
||||
Logo width in pixels.
|
||||
|
||||
**Example**: `150`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="height" type="integer">
|
||||
Logo height in pixels.
|
||||
|
||||
**Example**: `60`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Footer
|
||||
|
||||
<ResponseField name="footer" type="array of objects">
|
||||
Custom footer links displayed at the bottom of the page.
|
||||
|
||||
**Example**:
|
||||
```yaml
|
||||
footer:
|
||||
- text: "Privacy Policy"
|
||||
href: "https://example.com/privacy"
|
||||
- text: "Terms of Service"
|
||||
href: "https://example.com/terms"
|
||||
```
|
||||
|
||||
<ResponseField name="text" type="string">
|
||||
The display text for the footer link.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="href" type="string">
|
||||
The URL or path for the footer link.
|
||||
</ResponseField>
|
||||
</ResponseField>
|
||||
|
||||
### Hide Auth Page Footer
|
||||
|
||||
<ResponseField name="hide_auth_layout_footer" type="boolean">
|
||||
Hide the horizontal footer text that appears in the bottom layout of all authentication pages.
|
||||
|
||||
**Default**: `false`
|
||||
</ResponseField>
|
||||
|
||||
### Login Page
|
||||
|
||||
<ResponseField name="login_page" type="object">
|
||||
Custom text for the login page.
|
||||
|
||||
<Expandable title="Login Page">
|
||||
<ResponseField name="subtitle_text" type="string">
|
||||
The subtitle text displayed below the title.
|
||||
|
||||
**Example**: `"Sign in to continue to your account"`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Signup Page
|
||||
|
||||
<ResponseField name="signup_page" type="object">
|
||||
Custom text for the signup page.
|
||||
|
||||
<Expandable title="Signup Page">
|
||||
<ResponseField name="subtitle_text" type="string">
|
||||
The subtitle text displayed below the title.
|
||||
|
||||
**Example**: `"Join us and get started today"`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Resource Auth Page
|
||||
|
||||
<ResponseField name="resource_auth_page" type="object">
|
||||
Configuration for the resource authentication page shown to users when accessing protected resources.
|
||||
|
||||
<Expandable title="Resource Auth Page">
|
||||
<ResponseField name="show_logo" type="boolean">
|
||||
Whether to display the logo on the resource auth page.
|
||||
|
||||
**Default**: `true`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="hide_powered_by" type="boolean">
|
||||
Whether to hide the "Powered by Pangolin" text on the resource auth page.
|
||||
|
||||
**Default**: `false`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="title_text" type="string">
|
||||
The main title text displayed on the resource auth page.
|
||||
|
||||
**Example**: `"Access Restricted"`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="subtitle_text" type="string">
|
||||
The subtitle text displayed below the title.
|
||||
|
||||
**Example**: `"Please sign in to view this resource"`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Emails
|
||||
|
||||
<ResponseField name="emails" type="object">
|
||||
Email customization settings.
|
||||
|
||||
<Expandable title="Emails">
|
||||
<ResponseField name="signature" type="string">
|
||||
Custom signature appended to sent emails.
|
||||
|
||||
**Example**: `"Best regards,\nThe MyApp Team"`
|
||||
|
||||
<Note>
|
||||
Use `\n` for line breaks in the signature.
|
||||
</Note>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="colors" type="object">
|
||||
Email color scheme.
|
||||
|
||||
<Expandable title="Colors">
|
||||
<ResponseField name="primary" type="string">
|
||||
Primary color for email elements.
|
||||
|
||||
**Example**: `"oklch(0.6717 0.1946 41.93)"`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
## Applying Changes
|
||||
|
||||
<Warning>
|
||||
After creating or modifying `privateConfig.yml`, you must restart the Pangolin container for changes to take effect.
|
||||
</Warning>
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: "Archiving & Blocking"
|
||||
description: "Manage clients and block unwanted connections"
|
||||
---
|
||||
## Blocking Devices
|
||||
|
||||
To block a device that has been compromised or lost:
|
||||
|
||||
1. Navigate to the device in your client list
|
||||
2. Click the Action Menu (three dots)
|
||||
3. Select **Block**
|
||||
|
||||
When you block a device, it is immediately moved to a restricted list and loses access to all resources or the ability to connect.
|
||||
|
||||
## Archiving Devices
|
||||
|
||||
Devices cannot be permanently deleted from Pangolin. Instead, you can archive devices to remove them from your active client list.
|
||||
|
||||
To archive a device:
|
||||
|
||||
1. Navigate to the device in your client list
|
||||
2. Click the Action Menu (three dots)
|
||||
3. Select **Archive**
|
||||
|
||||
### Why Archive Instead of Delete?
|
||||
|
||||
Pangolin maintains a permanent audit trail of all devices that have accessed your resources. This ensures:
|
||||
|
||||
- Block rules remain effective even for archived devices
|
||||
- Security logs and access history are preserved
|
||||
|
||||
Archiving keeps your interface clean by hiding inactive or duplicate devices while maintaining the integrity of your security records.
|
||||
|
||||
## Filtering
|
||||
|
||||
You can filter your client list to show only active, blocked, or archived devices.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/device_list_filter.png" alt="Client List Filters" style={{width: "400px", height: "auto"}} />
|
||||
</Frame>
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: "Client Logs"
|
||||
description: "View Pangolin client logs on Windows, Mac, Linux, Android, and iOS"
|
||||
---
|
||||
How you access client logs depends on the platform. Use the section for your client type below.
|
||||
|
||||
## Windows
|
||||
|
||||
You can view logs in the Pangolin client GUI under **Preferences > Logs**.
|
||||
|
||||
You can also open the log file directly. The default location is:
|
||||
|
||||
```
|
||||
C:\ProgramData\pangolin\logs\pangolin.log
|
||||
```
|
||||
|
||||
To tail the log in PowerShell:
|
||||
|
||||
```powershell
|
||||
Get-Content "C:\ProgramData\pangolin\logs\pangolin.log" -Tail 0 -Wait
|
||||
```
|
||||
|
||||
## Mac
|
||||
|
||||
Mac uses the Apple system logger. To stream Pangolin logs:
|
||||
|
||||
```bash
|
||||
log stream --predicate 'subsystem == "net.pangolin.Pangolin" OR subsystem == "net.pangolin.Pangolin.PacketTunnel"' --level debug --style compact
|
||||
```
|
||||
|
||||
## CLI / Linux
|
||||
|
||||
The default log file location is:
|
||||
|
||||
```
|
||||
~/.config/pangolin/logs/client.log
|
||||
```
|
||||
|
||||
If you run `pangolin up` in detached mode, you can tail logs with:
|
||||
|
||||
```bash
|
||||
pangolin logs client -f -n 10
|
||||
```
|
||||
|
||||
This example follows new log lines and shows the last 10 rows.
|
||||
|
||||
Alternatively, run in attached mode to see logs in the current terminal session:
|
||||
|
||||
```bash
|
||||
sudo pangolin up --attach
|
||||
```
|
||||
|
||||
## Android
|
||||
|
||||
View logs within the app under **Preferences > Logs**.
|
||||
|
||||
## iOS
|
||||
|
||||
Because of how iOS handles system logging, attach the iPhone to a Mac and use Console.app to view logs.
|
||||
|
||||
In Console.app, add a **Subsystem** filter for:
|
||||
|
||||
```
|
||||
net.pangolin.Pangolin
|
||||
```
|
||||
@@ -0,0 +1,543 @@
|
||||
---
|
||||
title: "Configure Clients"
|
||||
description: "Configure Olm for connecting to Pangolin clients"
|
||||
---
|
||||
## GUI Clients (Mac, Windows, Android, iOS/iPadOS)
|
||||
|
||||
Each respective client has a preferences window with all currently available configuration parameters. In your desktop client, click the menu bar or system tray icon, select "More" in the menu, and click "Preferences". In the mobile apps, navigate to the "Settings" screen.
|
||||
|
||||
To troubleshoot connection or configuration issues, see [Client Logs](/manage/clients/client-logs) for how to view logs on each platform.
|
||||
|
||||
## Preferences
|
||||
|
||||
The following preferences control how your client handles DNS resolution and network routing. Understanding these settings helps you configure Pangolin to work best with your network setup.
|
||||
|
||||
#### Enable Aliases (Override DNS)
|
||||
|
||||
When enabled, the client uses custom DNS servers to resolve internal resources and aliases. This overrides your system's default DNS settings. Queries that cannot be resolved as a Pangolin resource will be forwarded to your configured Upstream DNS Server.
|
||||
|
||||
**When to use it**: This is required if you use aliases on resources in Pangolin. Aliases are friendly domain names assigned to private resources. Pangolin resolves these alias addresses over a private DNS server running in your client.
|
||||
|
||||
**How it works**: The client loops back to itself to resolve the alias. This is why you may see your DNS server as an unfamiliar address (often like `100.90.128.x`) when this is enabled. When a request doesn't resolve to a Pangolin resource and is bound for another website (like `google.com`), it falls back to your configured upstream DNS server.
|
||||
|
||||
#### DNS Over Tunnel
|
||||
|
||||
When enabled, DNS queries are routed through the tunnel for remote resolution. To ensure queries are tunneled correctly, you must define the DNS server as a Pangolin resource and enter its address as an Upstream DNS Server.
|
||||
|
||||
**When to use it**: Tunnel DNS is used when you want to send all DNS queries over the tunnel to a private resource made available in Pangolin. For example, if you host a DNS server like Pi-hole, you could define a private resource for Pi-hole on your remote network. Then in the Pangolin client, you would enable Tunnel DNS and set the host of the Pi-hole private resource as the tunnel DNS server.
|
||||
|
||||
**How it works**: When a request needs to be resolved, Pangolin sends it over the tunnel to the site of the private resource with your DNS server. You must enable DNS Over Tunnel and also set the upstream DNS server to your private DNS server.
|
||||
|
||||
This requires aliases "override DNS" to be enabled as well. This is because the client must take control of your DNS settings to route queries through the tunnel to your private DNS server.
|
||||
|
||||
<Warning>
|
||||
You cannot use an alias name for your DNS server. It must be the IP address of the resource. This is because it's pointing to the DNS server, so the DNS server can't resolve itself.
|
||||
</Warning>
|
||||
|
||||
#### Primary Upstream DNS
|
||||
|
||||
This is the DNS server used to resolve queries that are not bound to a Pangolin alias when Override DNS or DNS Over Tunnel is enabled.
|
||||
|
||||
When left blank, **System DNS** is used. This pulls the existing configured system DNS settings and applies them to the tunnel. If Tunnel DNS is enabled and System DNS is used, requests will likely fail if the DNS server is not accessible over the tunnel.
|
||||
|
||||
#### Secondary Upstream DNS
|
||||
|
||||
This is a fallback DNS server used to resolve queries that are not bound to a Pangolin alias when the primary server is unavailable. Ordering and priority of the server is not guaranteed, but it provides redundancy for DNS resolution. When left blank, **System DNS** is used, same as Primary Upstream DNS.
|
||||
|
||||
#### Match Domains
|
||||
|
||||
By default, when match domains are not set, all DNS queries are sent to the configured upstream DNS server. Match domains let you whitelist which domains should be sent to the upstream DNS server. When match domains are set, only matching queries go to upstream DNS; all other requests use the system's DNS servers.
|
||||
|
||||
**When to use it**: When you have a private or corporate DNS server for specific domains (for example, `*.proxy.internal` or `corp.example.com`) and want everything else resolved by the system DNS as usual.
|
||||
|
||||
**How it works**: With no match domains configured, every query is forwarded to your Upstream DNS Server. With match domains set, only queries that match the list are forwarded upstream; the rest use the system's default DNS servers.
|
||||
|
||||
#### MTU
|
||||
|
||||
You can set the maximum transmission unit (MTU) for the client’s internal WireGuard interface. This value is client-wide: every site the client connects to must use the same MTU on the site side, or you can see fragmentation, failed handshakes, or unstable tunnels. See the **mtu** option on [Configure Sites](/manage/sites/configure-site) and set the same value on each of those sites.
|
||||
|
||||
<Warning>
|
||||
Changing MTU is advanced and not recommended for most users. Only change it when you have a specific, well-understood reason (for example, a constrained network path or a requirement from your infrastructure team). If you do change it, you must update every connected site to the identical value.
|
||||
</Warning>
|
||||
|
||||
## Windows Client (Advanced)
|
||||
|
||||
On Windows, the Pangolin GUI reads configuration from two `pangolin.json` files:
|
||||
|
||||
- User config: `%LOCALAPPDATA%\Pangolin\pangolin.json` (for example, `C:\Users\USER\AppData\Local\Pangolin\pangolin.json`)
|
||||
- Global config: `%ProgramData%\Pangolin\pangolin.json`
|
||||
|
||||
Most keys in the `Config` object below can be set in either file. If the same key exists in both places, the user config value overrides the global value. This lets administrators define global defaults while still allowing per-user overrides when needed. Keys marked **Global only** must be set in `%ProgramData%\Pangolin\pangolin.json`; restart the Pangolin manager/UI after changing them.
|
||||
|
||||
<ResponseField name="Config" type="object">
|
||||
JSON configuration for the Windows Pangolin client stored in `pangolin.json`.
|
||||
|
||||
<Expandable title="Config">
|
||||
<ResponseField name="dnsOverride" type="boolean">
|
||||
When true, matches the **Enable Aliases (Override DNS)** preference and lets the client take over DNS resolution for Pangolin resources.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="dnsTunnel" type="boolean">
|
||||
When true, matches the **DNS Over Tunnel** preference and sends DNS queries through the Pangolin tunnel.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="primaryDNS" type="string">
|
||||
Primary upstream DNS server used when override/tunnel DNS is enabled.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="secondaryDNS" type="string">
|
||||
Optional secondary upstream DNS server used as a fallback when the primary is unavailable.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="dnsMatchDomains" type="array of strings">
|
||||
Optional whitelist of domains sent to the configured upstream DNS server. When unset, all queries go to upstream DNS. When set, only matching queries use your Upstream DNS Server; all other requests use the system's DNS servers. Supports wildcards such as `*.proxy.internal`.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="defaultServerURL" type="string">
|
||||
When set, skips the deployment option screen during login; all login flows start directly with this server URL.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="authPath" type="string">
|
||||
Optional path appended to the server URL for authentication, for example `/auth/org/my-org` to always send users to a specific organization or branded login page. Most deployments should leave this unset.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="userSettingsDisabled" type="boolean">
|
||||
When true, hides and disables the settings form in the GUI so users cannot change these values themselves.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="openStatusTabOnConnect" type="boolean">
|
||||
When true, opens the Status tab immediately after clicking Connect so users can watch connection feedback while the tunnel is starting.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="mtu" type="integer">
|
||||
MTU for the internal WireGuard interface. Changing this is advanced and not recommended unless you have a clear reason; if you set a non-default value, configure the same MTU on every site this client connects to—see [Configure Sites](/manage/sites/configure-site).
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="preferLocalRoutes" type="boolean">
|
||||
When true, prioritizes local routes over remote ones by adding an arbitrary metric to the WireGuard routes. This is useful when you want to ensure that local network traffic (for example, to a printer or NAS) is not routed through the Pangolin tunnel.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="autoUpdateChecksEnabled" type="boolean">
|
||||
**Global only.** When true, periodically check for updates in the background. When false, automatic checks are off; users can still use **Check for Updates** unless that button is also disabled. If omitted, the default is `true`. Enabling checks surfaces the update UI when a new version exists (tray “Pangolin Update Available” and the update prompt). Intended for org admins / MDM so config is the source of truth.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="updateCheckIntervalSeconds" type="integer">
|
||||
**Global only.** How often automatic checks run, in seconds. Values below `3600` (1 hour) are clamped to `3600`. Only matters when `autoUpdateChecksEnabled` is true. If omitted, the default is `86400` (24 hours). The client applies a small amount of jitter around this interval.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="checkForUpdatesButtonEnabled" type="boolean">
|
||||
**Global only.** When true, show **Check for Updates** in the system tray More menu. When false, hide that menu item. If omitted, the default is `true`. Manual checks always perform a live network lookup when the button is used. This is independent of `autoUpdateChecksEnabled`.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="logLevel" type="string">
|
||||
**Global only.** Controls client log verbosity. Supported values include `debug` and `info`. If omitted, the default is `info`.
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
As a system administrator, you can script placing `pangolin.json` in `%ProgramData%\Pangolin\` to set global defaults, and/or in each user's `%LOCALAPPDATA%\Pangolin\` folder for per-user overrides and targeted rollout behavior.
|
||||
|
||||
<Tip>
|
||||
For enterprise customers, contact us if you need a custom MSI installer with baked-in configuration; we can maintain custom installers as an add-on to your enterprise license.
|
||||
</Tip>
|
||||
|
||||
## Mac Client (Advanced)
|
||||
|
||||
On Mac, the Pangolin GUI reads configuration from `~/Library/Application Support/Pangolin/pangolin.json`. Restart Pangolin after editing for changes to apply.
|
||||
|
||||
<ResponseField name="Config" type="object">
|
||||
JSON configuration for the Mac Pangolin client stored in `pangolin.json`.
|
||||
|
||||
<Expandable title="Config">
|
||||
<ResponseField name="dnsOverrideEnabled" type="boolean">
|
||||
When true, matches the **Enable Aliases (Override DNS)** preference and lets the client take over DNS resolution for Pangolin resources.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="dnsTunnelEnabled" type="boolean">
|
||||
When true, matches the **DNS Over Tunnel** preference and sends DNS queries through the Pangolin tunnel.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="primaryDNSServer" type="string">
|
||||
Primary upstream DNS server used when override/tunnel DNS is enabled.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="secondaryDNSServer" type="string">
|
||||
Optional secondary upstream DNS server used as a fallback when the primary is unavailable.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="dnsMatchDomains" type="array of strings">
|
||||
Optional whitelist of domains sent to the configured upstream DNS server. When unset, all queries go to upstream DNS. When set, only matching queries use your Upstream DNS Server; all other requests use the system's DNS servers. Supports wildcards such as `*.proxy.internal`.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="tunnelMTU" type="integer">
|
||||
MTU for the internal WireGuard interface. Changing this is advanced and not recommended unless you have a clear reason; if you set a non-default value, configure the same MTU on every site this client connects to. See [Configure Sites](/manage/sites/configure-site).
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="autoUpdateChecksEnabled" type="boolean">
|
||||
When true, periodically check for updates in the background. When false, automatic checks are off; users can still use **Check for Updates**. If omitted, the client default applies (may prompt the user on second launch). Enabling checks without `autoDownloadUpdatesEnabled` still surfaces the update UI when a new version exists. Intended for admin / MDM provisioning so config stays the source of truth over user toggles.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="autoDownloadUpdatesEnabled" type="boolean">
|
||||
When true, download updates silently when found and stage install for quit/relaunch. When false, show the normal update dialog instead of silent download. Silent download does not replace the running app mid-session; the update installs on quit (and relaunches), so long-running menu bar sessions may keep a staged update until quit. If omitted, the default is `false`.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="updateCheckIntervalSeconds" type="integer">
|
||||
How often automatic checks run, in seconds. Values below `3600` (1 hour) are clamped to `3600`. Only matters when `autoUpdateChecksEnabled` is true. If omitted, the default is `86400` (24 hours).
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
## Android Battery Optimization
|
||||
|
||||
To ensure Pangolin functions correctly in the background on Android devices, it's recommended to disable battery optimization for the app. This prevents the operating system from restricting its background activities, which could lead to disconnections.
|
||||
|
||||
1. Open the **Settings** app on your Android device.
|
||||
2. Navigate to **Apps & notifications** (or simply **Apps** on some devices).
|
||||
3. Find and select the Pangolin app from the list of installed apps.
|
||||
4. Tap on **App battery usage**.
|
||||
5. Select **Allow background usage** and enable if disabled.
|
||||
6. From the options menu, choose **Unrestricted**.
|
||||
|
||||
<Frame caption="Android Battery Optimization Settings">
|
||||
<img src="/images/android_battery.png" alt="Android Battery Optimization Settings" style={{width: "250px", height: "auto"}} />
|
||||
</Frame>
|
||||
|
||||
## Pangolin CLI
|
||||
|
||||
Refer to the [documentation in the official repository](https://github.com/fosrl/cli/blob/main/docs/pangolin.md) for the available commands, default values, and more.
|
||||
|
||||
## Olm (Advanced, Deprecated)
|
||||
|
||||
<Accordion title="Olm CLI (advanced use only)">
|
||||
|
||||
<Tip>
|
||||
We recommend using the Pangolin CLI for both user and machine clients if you're looking for a CLI interface. Olm is the underlying client for the Pangolin CLI.
|
||||
</Tip>
|
||||
|
||||
Olm is a command-line client for connecting machine clients in Pangolin. You can configure it using command-line flags, environment variables, or a configuration file. Expand the section below to view all available configuration options.
|
||||
|
||||
<Accordion title="CLI Arguments and Options">
|
||||
|
||||
### Flags
|
||||
|
||||
<ResponseField name="id" type="string" required>
|
||||
Olm ID generated by Pangolin to identify the client.
|
||||
|
||||
**Example**: `31frd0uzbjvp721`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="secret" type="string" required>
|
||||
A unique secret used to authenticate the client ID with the websocket.
|
||||
|
||||
**Example**: `h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6`
|
||||
|
||||
<Warning>
|
||||
Keep this secret private and secure. It's used for authentication.
|
||||
</Warning>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="endpoint" type="string" required>
|
||||
The endpoint where the Pangolin server resides for websocket connections.
|
||||
|
||||
**Example**: `https://pangolin.example.com`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="org" type="string">
|
||||
Organization ID to connect to.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="user-token" type="string">
|
||||
User authentication token.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="mtu" type="integer">
|
||||
MTU for the internal WireGuard interface.
|
||||
|
||||
**Default**: `1280`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="dns" type="string">
|
||||
DNS server to use to resolve the endpoint.
|
||||
|
||||
**Default**: `8.8.8.8`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="upstream-dns" type="string">
|
||||
Upstream DNS server(s), comma-separated.
|
||||
|
||||
**Default**: `8.8.8.8:53`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="match-domains-dns" type="string">
|
||||
FQDN wildcard patterns (using `*` and `?` wildcards, comma-separated, e.g. `*.proxy.internal,*.host-0?.autoco.internal`) to check against local records/upstream DNS. Queries for domains that don't match any pattern are sent directly to the host's own system DNS servers instead of being resolved as Pangolin resources.
|
||||
|
||||
**Default**: (empty, matches every domain)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="log-level" type="string">
|
||||
The log level to use for Olm output.
|
||||
|
||||
**Options**: `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`
|
||||
|
||||
**Default**: `INFO`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="ping-interval" type="string">
|
||||
Interval for pinging the server.
|
||||
|
||||
**Default**: `3s`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="ping-timeout" type="string">
|
||||
Timeout for each ping.
|
||||
|
||||
**Default**: `5s`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="interface" type="string">
|
||||
Name of the WireGuard interface.
|
||||
|
||||
**Default**: `olm`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="enable-api" type="boolean">
|
||||
Enable API server for receiving connection requests.
|
||||
|
||||
**Default**: `false`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="http-addr" type="string">
|
||||
HTTP server address (e.g., ':9452'). When unset, the HTTP API is not started and the socket API (see `socket-path`) is used instead.
|
||||
|
||||
**Default**: (not set)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="socket-path" type="string">
|
||||
Unix socket path (or named pipe on Windows).
|
||||
|
||||
**Default**: `/var/run/olm.sock` (Linux/macOS) or `olm` (Windows)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="disable-holepunch" type="boolean">
|
||||
Disable hole punching.
|
||||
|
||||
**Default**: `false`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="override-dns" type="boolean">
|
||||
When enabled, the client uses custom DNS servers to resolve internal resources and aliases. This overrides your system's default DNS settings. Queries that cannot be resolved as a Pangolin resource will be forwarded to your configured Upstream DNS Server.
|
||||
|
||||
**Default**: `true`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="tunnel-dns" type="boolean">
|
||||
When enabled, DNS queries are routed through the tunnel for remote resolution. To ensure queries are tunneled correctly, you must define the DNS server as a Pangolin resource and enter its address as an Upstream DNS Server.
|
||||
|
||||
**Default**: `false`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="match-domains-dns" type="string">
|
||||
Optional comma-separated whitelist of domains sent to the configured upstream DNS server. When unset, all queries go to upstream DNS. When set, only matching queries use your Upstream DNS Server; all other requests use the system's DNS servers. Supports wildcards such as `*.proxy.internal`.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="disable-relay" type="boolean">
|
||||
Disable relay connections.
|
||||
|
||||
**Default**: `false`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="prefer-local-routes" type="boolean">
|
||||
When true, prioritizes local routes over remote ones by adding an arbitrary metric to the WireGuard routes. This is useful when you want to ensure that local network traffic (for example, to a printer or NAS) is not routed through the Pangolin tunnel.
|
||||
|
||||
**Default**: `false`
|
||||
</ResponseField>
|
||||
|
||||
### Environment Variables
|
||||
|
||||
All CLI arguments can be set using environment variables as an alternative to command line flags. Environment variables are particularly useful when running Olm in containerized environments.
|
||||
|
||||
<Note>
|
||||
When both environment variables and CLI arguments are provided, CLI arguments take precedence.
|
||||
</Note>
|
||||
|
||||
<ResponseField name="PANGOLIN_ENDPOINT" type="string">
|
||||
Endpoint of your Pangolin server (equivalent to `--endpoint`)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="OLM_ID" type="string">
|
||||
Olm ID generated by Pangolin (equivalent to `--id`)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="OLM_SECRET" type="string">
|
||||
Olm secret for authentication (equivalent to `--secret`)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="ORG" type="string">
|
||||
Organization ID to connect to (equivalent to `--org`)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="USER_TOKEN" type="string">
|
||||
User authentication token (equivalent to `--user-token`)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="MTU" type="integer">
|
||||
MTU for the internal WireGuard interface (equivalent to `--mtu`)
|
||||
|
||||
**Default**: `1280`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="DNS" type="string">
|
||||
DNS server to use to resolve the endpoint (equivalent to `--dns`)
|
||||
|
||||
**Default**: `8.8.8.8`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="UPSTREAM_DNS" type="string">
|
||||
Upstream DNS server(s), comma-separated (equivalent to `--upstream-dns`)
|
||||
|
||||
**Default**: `8.8.8.8:53`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="MATCH_DOMAINS_DNS" type="string">
|
||||
FQDN wildcard patterns, comma-separated (equivalent to `--match-domains-dns`)
|
||||
|
||||
**Default**: (empty, matches every domain)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="LOG_LEVEL" type="string">
|
||||
Log level (equivalent to `--log-level`)
|
||||
|
||||
**Default**: `INFO`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="PING_INTERVAL" type="string">
|
||||
Interval for pinging the server (equivalent to `--ping-interval`)
|
||||
|
||||
**Default**: `3s`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="PING_TIMEOUT" type="string">
|
||||
Timeout for each ping (equivalent to `--ping-timeout`)
|
||||
|
||||
**Default**: `5s`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="INTERFACE" type="string">
|
||||
Name of the WireGuard interface (equivalent to `--interface`)
|
||||
|
||||
**Default**: `olm`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="ENABLE_API" type="boolean">
|
||||
Enable API server for receiving connection requests (equivalent to `--enable-api`)
|
||||
|
||||
Set to "true" to enable
|
||||
|
||||
**Default**: `false`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="HTTP_ADDR" type="string">
|
||||
HTTP server address (equivalent to `--http-addr`)
|
||||
|
||||
**Default**: (not set)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="SOCKET_PATH" type="string">
|
||||
Unix socket path or Windows named pipe (equivalent to `--socket-path`)
|
||||
|
||||
**Default**: `/var/run/olm.sock` (Linux/macOS) or `olm` (Windows)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="DISABLE_HOLEPUNCH" type="boolean">
|
||||
Disable hole punching (equivalent to `--disable-holepunch`)
|
||||
|
||||
Set to "true" to disable
|
||||
|
||||
**Default**: `false`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="OVERRIDE_DNS" type="boolean">
|
||||
Override system DNS settings (equivalent to `--override-dns`)
|
||||
|
||||
Set to "true" to enable
|
||||
|
||||
**Default**: `true`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="TUNNEL_DNS" type="boolean">
|
||||
Route DNS queries through the tunnel (equivalent to `--tunnel-dns`)
|
||||
|
||||
Set to "true" to enable
|
||||
|
||||
**Default**: `false`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="MATCH_DOMAINS_DNS" type="string">
|
||||
Optional whitelist of domains sent to the configured upstream DNS server (equivalent to `--match_domains_dns`). When unset, all queries go to upstream DNS.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="PREFER_LOCAL_ROUTES" type="boolean">
|
||||
When true, prioritizes local routes over remote ones by adding an arbitrary metric to the WireGuard routes. This is useful when you want to ensure that local network traffic (for example, to a printer or NAS) is not routed through the Pangolin tunnel.
|
||||
|
||||
**Default**: `false`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="DISABLE_RELAY" type="boolean">
|
||||
Disable relay connections (equivalent to `--disable-relay`)
|
||||
|
||||
Set to "true" to disable
|
||||
|
||||
**Default**: `false`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="CONFIG_FILE" type="string">
|
||||
Set to the location of a JSON file to load secret values
|
||||
</ResponseField>
|
||||
|
||||
### Loading secrets from files
|
||||
|
||||
You can use `CONFIG_FILE` to define a location of a config file to store the credentials between runs.
|
||||
|
||||
```
|
||||
$ cat ~/.config/olm-client/config.json
|
||||
{
|
||||
"id": "spmzu8rbpzj1qq6",
|
||||
"secret": "f6v61mjutwme2kkydbw3fjo227zl60a2tsf5psw9r25hgae3",
|
||||
"endpoint": "https://app.pangolin.net",
|
||||
"org": "",
|
||||
"userToken": "",
|
||||
"mtu": 1280,
|
||||
"dns": "8.8.8.8",
|
||||
"upstreamDNS": ["8.8.8.8:53"],
|
||||
"matchDomainsDNS": [],
|
||||
"interface": "olm",
|
||||
"logLevel": "INFO",
|
||||
"enableApi": false,
|
||||
"httpAddr": "",
|
||||
"socketPath": "/var/run/olm.sock",
|
||||
"pingInterval": "3s",
|
||||
"pingTimeout": "5s",
|
||||
"disableHolepunch": false,
|
||||
"overrideDNS": true,
|
||||
"tunnelDNS": false,
|
||||
"disableRelay": false,
|
||||
"tlsClientCert": "",
|
||||
"preferLocalRoutes": false
|
||||
}
|
||||
```
|
||||
|
||||
This file is also written to when olm first starts up. So you do not need to run every time with --id and secret if you have run it once!
|
||||
|
||||
Default locations:
|
||||
|
||||
- **macOS**: `~/Library/Application Support/olm-client/config.json`
|
||||
- **Windows**: `%PROGRAMDATA%\olm\olm-client\config.json`
|
||||
- **Linux/Others**: `~/.config/olm-client/config.json`
|
||||
|
||||
### API
|
||||
|
||||
Olm can be started with a HTTP or socket API to configure and manage it. See the [API documentation](https://github.com/fosrl/olm/blob/main/API.md) for more details.
|
||||
|
||||
</Accordion>
|
||||
|
||||
</Accordion>
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: "Client Credentials"
|
||||
description: "Understanding how client credentials work and how they can be rotated & regenerated"
|
||||
---
|
||||
## Understanding Credentials
|
||||
|
||||
Every machine client is provisioned with a unique identifier (ID), secret, and endpoint. The client uses the combination of these three to establish a secure, encrypted connection to the server.
|
||||
|
||||
User devices use a special combination of credentials and temporary session tokens tied to the user account. Therefore, these credentials are obscured and can not be regenerated for user devices. To invalidate a user device, the user should logout via the client of choice.
|
||||
|
||||
### ID
|
||||
|
||||
Example: `ln8yqs6w85la5zg`
|
||||
|
||||
The ID represents the client connection in the system. Every machine client has an ID.
|
||||
|
||||
This value is not a secret and it is okay if made publically available.
|
||||
|
||||
### Secret
|
||||
|
||||
Example: `tfpwoc580jf1l1glfagix0o97p8kirjogdflqg604n0tr3to`
|
||||
|
||||
The secret represents the "password" of the client. This secret must match the secret hashed in the system for the relevant ID.
|
||||
|
||||
<Note>
|
||||
This is a _secret_! Only share it with trusted people and be sure to store it safely and securely.
|
||||
</Note>
|
||||
|
||||
When the client connects, it uses this secret as a first handshake with the server. The server then passes temporary session credentials back to the site before it can initiate a websocket connection. Once the websocket connection is established, ephemeral keys are used to establish tunnels using WireGuard.
|
||||
|
||||
### Endpoint
|
||||
|
||||
Example: `https://app.pangolin.net` or `https://pangolin.my-server.com`
|
||||
|
||||
The endpoint is how the client knows which server to connect to. This is the fully qualified hostname of the Pangolin server (the URL you use to access the dashboard). For Pangolin cloud, the endpoint is `https://app.pangolin.net`. The client uses this endpoint ot establish a websocket connection and receive control messages from the server.
|
||||
|
||||
## Rotating and Regenerating Credentials
|
||||
|
||||
<Note>
|
||||
This is an [Enterprise Edition](/self-host/enterprise-edition)-only feature.
|
||||
</Note>
|
||||
|
||||
Client credentials can be regenerated. Regenerating credentials will completely invalidate the previous ID and secret. Use this feature if you have lost the secret and need to reset the credentials, or if you wish to rotate credentials on a regular basis for extra security.
|
||||
|
||||
To regenerate credentials, visit Clients > Machines > Your Client > Credentials in the Pangolin admin dashboard.
|
||||
|
||||
### Regenerate vs. Regenerate and Disconnect
|
||||
|
||||
Regenerate simply recreates the credentials and invalidates the old ones. The client will remain connected until you restart it with the new credentials.
|
||||
|
||||
Regenerate and Disconnect recreates the credentials and invalides the old ones. The client will instantly disconnect and will require you to restart it with the new credentials.
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: 'Client Fingerprinting'
|
||||
description:
|
||||
'A summary of device information that is collected during the connection'
|
||||
---
|
||||
## Device Fingerprinting
|
||||
|
||||
Pangolin clients collect device fingerprinting information and can perform
|
||||
security posture checks to help determine trusted devices. This information is
|
||||
used to enforce security policies, audit device configurations, and monitor
|
||||
compliance with organizational requirements. Snapshots of device information are
|
||||
collected periodically on each platform when clients are connected.
|
||||
|
||||
## Collected Fingerprint Information
|
||||
|
||||
The following device attributes are collected on each device when available:
|
||||
|
||||
- Username
|
||||
- Hostname
|
||||
- OS version
|
||||
- Kernel version
|
||||
- Architecture
|
||||
- Device model
|
||||
- Serial number
|
||||
|
||||
## Available Posture Checks
|
||||
|
||||
<Note>
|
||||
Posture checks are only collected on [Pangolin Cloud](https://app.pangolin.net/auth/signup) and self-hosted [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
Posture checks are also collected on each platform; this is device state that
|
||||
could potentially change, such as biometric availability, firewall settings, and
|
||||
other related information if it is available.
|
||||
|
||||
Posture checks are supported on a per-platform basis.
|
||||
|
||||
### Windows
|
||||
|
||||
| Posture Check | What It Reports |
|
||||
| --------------------- | ----------------------------------------------------------------- |
|
||||
| Hard drive encryption | Whether the system disk and other disks are encrypted (BitLocker) |
|
||||
| Firewall | If the firewall (Windows or third party) is enabled |
|
||||
| Antivirus | If antivirus (Windows or third party) is installed and active |
|
||||
| TPM availability | If a Trusted Platform Module is available |
|
||||
|
||||
### macOS
|
||||
|
||||
| Posture Check | What It Reports |
|
||||
| --------------------------------- | ---------------------------------------- |
|
||||
| Hard drive encryption | Whether the system disk is encrypted |
|
||||
| Biometric configuration | If Touch ID or Face ID is configured |
|
||||
| Firewall | If the macOS firewall is enabled |
|
||||
| System Integrity Protection (SIP) | Whether SIP is active |
|
||||
| Gatekeeper | Whether Gatekeeper is active |
|
||||
| Firewall stealth mode | Whether firewall stealth mode is enabled |
|
||||
| Auto updates | If automatic updates are enabled |
|
||||
|
||||
### Linux
|
||||
|
||||
| Posture Check | What It Reports |
|
||||
| --------------------- | ------------------------------------------------ |
|
||||
| Hard drive encryption | If LUKS devices are detected on the system |
|
||||
| Firewall | Whether UFW, firewalld, or iptables are enabled |
|
||||
| AppArmor | If AppArmor is active |
|
||||
| SELinux | If SELinux is active |
|
||||
| TPM availability | If a Trusted Platform Module device is available |
|
||||
|
||||
### iOS
|
||||
|
||||
No posture checks are currently supported on iOS.
|
||||
|
||||
### Android
|
||||
|
||||
| Posture Check | What It Reports |
|
||||
| --------------------- | --------------------------------------- |
|
||||
| Hard drive encryption | If File-Based Encryption is enabled |
|
||||
@@ -0,0 +1,133 @@
|
||||
---
|
||||
title: "Firewall Quirks"
|
||||
description: "Improve Pangolin direct-connect success behind restrictive firewalls"
|
||||
---
|
||||
|
||||
Most of the time, Pangolin works with existing firewall policies without any special tuning. Pangolin Sites and Pangolin Clients attempt NAT traversal first so traffic can flow directly between client and site. When direct connectivity cannot be established, traffic falls back to relaying through Gerbil. You can [learn more about how holepunching works here](https://pangolin.net/news/nat-holepunching).
|
||||
|
||||
For background on direct vs relayed paths and how to verify connection type, see [NAT Traversal](/manage/clients/nat-traversal).
|
||||
|
||||
## Firewall compatibility and workarounds
|
||||
|
||||
The table below summarizes common behavior and practical mitigations reported by users.
|
||||
|
||||
| Firewall platform | Typical behavior with Pangolin | Practical workaround |
|
||||
| --- | --- | --- |
|
||||
| Barracuda | Often relayed | Increase available UDP session capacity; optionally allow a dedicated inbound Pangolin Site port |
|
||||
| Check Point | Usually direct | No workaround typically required |
|
||||
| Cisco | Often relayed | Allow required UDP flows and, if needed, open a fixed Pangolin Site listening port |
|
||||
| Cisco Umbrella Endpoint Security | Usually relayed | Endpoint filtering commonly prevents direct paths |
|
||||
| Fortinet | Often connects directly | No workaround typically required |
|
||||
| OPNsense | Often relayed by default | Disable hard NAT |
|
||||
| pfSense | Often relayed by default | Disable hard NAT |
|
||||
| Palo Alto Networks | Mixed; may relay without NAT tuning | Use persistent NAT behavior to stabilize UDP mappings |
|
||||
| Sophos | Commonly direct | No workaround typically required |
|
||||
| UniFi Gateways (threat detection on) | Often relayed when threat detection is on | Allow peer-to-peer traffic categories |
|
||||
|
||||
If your firewall is not listed and connections are consistently relayed, start with:
|
||||
|
||||
1. Confirm current path with `pangolin status` (CLI) or client status JSON (`isRelay`).
|
||||
2. Keep relaying enabled for reliability while testing firewall adjustments.
|
||||
3. If needed, pin the Pangolin Site to a fixed listening port with `--port` / `PORT` and forward that port to the Pangolin Site host.
|
||||
|
||||
## Platform notes
|
||||
|
||||
### Barracuda
|
||||
|
||||
Barracuda environments commonly struggle with simultaneous UDP flows from multiple clients, which can push connections to relay mode.
|
||||
|
||||
What helps:
|
||||
|
||||
- Increase the firewall's maximum UDP session capacity [by increasing the max udp parameter](https://campus.barracuda.com/product/cloudgenfirewall/doc/95258827/general-firewall-configuration/?sl=AX5zwzvyOBUvMAhoTe4U&so=2)
|
||||
- If direct connectivity is still inconsistent, use a fixed Pangolin Site listening port and forward it explicitly.
|
||||
|
||||
### Check Point
|
||||
|
||||
Check Point deployments generally allow Pangolin direct connectivity without extra configuration.
|
||||
|
||||
### Cisco
|
||||
|
||||
Cisco firewalls often require additional policy tuning before direct paths become stable.
|
||||
|
||||
#### Cisco Umbrella Endpoint Security
|
||||
|
||||
When Cisco Umbrella Endpoint Security is in-path, direct peer connectivity is frequently blocked and relay becomes the normal outcome.
|
||||
|
||||
### Fortinet
|
||||
|
||||
Fortinet environments are often stable for smaller deployments but may shift heavily to relay as concurrent client count grows.
|
||||
|
||||
### pfSense
|
||||
|
||||
pfSense frequently land in relay mode unless inbound UDP mapping behavior is made explicit. Try the following changes:
|
||||
|
||||
By default, pfSense software rewrites the source port on all outgoing connections to enhance security and prevent direct exposure of internal port numbers.
|
||||
|
||||
Static port mapping in pfSense involves creating a fixed association between a specific external port number and an internal IP address and port, allowing incoming traffic to be directed to the correct destination within the local network.
|
||||
|
||||
Locate the Firewall > NAT, Outbound tab. Select Hybrid Outbound NAT rule generation. Select Save. Select ↑ Add to create a new NAT rule to the top of the list.
|
||||
|
||||
Configure the rule: IPv4+IPv6, for Protocol UDP, source address Any. Check Static Port in the Translation section of the page.
|
||||
|
||||
Select Save. Select Apply Changes.
|
||||
|
||||
### OPNsense
|
||||
|
||||
OPNsense frequently land in relay mode unless inbound UDP mapping behavior is made explicit. Try the following changes:
|
||||
|
||||
By default, OPNsense software rewrites the source port on all outgoing connections to enhance security and prevent direct exposure of internal port numbers.
|
||||
|
||||
Static port mapping in OPNsense involves creating a fixed association between a specific external port number and an internal IP address and port, allowing incoming traffic to be directed to the correct destination within the local network.
|
||||
|
||||
Go to Firewall > NAT, Outbound tab. Select Hybrid Outbound NAT rule generation. Select Save. Select ↑ Add to create a new NAT rule to the top of the list.
|
||||
|
||||
Configure the rule to match UDP traffic. Note, for each rule, select the appropriate Address Family (IP version), IPv4 for one and IPv6 for the other.
|
||||
|
||||
Here is the Markdown table based on the image provided:
|
||||
|
||||
| Interface | Source | Source Port | Destination | Destination Port | NAT Address | NAT Port | Static Port | Description |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| WAN | LAN net | udp/ * | * | udp/ * | Interface address | * | YES | Pangolin IPv4 |
|
||||
| WAN | LAN net | udp/ * | * | udp/ * | Interface address | * | YES | Pangolin IPv6 |
|
||||
|
||||
Check Static Port in the Translation section of the page. Select Save. Select Apply Changes.
|
||||
|
||||
### Palo Alto Networks
|
||||
|
||||
Palo Alto deployments can improve direct path reliability by using persistent NAT translation behavior (dynamic IP and port persistence) so UDP mappings do not churn between destinations. [Take a look at the documentation here](https://docs.paloaltonetworks.com/pan-os/10-1/pan-os-new-features/networking-features/persistent-nat-for-dipp).
|
||||
|
||||
PAN-OS 11.1.x and later: Persistent Dynamic IP and Port
|
||||
Ensure stability and performance by referring to the list of preferred releases for PAN-OS.
|
||||
|
||||
PAN-OS 11.1.1 contains the translation type for NAT policies, Persistent Dynamic IP and Port. In this translation type, once a client has sent a packet with a particular source port, the same NAT translated port number will be used for all destinations. This lets Tailscale to predict the port number and establish a direct connection.
|
||||
|
||||
To use this translation type, go to NAT Policy Rule, select the Translated Packet tab, then select the Translation Type option Persistent Dynamic IP and Port.
|
||||
|
||||
Earlier PAN-OS releases: Static IP
|
||||
With older PAN-OS releases and the Dynamic IP and Port translation type, every UDP stream will translate to a random UDP port. Opening a specific port will not allow traffic through, and Tailscale cannot predict what port number to try for a direct connection.
|
||||
|
||||
With older PAN-OS releases it is possible to use the Static IP NAT Policy to enable one device within the protected zone to make direct connections. One might choose a subnet router or other high-traffic node to optimize. In the PAN-OS software create a NAT policy rule with Translation Type set to Static IP, instead of the default setting Dynamic IP And Port. This helps Tailscale figure out how to get packets through the NAT and establish direct connections.
|
||||
|
||||
### Sophos
|
||||
|
||||
Sophos gateways usually work well with Pangolin's direct-connect flow under default NAT behavior.
|
||||
|
||||
If relay usage rises unexpectedly, verify no extra IPS/inspection rule set is rewriting UDP sessions.
|
||||
|
||||
### UniFi Gateways
|
||||
|
||||
On UniFi networks with threat detection enabled, peer-to-peer categories can interfere with hole punching.
|
||||
|
||||
In UniFi Network version 9.0.107 and earlier, select to Settings, Firewall & Security, Edit threat categories, and uncheck P2P.
|
||||
|
||||
In UniFi Network version 9.0.108 and later, select to Network, Security, Protection, Peer to Peer and Dark Web, and uncheck P2P.
|
||||
|
||||
## Verify improvements
|
||||
|
||||
After each firewall change, validate results from a client:
|
||||
|
||||
1. Reconnect the site/client pair.
|
||||
2. Run `pangolin status` (or check JSON status in GUI clients).
|
||||
3. Confirm `RELAY=false` / `isRelay: false` for sessions expected to be direct.
|
||||
|
||||
If direct paths still fail, keep relay enabled and continue iterating with narrower firewall adjustments rather than broad allow-all rules.
|
||||
@@ -0,0 +1,460 @@
|
||||
---
|
||||
title: "Install Clients"
|
||||
description: "Install native clients for Mac, Windows, and Linux"
|
||||
---
|
||||
## Windows
|
||||
|
||||
- [Pangolin for Windows Installer](https://pangolin.net/downloads/windows) - This is the official page to download the latest installer file for Windows.
|
||||
- [All Versions](https://github.com/fosrl/windows/releases) - The releases section of this repository contains release notes and download artifacts for the latest version and all older versions.
|
||||
|
||||
### Installation Steps
|
||||
|
||||
1. **Download and install the Pangolin client**
|
||||
|
||||
Download and install the Pangolin client using the official .msi installer from the download button above.
|
||||
|
||||
2. **Launch Pangolin**
|
||||
|
||||
Open Pangolin from the Start menu or the shortcut on your Desktop.
|
||||
|
||||
3. **Log in with your Pangolin account**
|
||||
|
||||
Log in on your Pangolin Cloud account or your self-hosted Pangolin instance.
|
||||
|
||||
- Click the Pangolin icon in the task bar's system tray and select Log in.
|
||||
|
||||
## Mac
|
||||
|
||||
- [Pangolin for macOS Installer](https://pangolin.net/downloads/mac) - This is the official page to download the latest installer file for macOS.
|
||||
- [All Versions](https://github.com/fosrl/apple/releases) - The releases section of this repository contains release notes and download artifacts for the latest version and all older versions.
|
||||
|
||||
### Installation Steps
|
||||
|
||||
1. **Download and install the Pangolin client**
|
||||
|
||||
Download and install the Pangolin client using the official .dmg installer from the download button above.
|
||||
|
||||
- Open the downloaded .dmg file
|
||||
- Drag and drop Pangolin.app into your Applications folder
|
||||
|
||||
2. **Launch Pangolin**
|
||||
|
||||
Open Pangolin from your Applications folder.
|
||||
|
||||
3. **Install the VPN configuration**
|
||||
|
||||
Follow the Pangolin onboarding flow, which will guide you to install the Pangolin VPN configuration.
|
||||
|
||||
- Select Open System Settings on startup when it asks to install a network extension.
|
||||
- In System Settings, under General > Login Items & Extension > By Category > Network Extensions, ensure that Pangolin.app is toggled on.
|
||||
- Select Allow when Pangolin asks to add a VPN configuration.
|
||||
|
||||
4. **Log in with your Pangolin account**
|
||||
|
||||
Log in on your Pangolin Cloud account or your self-hosted Pangolin instance.
|
||||
|
||||
- Click the Pangolin icon in the menu bar and select Log in.
|
||||
|
||||
## iOS/iPadOS
|
||||
|
||||
- [Pangolin on the App Store](https://apps.apple.com/us/app/pangolin-client/id6757407406) - This is the official page to download the latest Pangolin app for iOS and iPadOS.
|
||||
|
||||
### Installation Steps
|
||||
|
||||
1. **Download and install the Pangolin app**
|
||||
|
||||
Download and install the Pangolin app from the App Store using the link above.
|
||||
|
||||
2. **Launch Pangolin**
|
||||
|
||||
Open the Pangolin app from your home screen.
|
||||
|
||||
3. **Install the VPN configuration**
|
||||
|
||||
When prompted, allow Pangolin to add VPN configurations to your device.
|
||||
|
||||
You may be asked to enter your device passcode or use Face ID/Touch ID to authorize the VPN configuration.
|
||||
|
||||
4. **Log in with your Pangolin account**
|
||||
|
||||
Log in on your Pangolin Cloud account or your self-hosted Pangolin instance.
|
||||
|
||||
5. **Connect to Pangolin**
|
||||
|
||||
Tap the Connect button to establish a VPN connection.
|
||||
|
||||
## Android
|
||||
|
||||
- [Pangolin on Google Play](https://play.google.com/store/apps/details?id=net.pangolin.Pangolin) - This is the official page to download the latest Pangolin app for Android devices.
|
||||
- [All Versions](https://github.com/fosrl/android/releases) - The releases section of this repository contains release notes and download artifacts for the latest version and all older versions.
|
||||
|
||||
### Installation Steps
|
||||
|
||||
1. **Download and install the Pangolin app**
|
||||
|
||||
Download and install the Pangolin app from the Google Play Store using the link above.
|
||||
|
||||
2. **Launch Pangolin**
|
||||
|
||||
Open the Pangolin app from your app drawer or home screen.
|
||||
|
||||
3. **Log in with your Pangolin account**
|
||||
|
||||
Log in on your Pangolin Cloud account or your self-hosted Pangolin instance.
|
||||
|
||||
4. **Connect to Pangolin**
|
||||
|
||||
Tap the Connect button to establish a VPN connection. On the first connection, you may be prompted to allow the VPN connection.
|
||||
|
||||
## Pangolin CLI (Linux, macOS, Windows)
|
||||
|
||||
Pangolin CLI is the recommended way to run a client using a command line interface on Mac and Linux.
|
||||
|
||||
Pangolin CLI can run on Windows, but the CLI VPN functionality is not supported. You can still use Pangolin CLI on Windows for SSH alongside the Windows GUI client.
|
||||
|
||||
Pangolin CLI supports running as user device with authentication or a machine client.
|
||||
|
||||
### Install
|
||||
|
||||
Use this command to automatically install Pangolin CLI. It detects your system architecture automatically and always pulls the latest version, adding `pangolin` to your PATH:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://static.pangolin.net/get-cli.sh | bash
|
||||
```
|
||||
|
||||
On Windows, [download the latest installer](https://github.com/fosrl/cli/releases/latest/download/pangolin-cli_windows_installer.msi), or choose to install the CLI from menu bar of the desktop app by choosing the "Install Pangolin CLI" option.
|
||||
|
||||
Binaries for all platforms are available in the [GitHub releases](https://github.com/fosrl/cli/releases) for ARM and AMD64 (x86_64) architectures.
|
||||
|
||||
### Installation Steps
|
||||
|
||||
1. **Download and install the Pangolin client**
|
||||
|
||||
Install Pangolin using the installation script:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://static.pangolin.net/get-cli.sh | bash
|
||||
```
|
||||
|
||||
2. **Log in with your Pangolin account**
|
||||
|
||||
Log in on your Pangolin Cloud account or your self-hosted Pangolin instance:
|
||||
|
||||
```bash
|
||||
pangolin login
|
||||
```
|
||||
|
||||
3. **Start Pangolin**
|
||||
|
||||
When logged in as a Pangolin user, connect by running:
|
||||
|
||||
```bash
|
||||
pangolin up
|
||||
```
|
||||
|
||||
To launch a machine client without logging in, use your client credentials:
|
||||
|
||||
```bash
|
||||
pangolin up --id {client_id} --secret {client_secret} --endpoint {endpoint_url} --attach
|
||||
```
|
||||
|
||||
<Tip>
|
||||
The `--attach` flag runs the client in the foreground instead of spawning it as a background process.
|
||||
</Tip>
|
||||
|
||||
Pangolin CLI can be installed as a systemd service or run in a container. See the sections below for advanced setups.
|
||||
|
||||
## Machine Clients
|
||||
|
||||
Machine clients don't require a login and are built for machines like services to be able to connect to private resources. Like sites, they have an ID and a secret.
|
||||
|
||||
### Run as a Service
|
||||
|
||||
The CLI can install and manage a service on your host machine for you. This supports Windows services, MacOS's launchd, and Linux's systemd to create a persistent site connection from that host.
|
||||
|
||||
```bash
|
||||
sudo pangolin service install client \
|
||||
--id 31frd0uzbjvp721 \
|
||||
--secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 \
|
||||
--endpoint https://app.pangolin.net
|
||||
```
|
||||
|
||||
Check the service status:
|
||||
|
||||
```bash
|
||||
sudo pangolin service status client
|
||||
```
|
||||
|
||||
And to get the logs:
|
||||
|
||||
```bash
|
||||
sudo pangolin service logs client
|
||||
```
|
||||
|
||||
### Systemd Service (Pangolin CLI)
|
||||
|
||||
Create a basic systemd service for Pangolin CLI:
|
||||
|
||||
```ini title="/etc/systemd/system/pangolin-cli.service"
|
||||
[Unit]
|
||||
Description=Pangolin CLI
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/pangolin up --id {client_id} --secret {client_secret} --endpoint {endpoint_url} --attach
|
||||
Restart=always
|
||||
User=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Make sure to move the binary to `/usr/local/bin/pangolin` before creating the service. Replace `{client_id}`, `{client_secret}`, and `{endpoint_url}` with your machine client credentials and endpoint.
|
||||
</Warning>
|
||||
|
||||
### Docker (Pangolin CLI)
|
||||
|
||||
You can run Pangolin CLI with Docker Compose. For example, a service in your `docker-compose.yml` might look like this using environment variables (recommended):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
pangolin-cli:
|
||||
image: fosrl/pangolin-cli:latest
|
||||
container_name: pangolin-cli
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
devices:
|
||||
- /dev/net/tun:/dev/net/tun
|
||||
environment:
|
||||
- PANGOLIN_ENDPOINT=https://app.pangolin.net
|
||||
- CLIENT_ID=5n52gnzfgl3tdox
|
||||
- CLIENT_SECRET=wyael1dhftekp0ii2ni0ym6xczwjnwmucy2vr6u9kgkp8tw9
|
||||
```
|
||||
|
||||
You can also pass the CLI args to the container:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
pangolin-cli:
|
||||
image: fosrl/pangolin-cli:latest
|
||||
container_name: pangolin-cli
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
devices:
|
||||
- /dev/net/tun:/dev/net/tun
|
||||
command:
|
||||
- up
|
||||
- --id
|
||||
- "5n52gnzfgl3tdox"
|
||||
- --secret
|
||||
- "wyael1dhftekp0ii2ni0ym6xczwjnwmucy2vr6u9kgkp8tw9"
|
||||
- --endpoint
|
||||
- https://app.pangolin.net
|
||||
- --attach
|
||||
```
|
||||
|
||||
**Docker Configuration Notes:**
|
||||
|
||||
- `network_mode: host` brings the Pangolin CLI network interface to the host system, allowing the WireGuard tunnel to function properly
|
||||
- `cap_add: - NET_ADMIN` is required to grant the container permission to manage network interfaces
|
||||
- `devices: - /dev/net/tun:/dev/net/tun` is required to give the container access to the TUN device for creating WireGuard interfaces
|
||||
|
||||
<Note>
|
||||
Deploying in Kubernetes? See [Kubernetes Deployment](/manage/clients/kubernetes/deployment) for a basic guide, including how to run the client as a sidecar container.
|
||||
</Note>
|
||||
|
||||
## Olm (Advanced)
|
||||
|
||||
<Accordion title="Olm CLI (advanced use only)">
|
||||
|
||||
Olm CLI is the most basic form of a client. All other clients implement Olm under the hood in some form.
|
||||
|
||||
If you're looking for a CLI interface for a client, we recommend using Pangolin CLI where possible.
|
||||
|
||||
Olm CLI is mainly only used for machine clients. Though the Pangolin CLI can also be used for machine clients, use Pangolin CLI if you expect to log in as a user.
|
||||
|
||||
### Binary Installation (Linux)
|
||||
|
||||
#### Quick Install (Recommended)
|
||||
|
||||
Use this command to automatically install Olm. It detects your system architecture automatically and always pulls the latest version, adding Olm to your PATH:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://static.pangolin.net/get-olm.sh | bash
|
||||
```
|
||||
|
||||
#### Windows
|
||||
|
||||
If you would like to use Olm on Windows, wintun.dll is required. Please use latest installer from [GitHub releases](https://github.com/fosrl/olm/releases/latest).
|
||||
|
||||
#### Manual Download
|
||||
|
||||
Binaries for Linux, macOS, and Windows are available in the [GitHub releases](https://github.com/fosrl/olm/releases) for ARM and AMD64 (x86_64) architectures.
|
||||
|
||||
Download and install manually:
|
||||
|
||||
```bash
|
||||
wget -O olm "https://github.com/fosrl/olm/releases/download/{version}/olm_{architecture}" && chmod +x ./olm
|
||||
```
|
||||
|
||||
<Note>
|
||||
Replace `{version}` with the desired version and `{architecture}` with your architecture. Check the [release notes](https://github.com/fosrl/olm/releases) for the latest information.
|
||||
</Note>
|
||||
|
||||
### Running Olm
|
||||
|
||||
Run Olm with the configuration from Pangolin:
|
||||
|
||||
```bash
|
||||
olm \
|
||||
--id 31frd0uzbjvp721 \
|
||||
--secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 \
|
||||
--endpoint https://example.com
|
||||
```
|
||||
|
||||
### Systemd Service
|
||||
|
||||
Create a basic systemd service:
|
||||
|
||||
```ini title="/etc/systemd/system/olm.service"
|
||||
[Unit]
|
||||
Description=Olm
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/olm --id 31frd0uzbjvp721 --secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 --endpoint https://example.com
|
||||
Restart=always
|
||||
User=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Make sure to move the binary to `/usr/local/bin/olm` before creating the service!
|
||||
</Warning>
|
||||
|
||||
### Docker
|
||||
|
||||
You can also run it with Docker compose. For example, a service in your `docker-compose.yml` might look like this using environment vars (recommended):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
olm:
|
||||
image: fosrl/olm
|
||||
container_name: olm
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
devices:
|
||||
- /dev/net/tun:/dev/net/tun
|
||||
environment:
|
||||
- PANGOLIN_ENDPOINT=https://example.com
|
||||
- OLM_ID=31frd0uzbjvp721
|
||||
- OLM_SECRET=h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6
|
||||
```
|
||||
|
||||
You can also pass the CLI args to the container:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
olm:
|
||||
image: fosrl/olm
|
||||
container_name: olm
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
devices:
|
||||
- /dev/net/tun:/dev/net/tun
|
||||
command:
|
||||
- --id 31frd0uzbjvp721
|
||||
- --secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6
|
||||
- --endpoint https://example.com
|
||||
```
|
||||
|
||||
**Docker Configuration Notes:**
|
||||
|
||||
- `network_mode: host` brings the olm network interface to the host system, allowing the WireGuard tunnel to function properly
|
||||
- `cap_add: - NET_ADMIN` is required to grant the container permission to manage network interfaces
|
||||
- `devices: - /dev/net/tun:/dev/net/tun` is required to give the container access to the TUN device for creating WireGuard interfaces
|
||||
|
||||
### Windows Service
|
||||
|
||||
On Windows, olm has to be installed and run as a Windows service. When running it with the cli args, it will attempt to install and run the service to function like a cli tool.
|
||||
|
||||
Minimum Windows version: Windows 10
|
||||
|
||||
#### Service Management Commands
|
||||
|
||||
```
|
||||
# Install the service
|
||||
olm.exe install
|
||||
|
||||
# Start the service
|
||||
olm.exe start
|
||||
|
||||
# Stop the service
|
||||
olm.exe stop
|
||||
|
||||
# Check service status
|
||||
olm.exe status
|
||||
|
||||
# Remove the service
|
||||
olm.exe remove
|
||||
|
||||
# Run in debug mode (console output) with our without id & secret
|
||||
olm.exe debug
|
||||
|
||||
# Show help
|
||||
olm.exe help
|
||||
```
|
||||
|
||||
Note running the service requires credentials in `%PROGRAMDATA%\olm\olm-client\config.json`.
|
||||
|
||||
#### Service Configuration
|
||||
|
||||
When running as a service, Olm will read configuration from environment variables or you can modify the service to include command-line arguments:
|
||||
|
||||
1. Install the service: `olm.exe install`
|
||||
2. Set the credentials in `%PROGRAMDATA%\olm\olm-client\config.json`. Hint: if you run olm once with --id and --secret this file will be populated!
|
||||
3. Start the service: `olm.exe start`
|
||||
|
||||
#### Service Logs
|
||||
|
||||
When running as a service, logs are written to:
|
||||
|
||||
- Windows Event Log (Application log, source: "OlmWireguardService")
|
||||
- Log files in: `%PROGRAMDATA%\olm\logs\olm.log`
|
||||
|
||||
You can view the Windows Event Log using Event Viewer or PowerShell:
|
||||
|
||||
```powershell
|
||||
Get-EventLog -LogName Application -Source "OlmWireguardService" -Newest 10
|
||||
```
|
||||
|
||||
### Gotchas
|
||||
|
||||
Olm creates a native tun interface. This usually requires sudo / admin permissions. Some notes:
|
||||
|
||||
- **Windows**: Olm will run as a service. You can use the commands described [Configure Client](/manage/clients/configure-client) to manage it. You can use this to run it in the background if needed!
|
||||
- **LXC containers**: Need to be configured to allow tun access. On Proxmox see below.
|
||||
- **Linux**: May require root privileges or specific capabilities to create tun interfaces.
|
||||
- **macOS**: May require additional permissions for network interface creation.
|
||||
|
||||
#### LXC Proxmox
|
||||
|
||||
1. Create your LXC container.
|
||||
2. Go to the Resources tab of the container.
|
||||
3. Select Add. Then select Device Passthrough.
|
||||
4. On the Add Device prompt, enter dev/net/tun in the Device Path field and select Add.
|
||||
5. If the container is running, shut it down and start it up again.
|
||||
|
||||
Once /dev/net/tun is available, the olm can run within the LXC.
|
||||
</Accordion>
|
||||
@@ -0,0 +1,126 @@
|
||||
---
|
||||
title: "Deployment"
|
||||
description: "Basic guide for running a Pangolin client in Kubernetes as a sidecar."
|
||||
---
|
||||
|
||||
This page covers a basic way to run a Pangolin client inside Kubernetes: as a sidecar container that gives a Pod access to your Pangolin resources over the tunnel.
|
||||
|
||||
<Note>
|
||||
This is a minimal example, not a production chart. There is no official Helm chart for the client at this time — adapt the manifests below to your own deployment, Kustomize overlays, or GitOps workflow.
|
||||
</Note>
|
||||
|
||||
## Why a sidecar
|
||||
|
||||
Containers in the same Pod share a network namespace. Running Pangolin CLI (or Olm) as a sidecar container brings up the WireGuard tunnel inside that shared namespace, so every other container in the Pod can reach your Pangolin resources as if they were on the tunnel directly — no `hostNetwork` required.
|
||||
|
||||
This is useful when a workload running in your cluster (a batch job, an internal service, a CI runner) needs to reach private resources behind Pangolin.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A machine client created in Pangolin, with its `Client ID` and `Client Secret`. See [Install Clients](/manage/clients/install-client).
|
||||
- A Kubernetes cluster where you can grant the `NET_ADMIN` capability and access to `/dev/net/tun`.
|
||||
|
||||
## Step 1: Create a Secret for client credentials
|
||||
|
||||
```bash
|
||||
kubectl create secret generic pangolin-client \
|
||||
--namespace default \
|
||||
--from-literal=CLIENT_ID=<client-id> \
|
||||
--from-literal=CLIENT_SECRET=<client-secret>
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Do not commit plaintext credentials to Git. For GitOps workflows, use an encrypted or external secret backend such as SOPS, Sealed Secrets, External Secrets Operator, Vault, or Infisical.
|
||||
</Warning>
|
||||
|
||||
## Step 2: Add the sidecar container
|
||||
|
||||
Add a `pangolin-cli` container alongside your application container in the Pod spec. It needs the `NET_ADMIN` capability and access to the host's `/dev/net/tun` device to create the WireGuard interface.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: my-app
|
||||
labels:
|
||||
app: my-app
|
||||
spec:
|
||||
containers:
|
||||
- name: my-app
|
||||
image: my-app-image:latest
|
||||
# This container can reach Pangolin resources through
|
||||
# the tunnel brought up by the pangolin-cli sidecar below.
|
||||
|
||||
- name: pangolin-cli
|
||||
image: fosrl/pangolin-cli:latest
|
||||
restartPolicy: Always
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: pangolin-client
|
||||
env:
|
||||
- name: PANGOLIN_ENDPOINT
|
||||
value: "https://pangolin.example.com"
|
||||
securityContext:
|
||||
capabilities:
|
||||
add: ["NET_ADMIN"]
|
||||
volumeMounts:
|
||||
- name: tun
|
||||
mountPath: /dev/net/tun
|
||||
|
||||
volumes:
|
||||
- name: tun
|
||||
hostPath:
|
||||
path: /dev/net/tun
|
||||
```
|
||||
|
||||
<Note>
|
||||
Setting `restartPolicy: Always` on the container makes it a [native sidecar](https://kubernetes.io/docs/concepts/workloads/pods/sidecar-containers/) on Kubernetes 1.29+. It starts before the main container and Kubernetes keeps it running for the life of the Pod. On older clusters, omit `restartPolicy` and the container runs as a regular container instead — the tunnel still comes up, but startup ordering isn't guaranteed.
|
||||
</Note>
|
||||
|
||||
The `pangolin-cli` image runs `pangolin up --attach` by default, which launches the client as a machine client using the `CLIENT_ID`/`CLIENT_SECRET` from the Secret and keeps it in the foreground so the container stays alive. See [Install Clients](/manage/clients/install-client#pangolin-cli-linux-macos-windows) for the full list of environment variables and flags.
|
||||
|
||||
## Step 3: Apply and verify
|
||||
|
||||
```bash
|
||||
kubectl apply -f pod.yaml
|
||||
```
|
||||
|
||||
Check that both containers are running:
|
||||
|
||||
```bash
|
||||
kubectl get pod my-app
|
||||
```
|
||||
|
||||
Check the tunnel came up in the sidecar's logs:
|
||||
|
||||
```bash
|
||||
kubectl logs my-app -c pangolin-cli
|
||||
```
|
||||
|
||||
In the Pangolin dashboard, verify the client shows as connected. Then, from inside the `my-app` container, confirm you can reach a private resource:
|
||||
|
||||
```bash
|
||||
kubectl exec my-app -c my-app -- curl -s http://internal-resource.example
|
||||
```
|
||||
|
||||
## Deployments and other workload types
|
||||
|
||||
The same sidecar pattern applies to a `Deployment`, `StatefulSet`, `Job`, or `CronJob` — add the `pangolin-cli` container to `spec.template.spec.containers` (or `initContainers` with `restartPolicy: Always` for native sidecar behavior) the same way as shown above.
|
||||
|
||||
<Warning>
|
||||
Each Pod running the sidecar connects as the same machine client. If you scale a Deployment to multiple replicas, every replica's sidecar authenticates with the same `CLIENT_ID`/`CLIENT_SECRET` and appears as one client in Pangolin. If you need to distinguish traffic per replica, create a separate machine client and Secret for each one.
|
||||
</Warning>
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Install Clients" href="/manage/clients/install-client" icon="download">
|
||||
Review Pangolin CLI and Olm installation options, including Docker.
|
||||
</Card>
|
||||
<Card title="Configure Clients" href="/manage/clients/configure-client" icon="sliders">
|
||||
Review client configuration options.
|
||||
</Card>
|
||||
<Card title="Credentials" href="/manage/clients/credentials" icon="key">
|
||||
Manage machine client credentials.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,150 @@
|
||||
---
|
||||
title: "NAT Traversal"
|
||||
description: "How clients connect on the same network, hole punch through NAT, or relay through Pangolin"
|
||||
---
|
||||
When a client connects to a site, Pangolin tries to establish the most direct path possible. If the client and site are on the same local network, they form a direct peer-to-peer connection on that network. Otherwise, the client attempts NAT hole punching before falling back to relaying.
|
||||
|
||||
## Same Network Detection
|
||||
|
||||
When a client and site share the same local network, they detect this and connect peer-to-peer over the LAN. Packets stay on the local network and are not routed out through the internet or your Pangolin server. The connection does not use the relay path when both peers are on the same network.
|
||||
|
||||
Same-network connections typically offer the lowest latency because traffic never leaves the LAN.
|
||||
|
||||
The site collects IP addresses from the host's network interfaces and uses them to send UDP test packets to the client. If the site runs inside a Docker container, it only sees the container's internal network, so run it on the host itself to ensure it can detect the host's real IP addresses. This detection also works across VLANs. If a client and site are on different VLANs but routing between them is configured correctly, the connection should still succeed.
|
||||
|
||||
## NAT Hole Punching
|
||||
|
||||
NAT hole punching establishes a direct peer-to-peer connection between the client and site, bypassing the need to route traffic through the Pangolin server. The server coordinates the initial connection by helping both peers discover each other's network addresses, allowing them to establish a direct tunnel through their respective NATs and firewalls.
|
||||
|
||||
For a deeper look at how Pangolin coordinates hole punching between clients and sites, see [NAT Hole Punching in Pangolin](https://pangolin.net/news/nat-holepunching).
|
||||
|
||||
Direct connections typically offer:
|
||||
|
||||
- Better performance (speed and bandwidth)
|
||||
- Lower VPS transit costs when you self-host
|
||||
|
||||
## Relaying
|
||||
|
||||
If the site and client are unable to hole punch, they fall back to relaying through your Pangolin server.
|
||||
|
||||
Clients can relay traffic through a Pangolin server, through the Gerbil service specifically. Gerbil listens on UDP port 21820 for new WireGuard connections and forwards the packets down the site tunnels to the right peers. This means your connections back to your site do not require firewall config and uses the existing NAT hole punching capabilities of the site.
|
||||
|
||||
Relaying is reliable when direct paths are blocked by NAT or firewall rules, but traffic passes through your Pangolin node instead of traveling directly between the client and site.
|
||||
|
||||
<Note>
|
||||
If you use [Pangolin Cloud](https://app.pangolin.net/auth/signup) and want relayed traffic to stay on infrastructure you control, [self-host a remote node](/manage/remote-node/understanding-nodes). Relayed client traffic then traverses your node rather than Pangolin's shared cloud infrastructure.
|
||||
</Note>
|
||||
|
||||
## Check Whether a Site Is Relayed
|
||||
|
||||
You can confirm whether a connection is direct or relayed from the client.
|
||||
|
||||
### GUI clients
|
||||
|
||||
In a GUI client (Mac, Windows, Android, or iOS/iPadOS), open **Preferences**, go to the **Status** tab, and switch to the **JSON** view. Under each connected site in the `peers` object, check `isRelay` and `isLocal`:
|
||||
|
||||
- `isRelay: false` — direct hole-punched connection to the site
|
||||
- `isRelay: true` — traffic is relayed through your Pangolin server
|
||||
- `isLocal: true` — the site is on the same local network as the client
|
||||
- `isLocal: false` — the site is on a different network
|
||||
|
||||
Example (values obfuscated):
|
||||
|
||||
```json
|
||||
{
|
||||
"agent": "Pangolin macOS",
|
||||
"connected": true,
|
||||
"orgId": "org_example123",
|
||||
"peers": {
|
||||
"1001": {
|
||||
"connected": true,
|
||||
"endpoint": "203.0.113.10:51820",
|
||||
"isRelay": false,
|
||||
"isLocal": true,
|
||||
"name": "Office Network",
|
||||
"siteId": 1001
|
||||
},
|
||||
"1002": {
|
||||
"connected": true,
|
||||
"endpoint": "198.51.100.5:21820",
|
||||
"isRelay": true,
|
||||
"isLocal": false,
|
||||
"name": "Remote Lab",
|
||||
"siteId": 1002
|
||||
}
|
||||
},
|
||||
"registered": true,
|
||||
"version": "0.8.4"
|
||||
}
|
||||
```
|
||||
|
||||
In this example, **Office Network** is connected directly (`isRelay: false`) and **Remote Lab** is relayed (`isRelay: true`).
|
||||
|
||||
### CLI
|
||||
|
||||
On Linux or when using [Pangolin CLI](/manage/clients/install-client), run `pangolin status`. The **CONNECTION** column shows whether each site is connected directly (`Direct`) or via relay (`Relay`):
|
||||
|
||||
```bash
|
||||
pangolin status
|
||||
AGENT VERSION STATUS ORG
|
||||
Pangolin CLI 0.10.1 Connected org_example123
|
||||
|
||||
SITE ENDPOINT STATUS LAST SEEN CONNECTION
|
||||
Office Network 203.0.113.10:51820 Connected 1s ago Direct
|
||||
Remote Lab 198.51.100.5:21820 Connected 1s ago Relay
|
||||
Worklab Lab 192.168.1.33:23423 Connected 1s ago Local
|
||||
```
|
||||
|
||||
Use either view when troubleshooting hole punching or verifying that configuration changes took effect.
|
||||
|
||||
## Improve Hole Punching Reliability
|
||||
|
||||
Pangolin Sites support NAT traversal so clients can connect directly without relaying through the Pangolin server, improving performance and reducing latency.
|
||||
|
||||
In some environments, depending on the NAT type and firewall, you may need to tweak settings to get optimal connectivity in the firewall itself. See [Firewall Integrations](/manage/clients/firewalls) for Pangolin-specific guidance for common platforms.
|
||||
|
||||
Another option is to keep the Pangolin Site listening for client connections on a static port. This allows you to open a specific port in your firewall for Pangolin Site client connections instead of random high ports. You can do this by setting the `--port` flag or `PORT` environment variable and then opening this port in your firewall to DNAT to the Pangolin Site. See [Configure Sites](/manage/sites/configure-site) for the full list of Pangolin Site flags and environment variables.
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="What happens when the client and site are on the same network?">
|
||||
The client and site detect that they share a local network and form a direct peer-to-peer connection on the LAN. Packets are not routed out of the network, and the connection does not use the relay path.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Why is my connection relayed instead of direct?">
|
||||
Hole punching can fail when either side sits behind a restrictive NAT, symmetric NAT, or a firewall that blocks the UDP ports needed for peer discovery. When that happens, the client and site fall back to relaying through your Pangolin server so the tunnel still comes up reliably.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I check whether a site is relayed?">
|
||||
**GUI clients:** Open **Preferences**, go to the **Status** tab, and switch to the **JSON** view. Each entry under `peers` includes an `isRelay` field—`false` for direct, `true` when relayed.
|
||||
|
||||
**CLI:** Run `pangolin status` and check the **RELAY** column for each site.
|
||||
|
||||
See [Check Whether a Site Is Relayed](#check-whether-a-site-is-relayed) for examples.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Is relayed traffic still encrypted?">
|
||||
Yes. Whether the path is direct or relayed, client-to-site traffic is encrypted over WireGuard. Relaying changes the network path, not the encryption of the tunnel itself.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Will relaying affect performance?">
|
||||
Direct hole-punched connections usually offer lower latency and higher throughput because traffic does not pass through your Pangolin node. Relayed connections add an extra hop through Gerbil, which can increase latency and bandwidth use on the node—especially relevant if you self-host and pay for transit.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I force direct connections only?">
|
||||
You can disable relaying with `disable-relay` in the client config or `--disable-relay` / `DISABLE_RELAY=true` on CLI clients. If hole punching fails, the client will not fall back to a relay and the site may not connect. See [Configure Clients](/manage/clients/configure-client) for details.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I always relay and skip hole punching?">
|
||||
Yes. Set `disable-holepunch` in the client config or use `--disable-holepunch` / `DISABLE_HOLEPUNCH=true` on CLI clients to skip hole punching and connect through the relay path. See [Configure Clients](/manage/clients/configure-client) for details.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Does relayed traffic go through Pangolin Cloud?">
|
||||
On self-hosted Pangolin, relayed traffic passes through your own Gerbil instance. On [Pangolin Cloud](https://app.pangolin.net/auth/signup), relayed traffic may traverse Pangolin's shared infrastructure unless you [self-host a remote node](/manage/remote-node/understanding-nodes), in which case relayed client traffic uses your node instead.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Do I need to open firewall ports for hole punching?">
|
||||
Not always. Many networks hole punch successfully without inbound rules. If punching is unreliable, try [Firewall Integrations](/manage/clients/firewalls) guidance or pin the Pangolin Site to a static port with `--port` / `PORT` and DNAT that port to the Pangolin Site host.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: "Understanding Clients"
|
||||
description: "Create a client to connect to your Pangolin network from a remote computer"
|
||||
---
|
||||
A client is a way to access resources on sites remotely and privately via a virtual private network. Clients are used with private resources to faciliate zero-trust network access.
|
||||
|
||||
By default a client does not have access to any hosts on the local network of the site. Admins must explicitly define resources on the site and give specific users and roles access to the resources.
|
||||
|
||||
Users must log in and connect from a Pangolin client available on [Windows, Mac, Linux, iOS/iPadOS, and Android](/manage/clients/install-client). Machines (automated systems and servers) connect with an ID and secret.
|
||||
|
||||
## Client Types
|
||||
|
||||
There are two types of clients: user devices and machines.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="User Devices">
|
||||
- Associated with a user in your Pangolin organization
|
||||
- Requires login to connect (password, 2fa, etc)
|
||||
- Available for download on Mac, Windows, and Linux
|
||||
</Card>
|
||||
|
||||
<Card title="Machines">
|
||||
- Represent a server or automated system instead of a user
|
||||
- Connect with an ID and secret
|
||||
- Available in CLI form with Pangolin CLI
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
### User Devices
|
||||
|
||||
A user may download a client for their specific system. Before they can connect, they must select a Pangolin server to authenticate to using their provided Pangolin account. Users can log in as a Pangolin user or with your attached external identity provider.
|
||||
|
||||
Examples include:
|
||||
|
||||
- **SSH**: Admins and developers can connect with their client to specific hosts for SSH.
|
||||
- **RDP**: Users can connect to a remote host using familiar remote desktop software.
|
||||
|
||||
Then, just like in the Pangolin dashboard, a user selects the organization to connect to. Once connected, all resources made available to the user in that organization become available via the tunnel.
|
||||
|
||||
### Machines
|
||||
|
||||
Machine clients are for servers and automated systems that are not associated with a specific user.
|
||||
|
||||
Examples include:
|
||||
|
||||
- **CICD**: Access remote resources like a database in an automated deployment pipeline.
|
||||
- **Servers**: Provide a VPS with access to a resource running in a different network.
|
||||
|
||||
Though you may connect a server via a user account using a CLI client, we recommend you specifically use a machine client.
|
||||
|
||||
Machine clients authenticate with an ID and secret string. These credentials are passed via arguments into one of the supported Pangolin CLI clients. They can be revoked and rotated.
|
||||
|
||||
## Client Modalities
|
||||
|
||||
Clients connect to sites using NAT hole punching or relaying. A client first attempts a direct peer-to-peer tunnel, then falls back to relaying through your Pangolin server if hole punching fails.
|
||||
|
||||
See [NAT Traversal](/manage/clients/nat-traversal) for how each modality works, how to check whether a site is relayed, and tips for improving hole punching reliability.
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: "Update Clients"
|
||||
description: "Update your installed client to the latest version"
|
||||
---
|
||||
## Mac and Windows
|
||||
|
||||
### Automatic Updates (Recommended)
|
||||
|
||||
The desktop clients for Mac and Windows will periodically check for updates in the background. When an update is available, they will request permission to update. However, you can manually check for updates in the menu bar or system tray menu, or by restarting the application.
|
||||
|
||||
Once you accept the update, these clients will automatically download the latest version and replace itself on your computer.
|
||||
|
||||
### Manual Updates
|
||||
|
||||
- **Mac**: Find the latest version in the [GitHub releases](https://github.com/fosrl/apple/releases).
|
||||
- **Windows**: Find the latest version in the [GitHub releases](https://github.com/fosrl/windows/releases).
|
||||
|
||||
You can download the latest installer files and restart the installation process to install the latest version. Visit [https://pangolin.net/downloads](https://pangolin.net/downloads) to find the latest official installers for your platform.
|
||||
|
||||
## Pangolin CLI
|
||||
|
||||
Find the latest version in the [GitHub releases](https://github.com/fosrl/cli/releases).
|
||||
|
||||
### Automatic Updates (Recommended)
|
||||
|
||||
If you already have Pangolin CLI installed, use the update command:
|
||||
|
||||
```bash
|
||||
pangolin update
|
||||
```
|
||||
|
||||
Or you can re-run the installation script:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://static.pangolin.net/get-cli.sh | bash
|
||||
```
|
||||
|
||||
### Manual Updates
|
||||
|
||||
Download the latest binary for your system from [GitHub releases](https://github.com/fosrl/cli/releases) and replace your existing binary.
|
||||
|
||||
```bash
|
||||
wget -O pangolin "https://github.com/fosrl/cli/releases/download/{version}/pangolin-cli_{architecture}" && chmod +x ./pangolin
|
||||
```
|
||||
|
||||
<Note>
|
||||
Replace `{version}` with the desired version and `{architecture}` with your architecture. Check the [release notes](https://github.com/fosrl/cli/releases) for the latest information.
|
||||
</Note>
|
||||
@@ -0,0 +1,389 @@
|
||||
---
|
||||
title: "Common API Routes"
|
||||
description: "API routes and patterns for the most common actions and flows"
|
||||
---
|
||||
|
||||
All routes in this guide require an API key. See [Integration API](/manage/integration-api) for creating and using API keys (Bearer token in the `Authorization` header). Many routes use an organization ID in the path — see [Organization ID](/manage/organizations/org-id) for where to find it.
|
||||
|
||||
<Note>
|
||||
This guide is a **minimal** walkthrough for creating core Pangolin components (sites, resources, targets) via the API. It is not exhaustive — some elements are omitted for simplicity — but everything shown here works and illustrates patterns used elsewhere in the API. For full coverage of endpoints (get, update, delete, list, etc.), use the [Swagger docs](https://api.pangolin.net/v1/docs).
|
||||
</Note>
|
||||
|
||||
## What is an identifier?
|
||||
|
||||
In the Pangolin app, sites, public resources, private resources, and clients have an **Identifier**. In the API, this value is called `niceId`.
|
||||
|
||||
The identifier is a human-readable, organization-unique value you can use when you want something more stable and memorable than a numeric ID. For example, a site might have a numeric `siteId` like `8723` and a `niceId` like `warehouse-west`.
|
||||
|
||||
Identifiers are especially useful for automation:
|
||||
|
||||
- In [Blueprints](/manage/blueprints), resource and site keys act as stable identifiers. A target can reference a site by its identifier, such as `site: warehouse-west`.
|
||||
- In the API, responses include both the numeric ID and `niceId` when the object supports identifiers. Some routes can also look up objects by `niceId`, such as `GET /org/{orgId}/site/{niceId}` for sites.
|
||||
|
||||
Use the numeric ID when an endpoint asks for `siteId`, `resourceId`, or `siteResourceId`. Use `niceId` when an endpoint or blueprint field asks for an identifier.
|
||||
|
||||
## Create site
|
||||
|
||||
This section assumes you're creating a **Pangolin Site**. For all Site endpoints, see [Site API (Swagger)](https://api.pangolin.net/v1/docs/#/Site).
|
||||
|
||||
**PUT** `/org/{orgId}/site`
|
||||
|
||||
**Path**
|
||||
- `orgId` (string) — organization ID
|
||||
|
||||
**Body (Newt)**
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `name` | string | Yes | Display name for the site |
|
||||
| `type` | string | Yes | Use `"newt"` |
|
||||
|
||||
**Example Response**
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"siteId": 8723,
|
||||
"niceId": "quiet-lerista-labialis",
|
||||
"name": "My Site",
|
||||
"type": "newt",
|
||||
"online": false,
|
||||
"address": "100.90.128.0",
|
||||
"newtId": "se897rmyjvamy5q",
|
||||
"secret": "ystsahnvqetgx1fhn2udylq0zqgv611zbfp6ya3jvfrprn7q"
|
||||
},
|
||||
"success": true,
|
||||
"error": false,
|
||||
"message": "Site created successfully",
|
||||
"status": 201
|
||||
}
|
||||
```
|
||||
|
||||
### Advanced option: pick-site-defaults
|
||||
|
||||
Use this when you want to generate some template values and control the site creation.
|
||||
|
||||
**GET** `/org/{orgId}/pick-site-defaults`
|
||||
|
||||
Returns values you pass into the create-site endpoint.
|
||||
|
||||
**Path**
|
||||
- `orgId` (string) — organization ID
|
||||
|
||||
**Example Response**
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"newtId": "jwhk5154mfmos0s",
|
||||
"newtSecret": "8afipi4i79jjbsxjqpkgc0xe2ge143s54oi64mw5567mxgr8",
|
||||
"clientAddress": "100.90.128.0"
|
||||
},
|
||||
"success": true,
|
||||
"error": false,
|
||||
"message": "Site defaults chosen successfully",
|
||||
"status": 200
|
||||
}
|
||||
```
|
||||
|
||||
### Create site
|
||||
|
||||
**PUT** `/org/{orgId}/site`
|
||||
|
||||
**Path**
|
||||
- `orgId` (string) — organization ID
|
||||
|
||||
**Body (Newt)**
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `name` | string | Yes | Display name for the site |
|
||||
| `address` | string | Yes | From pick-site-defaults `clientAddress` |
|
||||
| `type` | string | Yes | Use `"newt"` |
|
||||
| `newtId` | string | Yes | From pick-site-defaults `newtId` |
|
||||
| `secret` | string | Yes | From pick-site-defaults `newtSecret` |
|
||||
|
||||
**Example Response**
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"siteId": 8723,
|
||||
"niceId": "quiet-lerista-labialis",
|
||||
"name": "My Site",
|
||||
"type": "newt",
|
||||
"online": false,
|
||||
"address": "100.90.128.0/24"
|
||||
"newtId": "se897rmyjvamy5q",
|
||||
"secret": "ystsahnvqetgx1fhn2udylq0zqgv611zbfp6ya3jvfrprn7q"
|
||||
},
|
||||
"success": true,
|
||||
"error": false,
|
||||
"message": "Site created successfully",
|
||||
"status": 201
|
||||
}
|
||||
```
|
||||
|
||||
## Create public HTTP resource
|
||||
|
||||
You need a **domain ID** before creating a resource. List your org's domains, then create the resource with the chosen domain. For all public resource endpoints, see [Public Resource API (Swagger)](https://api.pangolin.net/v1/docs/#/Public%20Resource).
|
||||
|
||||
<Steps>
|
||||
<Step title="List domains">
|
||||
Call the list-domains endpoint to get available domains and their `domainId` values.
|
||||
</Step>
|
||||
<Step title="Create the resource">
|
||||
Call the create-resource endpoint with `http: true` and the `domainId` from step 1.
|
||||
</Step>
|
||||
<Step title="Add targets to the resource">
|
||||
Call the create-target endpoint for each backend (site + ip:port) that should serve traffic for the resource.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### List domains
|
||||
|
||||
**GET** `/org/{orgId}/domains`
|
||||
|
||||
Returns all domains for the organization. Use `domainId` from a domain when creating a resource.
|
||||
|
||||
**Path**
|
||||
- `orgId` (string) — organization ID
|
||||
|
||||
**Query** (optional)
|
||||
| Param | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `limit` | number | 1000 | Max domains to return |
|
||||
| `offset` | number | 0 | Pagination offset |
|
||||
|
||||
**Example Response**
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"domains": [
|
||||
{
|
||||
"domainId": "pg3i1k4lhibhl3i",
|
||||
"baseDomain": "pangolin.net",
|
||||
"verified": true,
|
||||
"type": "ns"
|
||||
},
|
||||
{
|
||||
"domainId": "q1ngj5341k7oydo",
|
||||
"baseDomain": "bitwarden.pangolin.net",
|
||||
"verified": true,
|
||||
"type": "cname"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"total": 2,
|
||||
"limit": 1000,
|
||||
"offset": 0
|
||||
}
|
||||
},
|
||||
"success": true,
|
||||
"error": false,
|
||||
"message": "Domains retrieved successfully",
|
||||
"status": 200
|
||||
}
|
||||
```
|
||||
|
||||
### Create public HTTP resource
|
||||
|
||||
**PUT** `/org/{orgId}/public-resource`
|
||||
|
||||
**Path**
|
||||
- `orgId` (string) — organization ID
|
||||
|
||||
**Body (HTTP resource)**
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `name` | string | Yes | Display name for the resource (1–255 chars) |
|
||||
| `http` | boolean | Yes | Use `true` for public HTTP resource |
|
||||
| `domainId` | string | Yes | From list-domains |
|
||||
| `protocol` | string | Yes | `"tcp"` |
|
||||
| `subdomain` | string \| null | No | See below. |
|
||||
|
||||
**Subdomain and domain types**
|
||||
|
||||
Domains come in three types: **ns** | **cname** | **wildcard**.
|
||||
|
||||
<Note>
|
||||
**Wildcard** is only available in self-hosted Pangolin. Pangolin Cloud uses **ns** and **cname** only.
|
||||
</Note>
|
||||
|
||||
- **ns** — You can use the base domain (set `subdomain` to `null`) or set a subdomain (e.g. `my-app` → `my-app.digpangolin.io`).
|
||||
- **cname** — Only the base domain is used; set `subdomain` to `null` (the domain’s `baseDomain` is the FQDN).
|
||||
- **wildcard** — Same as ns for subdomain behavior (self-hosted only).
|
||||
|
||||
The `subdomain` value is combined with the base domain from `domainId` to form the FQDN. Omit `subdomain` or pass `null` when using the base domain alone.
|
||||
|
||||
**Example request**
|
||||
```json
|
||||
{
|
||||
"name": "My Resource",
|
||||
"http": true,
|
||||
"subdomain": "my-subdomain",
|
||||
"domainId": "pg3i1k4lhibhl3i",
|
||||
"protocol": "tcp"
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"resourceId": 9942,
|
||||
"niceId": "decent-louisiana-waterthrush",
|
||||
"name": "My Resource",
|
||||
"subdomain": "my-subdomain",
|
||||
"fullDomain": "my-subdomain.pangolin.net",
|
||||
"domainId": "pg3i1k4lhibhl3i"
|
||||
},
|
||||
"success": true,
|
||||
"error": false,
|
||||
"message": "Http resource created successfully",
|
||||
"status": 201
|
||||
}
|
||||
```
|
||||
|
||||
### Add targets to the resource
|
||||
|
||||
**PUT** `/public-resource/{resourceId}/target`
|
||||
|
||||
Add a target (backend) to a resource. Use the numeric `resourceId` from the create-resource response. The target is the site and address (ip + port) that will receive traffic for the resource. For all target endpoints, see [Resource Target API (Swagger)](https://api.pangolin.net/v1/docs/#/Resource%20Target).
|
||||
|
||||
**Path**
|
||||
- `resourceId` (number) — From create-resource response (e.g. `9943`)
|
||||
|
||||
**Body**
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `siteId` | number | Yes | Site that hosts the backend (from create-site or list sites) |
|
||||
| `ip` | string | Yes | Backend IP or hostname |
|
||||
| `port` | number | Yes | Backend port |
|
||||
| `method` | string | Yes | e.g. `"http"` for HTTP resources |
|
||||
|
||||
**Example request**
|
||||
```json
|
||||
{
|
||||
"ip": "localhost",
|
||||
"port": 8080,
|
||||
"method": "http",
|
||||
"siteId": 5165
|
||||
}
|
||||
```
|
||||
|
||||
**Example response**
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"targetId": 11280,
|
||||
"resourceId": 9942,
|
||||
"siteId": 8723,
|
||||
"ip": "localhost",
|
||||
"method": "http",
|
||||
"port": 8080
|
||||
},
|
||||
"success": true,
|
||||
"error": false,
|
||||
"message": "Target created successfully",
|
||||
"status": 201
|
||||
}
|
||||
```
|
||||
|
||||
## Create private resource
|
||||
|
||||
You need an existing site. For more endpoints, see [Private Resource API (Swagger)](https://api.pangolin.net/v1/docs/#/Private%20Resource).
|
||||
|
||||
### Create private resource
|
||||
|
||||
**PUT** `/org/{orgId}/private-resource`
|
||||
|
||||
**Path**
|
||||
- `orgId` (string) — organization ID
|
||||
|
||||
**Body**
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `name` | string | Yes | Display name (1–255 chars) |
|
||||
| `siteId` | number | Yes | Site that hosts the resource (from create-site or list sites) |
|
||||
| `mode` | string | Yes | `"host"` \| `"cidr"` |
|
||||
| `destination` | string | Yes | For `host`: IP or hostname (e.g. `localhost`). For `cidr`: CIDR (e.g. `10.0.0.0/24`). |
|
||||
| `alias` | string | For host+domain | Alias hostname (e.g. `private-resource.internal`). Required when `destination` is a domain; optional for IP. Must be unique in the org. |
|
||||
| `tcpPortRangeString` | string | Yes | See below. |
|
||||
| `udpPortRangeString` | string | Yes | See below. |
|
||||
| `disableIcmp` | boolean | No | Default `false` |
|
||||
| `authDaemonMode` | string | No | `"site"` \| `"remote"` |
|
||||
| `roleIds` | number[] | No | Role IDs that can access (default `[]`) |
|
||||
| `userIds` | string[] | No | User IDs that can access (default `[]`) |
|
||||
|
||||
**TCP/UDP port range strings:** Control which ports are allowed for the private resource.
|
||||
|
||||
- **`"*"`** — Allow all ports (common for TCP when you want full access to the host).
|
||||
- **`""`** (empty string) — Allow no ports. Use when you don’t need that protocol (e.g. `udpPortRangeString: ""` if only TCP is used).
|
||||
- **Specific ports or ranges** — Comma-separated list: single ports (e.g. `"80,443"`) or ranges (e.g. `"8000-9000"`). Example: `"80,443,8080-8090"` allows 80, 443, and 8080–8090.
|
||||
|
||||
Use `tcpPortRangeString` and `udpPortRangeString` independently (e.g. TCP all, UDP none, or vice versa).
|
||||
|
||||
If you omit `roleIds`/`userIds`, the org admin role is granted access by default. Add IDs to restrict access.
|
||||
|
||||
**Example request**
|
||||
```json
|
||||
{
|
||||
"name": "My Private Resource",
|
||||
"siteId": 8723,
|
||||
"mode": "host",
|
||||
"destination": "localhost",
|
||||
"alias": "private-resource.internal",
|
||||
"tcpPortRangeString": "*",
|
||||
"udpPortRangeString": "",
|
||||
"disableIcmp": false,
|
||||
"authDaemonMode": "site",
|
||||
"roleIds": [],
|
||||
"userIds": []
|
||||
}
|
||||
```
|
||||
|
||||
**Example response**
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"siteResourceId": 1165,
|
||||
"siteId": 8723,
|
||||
"niceId": "unsung-round-tailed-ground-squirrel",
|
||||
"name": "My Private Resource",
|
||||
"mode": "host",
|
||||
"destination": "localhost",
|
||||
"alias": "private-resource.internal",
|
||||
"tcpPortRangeString": "*",
|
||||
"udpPortRangeString": "",
|
||||
"disableIcmp": false,
|
||||
"authDaemonPort": 22123,
|
||||
"authDaemonMode": "site"
|
||||
},
|
||||
"success": true,
|
||||
"error": false,
|
||||
"message": "Site resource created successfully",
|
||||
"status": 201
|
||||
}
|
||||
```
|
||||
|
||||
## Assign users and roles to a resource (public or private)
|
||||
|
||||
You can grant access to a **public resource** or a **private resource** by adding roles or users. First list roles and users in the org to get IDs, then call the add endpoints. The Admin role cannot be assigned via these endpoints.
|
||||
|
||||
### Get role and user IDs
|
||||
|
||||
**GET** `/org/{orgId}/roles` — Returns roles in the org. Use `roleId` (number) when adding a role to a resource. Query: `limit`, `offset` (optional).
|
||||
|
||||
**GET** `/org/{orgId}/users` — Returns users in the org. Use `id` (string) as `userId` when adding a user to a resource. Query: `limit`, `offset` (optional).
|
||||
|
||||
### Public resource
|
||||
|
||||
**POST** `/public-resource/{resourceId}/roles/add` — **Path:** `resourceId` (number, from create public-resource). **Body:** `{ "roleId": number }`. Admin role not allowed.
|
||||
|
||||
**POST** `/public-resource/{resourceId}/users/add` — **Path:** `resourceId` (number). **Body:** `{ "userId": string }`.
|
||||
|
||||
Both return `{ "data": {}, "success": true, "error": false, "message": "...", "status": 201 }`.
|
||||
|
||||
### Private resource
|
||||
|
||||
**POST** `/private-resource/{siteResourceId}/roles/add` — **Path:** `siteResourceId` (number, from create private-resource). **Body:** `{ "roleId": number }`. Admin role not allowed.
|
||||
|
||||
**POST** `/private-resource/{siteResourceId}/users/add` — **Path:** `siteResourceId` (number). **Body:** `{ "userId": string }`.
|
||||
|
||||
Same response shape as above. Role must belong to the same org as the private resource. For more endpoints (list/remove), see [Public Resource API](https://api.pangolin.net/v1/docs/#/Public%20Resource) and [Private Resource API](https://api.pangolin.net/v1/docs/#/Private%20Resource).
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: "Community Blueprints"
|
||||
description: "Community-maintained library of ready-to-use Pangolin Blueprints for self-hosted applications"
|
||||
---
|
||||
|
||||
The Pangolin Community Blueprints repository is a shared library of ready-to-use Docker Compose templates for common self-hosted services, already wired to expose those services through Pangolin.
|
||||
|
||||
<Card icon="github" arrow="true" cta="View repository" href="https://github.com/fosrl/blueprints">
|
||||
View the repository on GitHub to browse the available blueprints and learn more: [github.com/fosrl/blueprints](https://github.com/fosrl/blueprints)
|
||||
</Card>
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl"
|
||||
src="https://www.youtube.com/embed/j_chhXwFD9w?si=xwEWk5yXYlP-96AE"
|
||||
title="Pangolin Community Blueprints walkthrough"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
referrerPolicy="strict-origin-when-cross-origin"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
|
||||
## Why this repository exists
|
||||
|
||||
When people self-host, the hardest part is often not the app itself, it is the networking, exposure, and access control around it.
|
||||
|
||||
This repository exists to package those patterns into reusable blueprints so you can:
|
||||
|
||||
- start from known-good templates for popular services
|
||||
- avoid repetitive reverse proxy and exposure configuration
|
||||
- apply secure defaults like SSO-oriented access patterns
|
||||
- make deployments more consistent across environments
|
||||
- share and improve blueprints as a community
|
||||
|
||||
## What you get
|
||||
|
||||
Each service blueprint includes pre-defined labels and structure that map to Pangolin resources, so bringing a service up can create and configure its exposure model with minimal dashboard work.
|
||||
|
||||
Common examples include:
|
||||
|
||||
- Grafana
|
||||
- Homepage
|
||||
- Immich
|
||||
- Jellyfin
|
||||
- and more community-contributed services
|
||||
|
||||
Browse the full, current list in the repository:
|
||||
|
||||
- [View available services](https://github.com/fosrl/blueprints/tree/main/services)
|
||||
|
||||
## Quick start
|
||||
|
||||
At a high level, getting started looks like this:
|
||||
|
||||
1. Create a Pangolin Cloud account (or use your self-hosted Pangolin) and set up a site.
|
||||
2. Copy your site connector values (`NEWT_ID` and `NEWT_SECRET`) and set your base domain.
|
||||
3. Clone the community blueprints repository and configure the root `.env`.
|
||||
4. Initialize and run a service blueprint.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/fosrl/blueprints
|
||||
cd blueprints
|
||||
./bin/blueprint list
|
||||
./bin/blueprint init <service>
|
||||
./bin/blueprint up <service>
|
||||
```
|
||||
|
||||
For complete setup details and all command options, use the repository docs directly:
|
||||
|
||||
- [README and full quick start](https://github.com/fosrl/blueprints#readme)
|
||||
|
||||
## Contribute and learn more
|
||||
|
||||
The repository is community-driven. If a service you use is missing, you can propose or contribute a blueprint.
|
||||
|
||||
- [Repository](https://github.com/fosrl/blueprints)
|
||||
- [Contributing guide](https://github.com/fosrl/blueprints/blob/main/CONTRIBUTING.md)
|
||||
- [Community guide](https://github.com/fosrl/blueprints/blob/main/COMMUNITY.md)
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
title: "DNS Cache"
|
||||
description: "What is a DNS cache and how to manage it with private resources"
|
||||
---
|
||||
|
||||
## What is a DNS Cache?
|
||||
A DNS cache is a temporary storage of DNS records that allows for faster resolution of domain names to IP addresses. When a user visits a website, their device queries the DNS server to resolve the domain name to an IP address. The DNS cache stores this information so that subsequent requests for the same domain can be resolved more quickly without needing to query the DNS server again.
|
||||
|
||||
## Why you might see the placeholder page?
|
||||
If you are seeing a placeholder page instead of the expected content, it could be due to a DNS cache issue. This can happen if the DNS cache has stored outdated or incorrect information about the domain name, leading to the wrong IP address being resolved.
|
||||
|
||||
## How to clear my browser's DNS cache?
|
||||
**On most browsers, closing it completely and reopening it will clear the DNS cache.** However, if you want to manually clear the DNS cache without closing the browser, you can follow these steps:
|
||||
|
||||
### Chrome:
|
||||
1. Open a new tab and type `chrome://net-internals/#dns` in the address bar.
|
||||
2. Click on the "Clear host cache" button.
|
||||
|
||||
### Firefox:
|
||||
1. Open a new tab and type `about:networking#dns` in the address bar.
|
||||
2. Click on the "Clear DNS Cache" button.
|
||||
|
||||
### Safari:
|
||||
1. Open Safari and go to the "Safari" menu.
|
||||
2. Select "Settings" and then go to the "Advanced" tab.
|
||||
3. Check the box that says "Show Develop menu in menu bar".
|
||||
4. Close the preferences window and go to the "Develop" menu.
|
||||
5. Select "Empty Caches".
|
||||
|
||||
### Edge:
|
||||
1. Open a new tab and type `edge://net-internals/#dns` in the address bar.
|
||||
2. Click on the "Clear host cache" button.
|
||||
@@ -0,0 +1,108 @@
|
||||
---
|
||||
title: "Domains"
|
||||
icon: "globe"
|
||||
description: "Learn how to configure domains for your Pangolin resources and understand the different domain types available"
|
||||
---
|
||||
Domains allow you to access your Pangolin resources through custom URLs. Pangolin supports different domain types depending on your deployment model and requirements.
|
||||
|
||||
## Getting Started
|
||||
|
||||
In Pangolin Cloud, a domain is not required. You can use our provided domain endings like `.hostlocal.app` or `.tunneled.to` for your resources on any of the paid plans.
|
||||
|
||||
## Domain Types
|
||||
|
||||
| Type | DNS Record | Coverage | Availability |
|
||||
| --- | --- | --- | --- |
|
||||
| **Wildcard** | A / AAAA | Base domain and all subdomains (e.g. `example.com`, `app.example.com`) | Self-Hosted |
|
||||
| **Domain Delegation** | NS | Base domain and all subdomains, managed entirely by Pangolin | Cloud |
|
||||
| **Single Domain** | CNAME | Only the exact hostname provided (no subdomains) | Cloud |
|
||||
|
||||
<Tip>Wildcard (A record) domains are the most common setup for Self-Hosted, since you already control the server's DNS and just point it at your VPS IP.</Tip>
|
||||
|
||||
## Delegating a Domain to Pangolin Cloud
|
||||
|
||||
Domain delegation and single-domain (CNAME) setups exist because, unlike self-hosting, Pangolin Cloud doesn't run on a server whose IP you control — your traffic is routed and terminated on Pangolin's infrastructure instead. For Pangolin to issue certificates and route traffic for your domain, it needs to be able to create and update DNS records on your behalf.
|
||||
|
||||
- **Self-Hosted**: You point A/AAAA records at your own VPS's IP address. DNS stays with your existing provider; Pangolin never needs access to it.
|
||||
- **Cloud**: There's no single static IP that's "yours" to point at, and Pangolin needs to create records (for certificate validation, routing, and new resources) without you manually updating DNS every time. Delegation and CNAME setups solve this by giving Pangolin either full control over the zone (NS delegation) or a single record it can manage (CNAME).
|
||||
|
||||
Domain delegation is the more powerful option since new subdomains work automatically, but it comes with a real tradeoff: **it hands over the nameservers for the whole domain (or subdomain), not just the records Pangolin needs.**
|
||||
|
||||
## Setting It Up
|
||||
|
||||
### Self-Hosted (Wildcard / A Records)
|
||||
|
||||
<Steps>
|
||||
<Step title="Point DNS at your server">
|
||||
In your DNS provider, add an A record (or AAAA for IPv6) for the wildcard and, optionally, the root domain, pointing to your VPS's public IP:
|
||||
|
||||
```dns
|
||||
Type: A
|
||||
Name: *.example.com
|
||||
Value: 192.0.2.1
|
||||
|
||||
Type: A
|
||||
Name: example.com
|
||||
Value: 192.0.2.1
|
||||
```
|
||||
</Step>
|
||||
<Step title="Wait for propagation">
|
||||
DNS changes can take anywhere from a few minutes to 48 hours to propagate.
|
||||
</Step>
|
||||
<Step title="Add the domain in Pangolin">
|
||||
Configure the domain in `config/config.yml` or through the Pangolin dashboard so Traefik can request certificates for it.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
More detail on ports, firewalls, and networking for self-hosted setups is in the [DNS & Networking Section](/self-host/dns-and-networking).
|
||||
|
||||
### Cloud & Enterprise (Delegation or CNAME)
|
||||
|
||||
<Steps>
|
||||
<Step title="Add the domain in the Pangolin dashboard">
|
||||
From your organization's domain settings, add the domain and choose either **Domain Delegation** or **Single Domain (CNAME)**. Pangolin will show you the exact records to configure.
|
||||
</Step>
|
||||
<Step title="Configure DNS at your registrar/provider">
|
||||
<Tabs>
|
||||
<Tab title="Domain Delegation (NS)">
|
||||
Change the nameservers for the domain (or subdomain) to Pangolin's, as shown in the dashboard:
|
||||
|
||||
```dns
|
||||
Type: NS
|
||||
Name: test.example.com
|
||||
Value: ns1.pangolin-ns.net
|
||||
ns2.pangolin-ns.net
|
||||
ns3.pangolin-ns.net
|
||||
```
|
||||
|
||||
This is done at your registrar or in your current DNS provider's nameserver settings, not as a normal DNS record.
|
||||
</Tab>
|
||||
<Tab title="Single Domain (CNAME)">
|
||||
Add a CNAME for the hostname and for its ACME challenge, pointing to the values Pangolin gives you:
|
||||
|
||||
```dns
|
||||
Type: CNAME
|
||||
Name: test.example.com
|
||||
Value: 0nbn5rpcq4wthq6.cname.pangolin.net
|
||||
|
||||
Type: CNAME
|
||||
Name: _acme-challenge.test.example.com
|
||||
Value: _acme-challenge.0nbn5rpcq4wthq6.cname.pangolin.net
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</Step>
|
||||
<Step title="Wait for verification">
|
||||
Pangolin checks for the records automatically. NS delegation can take longer to propagate than a CNAME since it changes how the entire zone (or subzone) resolves.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Warning>
|
||||
**Changing nameservers hands over the whole zone, not individual records.** If you point the nameservers for your root domain (e.g. `example.com`) at Pangolin, Pangolin becomes authoritative for **every** record on that domain — including MX/email, TXT/SPF, and any other subdomains you weren't intending to move. Once delegated, you manage DNS for that zone through Pangolin, not your old provider.
|
||||
|
||||
Some registrars — Cloudflare included — only let you delegate NS records at the root, and **don't allow you to add other records once Cloudflare is no longer authoritative** for that zone (since it's no longer serving the zone at all). Practically, this means:
|
||||
|
||||
- Delegating `*.example.com` moves the entire domain, including email and any other services, to Pangolin's nameservers.
|
||||
- Delegating a subdomain instead (e.g. `*.apps.example.com`) avoids this — the root domain and its other records stay with your existing provider untouched.
|
||||
- If you must delegate the root and still need other records (MX, TXT, etc.) that your old provider managed use a subdomain delegation / CNAME setup instead and maintain these records in the root provider.
|
||||
</Warning>
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
title: "Endpoints & Relays"
|
||||
icon: "network-wired"
|
||||
description: "Pangolin Cloud hostnames for firewall allowlists and network whitelisting"
|
||||
---
|
||||
|
||||
<Note>
|
||||
This page applies to [Pangolin Cloud](https://app.pangolin.net/auth/signup)
|
||||
only. If you self-host Pangolin, see [DNS &
|
||||
Networking](/self-host/dns-and-networking) for the hostnames and ports on
|
||||
your own deployment.
|
||||
</Note>
|
||||
|
||||
Use this page when you need to allowlist Pangolin in a corporate firewall, proxy, or egress policy. Sites, clients, and browsers connect to two classes of infrastructure on Pangolin Cloud: the **control plane** and **points of presence** (also called relays).
|
||||
|
||||
## Control Plane
|
||||
|
||||
The control plane is the central Pangolin service that runs the dashboard, REST API, authentication, and configuration orchestration.
|
||||
|
||||
| Hostname | Purpose |
|
||||
| ------------------ | --------------------------------------------------------------------------------- |
|
||||
| `app.pangolin.net` | Dashboard, API, authentication, and WebSocket configuration for sites and clients |
|
||||
|
||||
Sites and clients use this hostname as their **endpoint**. For example, Sites connect here to register, receive policy, and maintain their control channel.
|
||||
|
||||
## Points of Presence
|
||||
|
||||
Points of presence (PoPs), also called relays, are the networking edge where sites and clients connect for data traffic.
|
||||
|
||||
| Hostname | Location |
|
||||
| -------------------------- | -------- |
|
||||
| `jupiter.pop.pangolin.net` | US West |
|
||||
| `saturn.pop.pangolin.net` | US West |
|
||||
| `neptune.pop.pangolin.net` | US West |
|
||||
| `earth.pop.pangolin.net` | US East |
|
||||
| `mars.pop.pangolin.net` | US East |
|
||||
| `erid.pop.pangolin.net` | US East |
|
||||
| `pluto.pop.pangolin.net` | US East |
|
||||
| `uranus.pop.pangolin.net` | Europe |
|
||||
| `venus.pop.pangolin.net` | Europe |
|
||||
| `mercury.pop.pangolin.net` | Europe |
|
||||
|
||||
### What PoPs Do
|
||||
|
||||
- **Public resources** send all traffic through a PoP.
|
||||
- **Private resources** use PoPs to coordinate [NAT traversal](/manage/clients/nat-traversal) between a client and a site. If hole punching fails, traffic relays through the PoP instead.
|
||||
|
||||
Pangolin sites and clients automatically select the most optimal PoP to connect to. You do not choose a PoP manually during normal operation.
|
||||
|
||||
## Remote Nodes
|
||||
|
||||
If you use [remote nodes](/manage/remote-node/understanding-nodes), the point of presence (relay) for your traffic is your self-hosted node, not the cloud PoPs listed above.
|
||||
|
||||
The control plane endpoint for dashboard access, API calls, and authentication remains `app.pangolin.net`.
|
||||
|
||||
## Whitelisting Checklist
|
||||
|
||||
To allow Pangolin Cloud on an outbound firewall or proxy, permit traffic to:
|
||||
|
||||
1. `app.pangolin.net` for the control plane
|
||||
2. All `*.pop.pangolin.net` hostnames listed above for site and client data traffic
|
||||
|
||||
If you use remote nodes, also allow traffic to your remote node's public hostname or IP address.
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: "Geo-blocking"
|
||||
description: "Configure geo-blocking to restrict access based on geographic location"
|
||||
---
|
||||
<Note>
|
||||
Geo-blocking is available in Pangolin Community Edition. It depends on Pangolin's geo-location database, which is also used by other location-aware features such as analytics. Follow [Enable Geo-location](/self-host/advanced/enable-geolocation) before creating country or region rules.
|
||||
</Note>
|
||||
|
||||
## Benefits of Geo-blocking
|
||||
|
||||
Geo-blocking provides several important security and compliance advantages:
|
||||
|
||||
### Security Benefits
|
||||
- **Reduce Attack Surface**: Block access from regions with high levels of malicious activity or where you don't expect legitimate users
|
||||
- **Prevent Unauthorized Access**: Limit exposure to threat actors operating from specific geographic locations
|
||||
- **Compliance Requirements**: Meet regulatory requirements that restrict data access based on geographic location
|
||||
- **Resource Protection**: Prevent unnecessary load on your services from regions where you don't operate
|
||||
|
||||
## Implementing Geo-blocking with Bypass Rules
|
||||
|
||||
Geo-blocking in Pangolin is implemented using [bypass rules](/manage/access-control/rules) with country-based matching. You can create rules that either allow or deny access based on the visitor's country. To apply the same geo-blocking rules to multiple public resources, define them in a [resource policy](/manage/resources/public/resource-policies) and attach that policy to each resource.
|
||||
|
||||
<Frame caption="Screenshot of resources rules from the Pangolin Dashboard.">
|
||||
<img src="/images/country_rules.png" alt="Pangolin Dashboard"/>
|
||||
</Frame>
|
||||
|
||||
### Setting Up Geo-blocking Rules
|
||||
|
||||
1. Navigate to your target resource and select the **Rules** tab
|
||||
2. Create a new rule and select **Country** as the match type
|
||||
3. Choose your rule action:
|
||||
- **Allow**: Bypass authentication for users from specific countries
|
||||
- **Deny**: Block all access from specific countries
|
||||
- **Pass to Auth**: Let users from specific countries proceed to authentication
|
||||
|
||||
### Common Geo-blocking Patterns
|
||||
|
||||
#### Allow Only Specific Countries
|
||||
Create a "Deny" rule that blocks all countries except those you want to allow:
|
||||
|
||||
1. Create a **Deny** rule
|
||||
2. Select **Country** match type
|
||||
3. Choose "ALL" to match all countries
|
||||
4. Add priority: 100 (lower priority)
|
||||
|
||||
Then create specific allow rules for your approved countries:
|
||||
|
||||
1. Create **Allow** rules for each approved country
|
||||
2. Set higher priority (e.g., 10, 20, 30) so they process first
|
||||
|
||||
#### Block Specific High-Risk Countries
|
||||
Create targeted deny rules for specific countries while allowing all others:
|
||||
|
||||
1. Create **Deny** rules for each country you want to block
|
||||
2. Select the specific countries from the dropdown
|
||||
3. Set appropriate priorities
|
||||
|
||||
#### Regional Access Control
|
||||
Combine geo-blocking with other rule types for sophisticated access control:
|
||||
|
||||
1. **Path + Country**: Block admin paths (`/admin/*`) from all countries except your headquarters
|
||||
2. **IP + Country**: Allow specific IPs from restricted countries (for VPN users or partners)
|
||||
3. **CIDR + Country**: Combine network-based and geography-based restrictions
|
||||
|
||||
### Best Practices
|
||||
|
||||
<Warning>
|
||||
IP geolocation is not always 100% accurate. Users with VPNs, proxies, or mobile networks may appear to be from different countries than expected.
|
||||
</Warning>
|
||||
|
||||
### Rule Priority Example
|
||||
|
||||
```
|
||||
Priority 1: Allow - Country: United States
|
||||
Priority 2: Allow - Country: Canada
|
||||
Priority 3: Allow - Country: United Kingdom
|
||||
Priority 4: Deny - Country: ALL
|
||||
```
|
||||
|
||||
This configuration allows access only from the US, Canada, and UK while blocking all other countries.
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: "Add Identity Providers"
|
||||
description: "Configure external identity providers for user authentication to resources and the organization"
|
||||
---
|
||||
|
||||
Identity providers allow your users to log into Pangolin and Pangolin resources using their existing accounts from external identity systems like Google, Microsoft Azure, or Okta. Instead of creating separate Pangolin accounts, users can authenticate with their familiar work or personal credentials.
|
||||
|
||||
Here is an example using Microsoft Azure Entra ID as SSO for Pangolin:
|
||||
|
||||
**This feature is for you if:**
|
||||
- Your organization already uses an identity provider like Google Workspace, Microsoft Entra ID, Okta, or similar systems
|
||||
- You want to centralize user management and avoid maintaining separate Pangolin accounts
|
||||
- You need to control who can access Pangolin resources through your existing user directory
|
||||
- You want users to access Pangolin using their existing credentials without creating new passwords
|
||||
|
||||
<Note>
|
||||
Assigning more than one role per user is only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
<Frame>
|
||||
<img src="/images/create-idp.png" alt="Identity provider creation form in the Pangolin dashboard" />
|
||||
</Frame>
|
||||
|
||||
## Identity Provider Types
|
||||
|
||||
### Organization Identity Providers
|
||||
|
||||
Organization identity providers are configured per organization and only apply to that specific organization. Each org can have its own identity providers, allowing for authentication methods based on the organization's needs.
|
||||
|
||||
Organization only identity providers appear only on the organization login page.
|
||||
|
||||
<Note>
|
||||
Available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) and [Enterprise Edition](/self-host/enterprise-edition). For [Enterprise Edition](/self-host/enterprise-edition), you must set `app.identity_provider_mode: "org"` in the [private config file](/self-host/advanced/private-config-file#param-identity-provider-mode) `privateConfig.yml`.
|
||||
</Note>
|
||||
|
||||
#### Sharing an identity provider across organizations
|
||||
|
||||
To share an organization-only identity provider across more than one organization, use the import feature. On the Identity Providers table in your organization, click Add Identity Provider and choose Import. You will see identity providers from other organizations where you are an administrator; you can associate another organization’s IdP with the current organization. Configure [auto provisioning](/manage/identity-providers/auto-provisioning) settings separately for each organization, since each organization has its own roles.
|
||||
|
||||
### Global Identity Providers
|
||||
|
||||
Global identity providers are managed at the server level and not the individual organization. They can apply to all or some organizations on the server. This means you must define policies per organization to map users to specific organizations and roles within those organizations.
|
||||
|
||||
Global identity providers appear on the global server login page.
|
||||
|
||||
<Tip>
|
||||
Global identity providers are the only supported method in Pangolin Community.
|
||||
</Tip>
|
||||
|
||||
## Supported Identity Providers
|
||||
|
||||
### OAuth2/OIDC
|
||||
|
||||
This can be used to connect to any external identity provider that supports the OpenID Connect protocol such as:
|
||||
|
||||
- Authentik
|
||||
- Keycloak
|
||||
- Okta
|
||||
- Other OIDC-compliant providers
|
||||
|
||||
### Google
|
||||
|
||||
<Note>
|
||||
Google IdP is only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
Easily set up Google Workspace authentication for your organization. Users can sign in with their Google accounts and access Pangolin resources using their existing Google credentials. Perfect for organizations already using Google Workspace for email, calendar, and other services.
|
||||
|
||||
### Azure Entra ID
|
||||
|
||||
<Note>
|
||||
Azure Entra ID IdP is only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
Integrate with Microsoft's enterprise identity platform to allow users to authenticate using their Azure Active Directory accounts. Ideal for organizations using Microsoft 365 or other Azure services, providing seamless single sign-on across your Microsoft ecosystem.
|
||||
|
||||
## How to Add an Identity Provider
|
||||
|
||||
<Note>
|
||||
When using global IDPs, identity providers are created and managed via the Server Admin UI rather than the organization settings.
|
||||
</Note>
|
||||
|
||||
<Steps>
|
||||
<Step title="Navigate to Identity Providers">
|
||||
In the Pangolin organization, select the "Identity Providers" section in the sidebar.
|
||||
</Step>
|
||||
|
||||
<Step title="Add New Provider">
|
||||
Click on the "Add Identity Provider" button.
|
||||
</Step>
|
||||
|
||||
<Step title="Select Type">
|
||||
Select the type of identity provider you want to add (OAuth2/OIDC, Google, Azure Entra ID).
|
||||
</Step>
|
||||
|
||||
<Step title="Set up Auto Provisioning (Optional)">
|
||||
Select the "Auto Provision Users" checkbox to automatically provision users and assign roles in Pangolin when they log in using an external identity provider. See [Auto Provision](/manage/identity-providers/auto-provisioning) for more information.
|
||||
|
||||
If this is disabled, you will need to pre-provision a user in Pangolin before they can log in using an external identity provider. Pre-provision means creating a user in Pangolin with a role and organization before they log in using an external identity provider.
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Settings">
|
||||
Fill in the required fields for the selected identity provider type.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Custom Login Page
|
||||
|
||||
You can [configure a custom login page](/manage/access-control/login-page) for your organization to be served at a domain of your choice. The log in page for every resource will be served at this URL. Additionally, you can visit this url to log in to the organization itself to access the Pangolin dashboard. This is particularly useful for identity providers because it creates a place for your users to go to select the identity provider of choice to access the Pangolin dashboard.
|
||||
|
||||
## Auto Provisioning
|
||||
|
||||
See [Auto Provision](/manage/identity-providers/auto-provisioning) for more information on how to automatically provision users and assign orgs and roles in Pangolin when they log in using an external identity provider.
|
||||
@@ -0,0 +1,205 @@
|
||||
---
|
||||
title: "Auto Provisioning"
|
||||
description: "Automatically create and manage user accounts from external identity providers"
|
||||
---
|
||||
|
||||
Auto provisioning is a feature that allows you to automatically create and manage user accounts in Pangolin when they log in using an external identity provider rather than pre-provisioning (manually creating) a user with a role and organization. This is useful for organizations that want to streamline the onboarding process for new users and ensure that their user accounts are always up-to-date.
|
||||
|
||||
You will be able to programmatically decide the roles and organizations for new users based on the information provided by the identity provider.
|
||||
|
||||
## Enable Auto Provision
|
||||
|
||||
Toggle the "Auth Provision Users" switch when creating or editing an identity provider.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/mapping-builder.png" alt="Auto provision users setting in the Pangolin dashboard" />
|
||||
</Frame>
|
||||
|
||||
## What if Auto Provisioning is Disabled?
|
||||
|
||||
If auto provision is disabled, organization admins will need to manually create the user accounts and select the role for each user. When creating a user, you can select the identity provider that the user will be associated with. A user will not be able to log in using the identity provider if a user is not pre-provisioned in the system.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/create-idp-user.png" alt="Creating an identity provider user in the Pangolin dashboard" />
|
||||
</Frame>
|
||||
|
||||
## Role Mappings
|
||||
|
||||
When you configure role mappings in auto provisioning settings, you use one of three approaches: fixed roles, mapping builder, or raw expression. These options are available for global identity providers and for organization-only identity providers.
|
||||
|
||||
<Note>
|
||||
Auto provisioning does not create roles in Pangolin. Every role you assign whether you pick fixed roles, map IdP values in the builder, or return names from a raw expression must already exist in the target organization, and the name you use must match that role’s name exactly (character-for-character). This one-to-one name match applies to all three mapping types. If a name does not match an existing role, the user will not receive that role (and may not be added to the organization, depending on your setup).
|
||||
</Note>
|
||||
|
||||
### Role Mapping: Fixed Roles
|
||||
|
||||
Fixed roles is the simplest option. Every user who signs in through the identity provider receives the same set of roles. The roles you select must already exist in Pangolin, and you must choose them by their exact names in that organization. Use this when you do not need dynamic mapping and a single role assignment for everyone is enough. You can still change roles on individual users after they have been auto-provisioned. This is the easiest way to get started.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/fixed-roles.png" alt="Fixed roles mapping option in the Pangolin dashboard" />
|
||||
</Frame>
|
||||
|
||||
### Role Mapping: Mapping Builder
|
||||
|
||||
The mapping builder lets you map roles from your identity provider to Pangolin roles without writing expressions. For example, a user might sign in from Azure and belong to several groups there. Azure identifies those groups with its own internal ID strings. With the mapping builder, you translate those IDs to Pangolin role names in the UI.
|
||||
|
||||
First, choose the claim in the OIDC token where roles or groups are provided such as `groups`. Then define a one-to-one mapping for each role: on one side, the role or group ID from the identity provider; on the other, the Pangolin role name that already exists in the organization. The Pangolin side must match that role’s name exactly (same spelling, spacing, and casing).
|
||||
|
||||
<Frame>
|
||||
<img src="/images/mapping-builder.png" alt="Role mapping builder in the Pangolin dashboard" />
|
||||
</Frame>
|
||||
|
||||
### Role Mapping: Raw Expression
|
||||
|
||||
Raw expression is the most flexible option and the most complex. It matches how many users previously defined mappings in Pangolin. You provide a [JMESPath](https://jmespath.org/) expression that must evaluate to a string or array of strings. Each value must be the exact name of a role that already exists in the organization. If you can express the logic in JMESPath, it will work (for example, combining conditions on name, email, and other claims).
|
||||
|
||||
The expression is evaluated against the token from the identity provider on each login. Results are matched per organization:
|
||||
|
||||
- Each returned string must be the exact name of a role that already exists in that organization (same rules as fixed roles and the mapping builder).
|
||||
- If no matching role is found for the resolved names, the user is not added to the organization.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/raw-expression.png" alt="Raw expression role mapping in the Pangolin dashboard" />
|
||||
</Frame>
|
||||
|
||||
#### Raw Expression Example: JMESPath role selection
|
||||
|
||||
This expression returns `"Admin"` when the user is in the `admin` group, and `"Member"` otherwise.
|
||||
|
||||
**Expression:**
|
||||
|
||||
<Note>
|
||||
When entering a string literal in JMESPath, surround it with `'` (single quotes), as in the example below.
|
||||
</Note>
|
||||
|
||||
```
|
||||
contains(groups, 'admin') && 'Admin' || 'Member'
|
||||
```
|
||||
|
||||
**Identity provider token (excerpt):**
|
||||
```json
|
||||
{
|
||||
...
|
||||
"sub": "9590c3bfccd1b1a54b35845fb1bb950057dfa50fba43cb8bada58b462c80e207",
|
||||
"aud": "JJoSvHCZcxnXT2sn6CObj6a21MuKNRXs3kN5wbys",
|
||||
"exp": 1745790819,
|
||||
"iat": 1745789019,
|
||||
"auth_time": 1745789019,
|
||||
"email": "user@example.com",
|
||||
"email_verified": true,
|
||||
"name": "Example User",
|
||||
"groups": [
|
||||
"home-lab",
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Organization Mapping
|
||||
|
||||
Use this when you want to conditionally evaluate if the user should be added to an organization based on the identity provider data. For example, you can add users to an organization based on their email domain or if they are a member of a specific group.
|
||||
|
||||
This is different from the role mapping options because it is not based on the roles assigned to the user, but rather on the organization they should be added to.
|
||||
|
||||
The expression will be matched against each organization. Meaning:
|
||||
|
||||
- The result of the expression must return true or the organization ID as it is defined in the system.
|
||||
- If no matching organization is found, the user will not be added to the organization.
|
||||
|
||||
You can insert the template variable `{{orgId}}` in the expression. This will be replaced with the organization ID when the expression is evaluated.
|
||||
|
||||
#### Example: Email-based Selection
|
||||
|
||||
<Note>
|
||||
When entering a string literal in JMESPath, surround it with `'` (single quotes).
|
||||
</Note>
|
||||
|
||||
This example will return true since the user's email ends with @acme.com. Use this if you want to add users to an organization based on their email domain.
|
||||
|
||||
**Expression:**
|
||||
```
|
||||
ends_with(email, '@acme.com')
|
||||
```
|
||||
|
||||
**Identity Provider Data:**
|
||||
```json
|
||||
{
|
||||
...
|
||||
"sub": "9590c3bfccd1b1a54b35845fb1bb950057dfa50fba43cb8bada58b462c80e207",
|
||||
"aud": "JJoSvHCZcxnXT2sn6CObj6a21MuKNRXs3kN5wbys",
|
||||
"exp": 1745790819,
|
||||
"iat": 1745789019,
|
||||
"auth_time": 1745789019,
|
||||
"email": "user@acme.com",
|
||||
"email_verified": true,
|
||||
"name": "Example User",
|
||||
"groups": [
|
||||
"home-lab",
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Global Identity Providers
|
||||
|
||||
After you create a global IdP, on the edit page you can manage organization policies from the Auto Provisioning tab. You can set a default (fallback) role mapping and organization rules, and you can add per-organization mappings that override or extend behavior for specific organizations. The fixed roles, mapping builder, and raw expression options apply both here (default and per org) and in organization-only identity providers.
|
||||
|
||||
### How Organization Policies Are Evaluated
|
||||
|
||||
It is helpful to think of the auto provisioning process as follows:
|
||||
|
||||
<Steps>
|
||||
<Step title="User Login">
|
||||
User successfully logs in using an identity provider.
|
||||
</Step>
|
||||
|
||||
<Step title="Account Creation">
|
||||
Pangolin creates a user account for the user.
|
||||
</Step>
|
||||
|
||||
<Step title="Organization Evaluation">
|
||||
Pangolin loops through each organization and evaluates the JMESPath expression for organization membership. If the expression does not return true or the same ID as the current organization, the user will not be added to the organization.
|
||||
</Step>
|
||||
|
||||
<Step title="Role Assignment">
|
||||
For each organization, Pangolin applies the configured role mapping (fixed roles, mapping builder, or raw expression). Resolved names must match existing Pangolin roles exactly; otherwise those assignments do not apply, and the user may not be added to the organization.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Role mappings for global IdPs
|
||||
|
||||
Use a default policy, per-organization policies, or both. Role mapping options (fixed roles, mapping builder, raw expression) work the same way as described in [Role mappings](#role-mappings).
|
||||
|
||||
### Default (Fallback) Policy
|
||||
|
||||
You can optionally configure a default policy for all organizations. This will be used if the organization does not have its own policy configured.
|
||||
|
||||
This example will always return 'home-lab' meaning the user will always be added to the "home-lab" organization.
|
||||
|
||||
### Example: Dynamic Organization Selection with Interpolation
|
||||
|
||||
When Pangolin evaluates this expression against the "home-lab" organization, it will replace `{{orgId}}` with "home-lab". The result of the expression will return true since the user is a member of the "home-lab" group.
|
||||
|
||||
**Expression:**
|
||||
```
|
||||
contains(groups, '{{orgId}}')
|
||||
```
|
||||
|
||||
**Identity Provider Data:**
|
||||
```json
|
||||
{
|
||||
...
|
||||
"sub": "9590c3bfccd1b1a54b35845fb1bb950057dfa50fba43cb8bada58b462c80e207",
|
||||
"aud": "JJoSvHCZcxnXT2sn6CObj6a21MuKNRXs3kN5wbys",
|
||||
"exp": 1745790819,
|
||||
"iat": 1745789019,
|
||||
"auth_time": 1745789019,
|
||||
"email": "user@example.com",
|
||||
"email_verified": true,
|
||||
"name": "Example User",
|
||||
"groups": [
|
||||
"home-lab",
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: "Azure Entra ID"
|
||||
description: "Configure Azure Entra ID Single Sign-On"
|
||||
---
|
||||
<Note>
|
||||
Azure SSO is only available on [Pangolin Cloud](https://app.pangolin.net/auth/signup) and [Enterprise Edition](/self-host/enterprise-edition) deployments. In [Enterprise Edition](/self-host/enterprise-edition), you must set `app.identity_provider_mode: "org"` in your [private config file](/self-host/advanced/private-config-file) `privateConfig.yml`.
|
||||
</Note>
|
||||
|
||||
The following steps will integrate Microsoft SSO using the built in Azure Entra ID identity provider in Pangolin.
|
||||
|
||||
<Accordion title="How to create and set up an App Registration in Microsoft Azure from scratch">
|
||||
|
||||
#### Create an App Registration
|
||||
|
||||
In Azure, go to "Microsoft Entra ID". Under "Manage", click "App registrations". On the "All applications" tab, select "Register an application".
|
||||
|
||||
Give it a name like "Pangolin", select your preferred supported account types, and click "Register". Leave the redirect URI blank for now; we will come back to this.
|
||||
|
||||
#### Copy Credentials
|
||||
|
||||
On the new app registration, select the "Overview" tab. Here, you can copy the "Application (client) ID" and save for later.
|
||||
|
||||
Now we need to generate the client secret. Click "Add a certificate or secret". Then click "New client secret". Enter a description like "Pangolin credentials" and choose an expiration time. Note that once this secret expires, you will need to generate a new one and replace it in the Pangolin dashboard for the associated IdP.
|
||||
|
||||
Copy the "Value" field and save for later.
|
||||
|
||||
<Note>
|
||||
We will revisit the **Authorised redirect URIs** field later, as we do not have Pangolin set up for Azure yet.
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
|
||||
## Creating an Azure Entra ID IdP in Pangolin
|
||||
|
||||
In Pangolin, go to "Identity Providers" and click "Add Identity Provider". Select the Azure Entra ID provider option.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/create-azure-idp.png" alt="Azure Entra ID identity provider setup in the Pangolin dashboard" />
|
||||
</Frame>
|
||||
|
||||
In the OAuth2/OIDC Configuration, you'll need the following fields:
|
||||
|
||||
<ResponseField name="Client ID" type="string" required>
|
||||
The application (client) ID from the "Overview" section of your app registration
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="Client Secret" type="string" required>
|
||||
The client secret value from the "Certificates and secrets" section of your app registration
|
||||
</ResponseField>
|
||||
|
||||
## Token Configuration
|
||||
|
||||
When you're done, click "Create Identity Provider". Then, copy the Redirect URL in the "General" tab as you will now need this for your app registration.
|
||||
|
||||
## Returning to Azure
|
||||
|
||||
Lastly, you'll need to return to your app registration in order to add the redirect URI created by Pangolin. On the "Overview" tab, click "Add a Redirect URI". The click "Add a platform", and select "Web". Here, you can add the redirect URL from Pangolin and click "Configure". Your configuration should now be complete. You'll now need to add an external user to Pangolin, or if you have "Auto Provision Users" enabled, you can now log in using Azure SSO.
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: "Google"
|
||||
description: "Configure Google Single Sign-On"
|
||||
---
|
||||
<Note>
|
||||
Google SSO is only available on [Pangolin Cloud](https://app.pangolin.net/auth/signup) and [Enterprise Edition](/self-host/enterprise-edition) deployments. In [Enterprise Edition](/self-host/enterprise-edition), you must set `app.identity_provider_mode: "org"` in your [private config file](/self-host/advanced/private-config-file#param-use-org-only-idp) `privateConfig.yml`.
|
||||
</Note>
|
||||
|
||||
The following steps will integrate Google SSO using the built in Google identity provider in Pangolin.
|
||||
|
||||
<Accordion title="How to create and set up a Google Project from scratch">
|
||||
|
||||
[Create a new Project](https://console.cloud.google.com/projectcreate), or use an [existing Project](https://console.developers.google.com/) you've already created in the Google Developers Console. Setting the organization isn't required, unless you intend to use SSO for [more than 100 users](https://support.google.com/cloud/answer/13464323) externally (not via Google Workspace).
|
||||
|
||||
Once created, or you've opened an existing Project, you may be on the project dashboard, where you will need to open the sidebar. If you are on the welcome page, continue by selecting [OAuth consent screen](https://console.cloud.google.com/auth/overview) in "APIs and services".
|
||||
|
||||
You should see that Google Auth Platform is not configured. Press "Get started" and fill in the relevant information, such as your "App name" and "User support email". These will be visible when the user is authenticating.
|
||||
|
||||
After continuing, you can select an "Audience". If you are using Pangolin for friends and family, use the "External" Audience. You can only have 100 users authenticated with a "Testing" status.
|
||||
|
||||
<Note>
|
||||
Depending on your use case, you may want to use the "Internal" Audience if you are utilising Google Workspace SSO.
|
||||
</Note>
|
||||
|
||||
Once completed, you will then need to open the [Branding](https://console.cloud.google.com/auth/branding) tab.
|
||||
|
||||
Locate "Authorized domains", then press "Add domain" to add an authorized domain. You'll need to authorize the top private (root) domain here, such as `example.com`. Your SSO *may* function without an authorized domain, though setting this field should guarantee functionality.
|
||||
|
||||
### Creating an OAuth client ID in your Project
|
||||
|
||||
Go to the [Clients](https://console.cloud.google.com/auth/clients) tab, and click "Create client" below the top bar.
|
||||
|
||||
For "Application type", select `Web application`. Any "Name" can be set. Leave "Authorised JavaScript origins" and "Authorised redirect URIs" empty.
|
||||
|
||||
<Note>
|
||||
We will revisit the "Authorised redirect URIs" field later, as we do not have Pangolin set up for Google yet.
|
||||
</Note>
|
||||
|
||||
After hitting "Create", you will be able to see the "Client ID" and "Client secret", you may want to copy these somewhere as these will be needed momentarily, though they will still be accessible in the future.
|
||||
</Accordion>
|
||||
|
||||
## Creating a Google IdP in Pangolin
|
||||
|
||||
In Pangolin, go to "Identity Providers" and click "Add Identity Provider". Select the Google provider option.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/create-google-idp.png" alt="Google identity provider setup in the Pangolin dashboard" />
|
||||
</Frame>
|
||||
|
||||
In the "Google Configuration", you'll need the following fields:
|
||||
|
||||
<ResponseField name="Client ID" type="string" required>
|
||||
The Client ID from your Web application client.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="Client Secret" type="string" required>
|
||||
The Client secret from your Web application client.
|
||||
</ResponseField>
|
||||
|
||||
## Token Configuration
|
||||
|
||||
When you're done, click "Create Identity Provider". Then, copy the Redirect URL in the "General" tab as you will now need this for your **Web application client**.
|
||||
|
||||
## Returning to Google Developers Console
|
||||
|
||||
Lastly, you'll need to return to your "Web application client" in order to add the redirect URI created by Pangolin. Add the URI to "Authorized redirect URIs", then hit "Save"! Your configuration should now be complete. You'll now need to add an external user to Pangolin, or if you have "Auto Provision Users" enabled, you can now log in using Google SSO.
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: "OAuth2/OIDC"
|
||||
description: "Configure OpenID Connect identity provider for external authentication"
|
||||
---
|
||||
This identity provider follows the OpenID Connect protocol. This means that it can be used to connect to any external identity provider that supports the OpenID Connect protocol such as Authentik, Keycloak, Okta, etc.
|
||||
|
||||
## Creating a Generic OAuth2/OIDC IdP in Pangolin
|
||||
|
||||
In Pangolin, go to "Identity Providers" and click "Add Identity Provider". Select the OAuth2/OIDC provider option.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/create-oidc-idp.png" alt="Pangolin dashboard form for adding an OAuth2/OIDC identity provider" />
|
||||
</Frame>
|
||||
|
||||
In the OAuth2/OIDC Configuration, you'll need the following fields:
|
||||
|
||||
<ResponseField name="Client ID" type="string" required>
|
||||
The client identifier provided by your identity provider.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="Client Secret" type="string" required>
|
||||
The client secret provided by your identity provider.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="Authorization URL" type="string" required>
|
||||
The authorization endpoint URL from your identity provider.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="Token URL" type="string" required>
|
||||
The token endpoint URL from your identity provider.
|
||||
</ResponseField>
|
||||
|
||||
## Token Configuration
|
||||
|
||||
Use JMESPath to select attributes from the claims token. See [JMESPath](https://jmespath.org/) for more information on how to use JMESPath.
|
||||
|
||||
Determine how to access information from the claims token returned by the identity provider. This is used to map the user information from the identity provider to the user information in Pangolin.
|
||||
|
||||
<ResponseField name="Identifier Path" type="string" required>
|
||||
This must be unique for each user within an identity provider.
|
||||
|
||||
**Example**: `sub` or `user_id`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="Email Path" type="string">
|
||||
Path to the user's email address in the claims token.
|
||||
|
||||
**Example**: `email`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="Name Path" type="string">
|
||||
Path to the user's display name in the claims token.
|
||||
|
||||
**Example**: `name` or `preferred_username`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="Scopes" type="string">
|
||||
The scopes to request from the identity provider (not JMESPath; must be space-delimited strings).
|
||||
|
||||
**Default**: `openid profile email`
|
||||
|
||||
<Note>
|
||||
Generally, `openid profile email` is sufficient for most use cases.
|
||||
</Note>
|
||||
</ResponseField>
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: "Pocket ID"
|
||||
description: "Configure Pocket ID Single Sign-On using OpenID Connect"
|
||||
---
|
||||
The following steps will integrate Pocket ID with Pangolin SSO using OpenID Connect (OIDC).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you can start, you'll need to have Pocket ID accessible and ensure it's not secured with Pangolin SSO.
|
||||
|
||||
### Creating an OIDC Client in Pocket ID
|
||||
|
||||
In Pocket ID, create a new OIDC Client.
|
||||
|
||||
<Steps>
|
||||
<Step title="Set Name">
|
||||
Set the name to something memorable (eg. Pangolin).
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Callback URL">
|
||||
Leave blank or set a placeholder. We will come back to this step after creating the IdP and we know the redirect URL.
|
||||
</Step>
|
||||
|
||||
<Step title="Keep Defaults">
|
||||
All other values can be kept as default.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Note>
|
||||
The callback URL is displayed in the IdP settings after you create the IdP in Pangolin.
|
||||
</Note>
|
||||
|
||||
After you have created the OIDC Client, take note of the following fields from the top of the page (click "Show more details" to see all of them):
|
||||
|
||||
- **Client ID**
|
||||
- **Client secret**
|
||||
- **Authorization URL**
|
||||
- **Token URL**
|
||||
|
||||
## Configuring Identity Providers in Pangolin
|
||||
|
||||
In Pangolin, go to “Identity Providers” and click “Add Identity Provider”. Select the OAuth2/OIDC provider option.
|
||||
|
||||
"Name" should be set to something memorable (eg. Pocket ID). The "Provider Type" should be set to the default `OAuth2/OIDC`.
|
||||
|
||||
### OAuth2/OIDC Configuration (Provider Credentials and Endpoints)
|
||||
|
||||
In the OAuth2/OIDC Configuration, you'll need the following fields:
|
||||
|
||||
<ResponseField name="Client ID" type="string" required>
|
||||
The Client ID from your Pocket ID OIDC client.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="Client Secret" type="string" required>
|
||||
The Client secret from your Pocket ID OIDC client.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="Authorization URL" type="string" required>
|
||||
The Authorization URL from your Pocket ID OIDC client.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="Token URL" type="string" required>
|
||||
The Token URL from your Pocket ID OIDC client.
|
||||
</ResponseField>
|
||||
|
||||
## Token Configuration
|
||||
|
||||
You should leave all of the paths default. In the "Scopes" field, add `openid profile email`.
|
||||
|
||||
<Note>
|
||||
Set the "Identifier Path" to `preferred_username` for Pocket ID integration.
|
||||
</Note>
|
||||
|
||||
When you're done, click "Create Identity Provider"! Then, copy the Redirect URL in the "General" tab as you will now need this for your Pocket ID OIDC client.
|
||||
|
||||
## Returning to Pocket ID
|
||||
|
||||
Lastly, you'll need to return to your Pocket ID OIDC client in order to add the redirect URI created by Pangolin. Add the URI to "Callback URLs", then save your changes! Your configuration should now be complete. You'll now need to add an external user to Pangolin, or if you have "Auto Provision Users" enabled, you can now log in using Pocket ID SSO.
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: "Zitadel"
|
||||
description: "Configure Zitadel Single Sign-On using OpenID Connect"
|
||||
---
|
||||
The following steps will integrate Zitadel with Pangolin SSO using OpenID Connect (OIDC).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
These instructions assume you have a working Zitadel organization and project setup already.
|
||||
|
||||
### Creating an Application in Zitadel
|
||||
|
||||
You need to configure an application in Zitadel:
|
||||
|
||||
<Steps>
|
||||
<Step title="Create New Application">
|
||||
Open an existing project and in `Applications` click `New`.
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Application">
|
||||
Set the name to something memorable (eg. Pangolin).
|
||||
</Step>
|
||||
|
||||
<Step title="Set Application Type">
|
||||
For `Type of application` choose `Web`.
|
||||
</Step>
|
||||
|
||||
<Step title="Set Authentication Method">
|
||||
For `Authentication Method` choose `Code`.
|
||||
</Step>
|
||||
|
||||
<Step title="Leave Redirect URIs Blank">
|
||||
Leave `Redirect URIs` blank for now. We'll come back to this once the IdP is created.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Note>
|
||||
When you click create, you'll be shown the `ClientSecret` and `ClientId`. Make sure to save these somewhere secure - you won't be able to see the Client Secret again.
|
||||
</Note>
|
||||
|
||||
<Steps>
|
||||
<Step title="Configure Token Settings">
|
||||
Click `Token settings` then change `Auth Token Type` to `JWT` and check the `User Info inside ID Token` box finally hit `Save`.
|
||||
</Step>
|
||||
|
||||
<Step title="Enable User Claims in ID Token">
|
||||
Still in `Token settings`, check the box for `Include user's roles in the ID Token`. This enables Zitadel to include necessary user profile claims (including `preferred_username`) in the ID token, which Pangolin requires for user identification.
|
||||
</Step>
|
||||
|
||||
<Step title="Note Endpoints">
|
||||
Open `URLs` and make note of:
|
||||
- `Authorization Endpoint`
|
||||
- `Token Endpoint`
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Configuring Identity Providers in Pangolin
|
||||
|
||||
In Pangolin, go to “Identity Providers” and click “Add Identity Provider”. Select the OAuth2/OIDC provider option.
|
||||
|
||||
"Name" should be set to something memorable (eg. Zitadel). The "Provider Type" should be set to the default `OAuth2/OIDC`.
|
||||
|
||||
### OAuth2/OIDC Configuration (Provider Credentials and Endpoints)
|
||||
|
||||
In the OAuth2/OIDC Configuration, you'll need the following fields:
|
||||
|
||||
<ResponseField name="Client ID" type="string" required>
|
||||
The Client ID from your Zitadel application.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="Client Secret" type="string" required>
|
||||
The Client Secret from your Zitadel application.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="Authorization URL" type="string" required>
|
||||
Use the `Authorization Endpoint` from your Zitadel application.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="Token URL" type="string" required>
|
||||
Use the `Token Endpoint` from your Zitadel application.
|
||||
</ResponseField>
|
||||
|
||||
## Token Configuration
|
||||
|
||||
You should leave all of the paths default. In the "Scopes" field, add `openid profile email`.
|
||||
|
||||
<Note>
|
||||
Set the "Identifier Path" to `preferred_username` for Zitadel integration.
|
||||
</Note>
|
||||
|
||||
When you're done, click "Create Identity Provider"! Then, copy the Redirect URL in the "General" tab as you will now need this for your **Zitadel application**.
|
||||
|
||||
## Returning to Zitadel
|
||||
|
||||
Lastly, you need to edit your `Redirect Settings` in your Zitadel application. Add the URL you copied to the `Redirect URIs`, then hit the `+` button and finally `Save`. Your configuration should now be complete. You'll now need to add an external user] to Pangolin, or if you have "Auto Provision Users" enabled, you can now log in using Zitadel SSO.
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: "Integration API"
|
||||
icon: "cube"
|
||||
description: "Learn how to use Pangolin's REST API to automate and script operations with fine-grained permissions"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
Pangolin is in heavy development. The REST API routes and behavior may include breaking changes between updates. We will do our best to document large changes.
|
||||
</Warning>
|
||||
|
||||
The API is REST-based and supports many operations available through the web interface. Authentication uses Bearer tokens, and you can create multiple API keys with specific permissions for different use cases.
|
||||
|
||||
For self-hosted editions, the integration API must be enabled. Check out [the documentation](/self-host/advanced/integration-api) for how to enable the integration API.
|
||||
|
||||
## Authentication
|
||||
|
||||
All API requests require authentication using a Bearer token in the Authorization header:
|
||||
|
||||
```bash tab="cURL"
|
||||
curl -H "Authorization: Bearer YOUR_API_KEY" \
|
||||
https://api.example.com/v1/
|
||||
```
|
||||
|
||||
```javascript tab="JavaScript"
|
||||
const response = await fetch('https://api.example.com/v1/endpoint', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${apiKey}`
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
```python tab="Python"
|
||||
import requests
|
||||
|
||||
headers = {'Authorization': f'Bearer {api_key}'}
|
||||
response = requests.get('https://api.example.com/v1/endpoint', headers=headers)
|
||||
```
|
||||
|
||||
## API Key Types
|
||||
|
||||
Pangolin supports two types of API keys with different permission levels:
|
||||
|
||||
### Organization API Keys
|
||||
|
||||
Organization API keys are created by organization admins and have limited scope to perform actions only in that organization.
|
||||
|
||||
### Root API Keys
|
||||
|
||||
Root API keys have some extra permissions and can execute operations across orgs. They are only available in the fully self-hosted editions of Pangolin:
|
||||
|
||||
<Warning>
|
||||
Root API keys have elevated permissions and should be used carefully. Only create them when you need server-wide access.
|
||||
</Warning>
|
||||
|
||||
## Creating API Keys
|
||||
|
||||
<Steps>
|
||||
<Step title="Access the admin panel">
|
||||
Navigate to your admin panel:
|
||||
- **Organization keys**: Organization → API Keys
|
||||
- **Root keys**: Server Admin → API Keys (self-hosted only)
|
||||
</Step>
|
||||
|
||||
<Step title="Generate a new key">
|
||||
Click "Create API Key" and provide a descriptive name for the key.
|
||||
</Step>
|
||||
|
||||
<Step title="Configure permissions">
|
||||
Select the specific permissions your API key needs from the permissions selector.
|
||||
|
||||
<Frame caption="API key permissions selector showing available operations">
|
||||
<img src="/images/permissions.png" alt="API Key Permissions"/>
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Copy and secure your key">
|
||||
Copy the generated API key immediately. It won't be shown again.
|
||||
|
||||
<Warning>
|
||||
Store API keys securely and never commit them to version control. Use environment variables or secure secret management.
|
||||
</Warning>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## API Documentation
|
||||
|
||||
For a minimal walkthrough of common flows (sites, resources, targets, assigning roles and users), see [Common API Routes](/manage/common-api-routes).
|
||||
|
||||
View the full Swagger docs here: [https://api.pangolin.net/v1/docs](https://api.pangolin.net/v1/docs).
|
||||
|
||||
Interactive API documentation is available through Swagger UI:
|
||||
|
||||
<Frame caption="Swagger UI showing API endpoints and interactive testing">
|
||||
<img src="/images/swagger.png" alt="Swagger Docs"/>
|
||||
</Frame>
|
||||
|
||||
For self-hosted Pangolin, access the documentation at `https://api.your-domain.com/v1/docs`.
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: "Integration API"
|
||||
description: "Learn how to use Pangolin's REST API to automate and script operations with fine-grained permissions"
|
||||
---
|
||||
The API is REST-based and supports many operations available through the web interface. Authentication uses Bearer tokens, and you can create multiple API keys with specific permissions for different use cases.
|
||||
|
||||
<Info>
|
||||
For Pangolin Community Edition, the integration API must be enabled. Check out [the documentation](/self-host/advanced/integration-api) for how to enable the integration API.
|
||||
</Info>
|
||||
|
||||
## Authentication
|
||||
|
||||
All API requests require authentication using a Bearer token in the Authorization header:
|
||||
|
||||
```bash tab="cURL"
|
||||
curl -H "Authorization: Bearer YOUR_API_KEY" \
|
||||
https://api.example.com/v1/
|
||||
```
|
||||
|
||||
```javascript tab="JavaScript"
|
||||
const response = await fetch('https://api.example.com/v1/endpoint', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${apiKey}`
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
```python tab="Python"
|
||||
import requests
|
||||
|
||||
headers = {'Authorization': f'Bearer {api_key}'}
|
||||
response = requests.get('https://api.example.com/v1/endpoint', headers=headers)
|
||||
```
|
||||
|
||||
## API Key Types
|
||||
|
||||
Pangolin supports two types of API keys with different permission levels:
|
||||
|
||||
### Organization API Keys
|
||||
|
||||
Organization API keys are created by organization admins and have limited scope to perform actions only in that organization.
|
||||
|
||||
### Root API Keys
|
||||
|
||||
Root API keys have some extra permissions and can execute operations across orgs. They are only available in the Community Edition Pangolin:
|
||||
|
||||
<Warning>
|
||||
Root API keys have elevated permissions and should be used carefully. Only create them when you need server-wide access.
|
||||
</Warning>
|
||||
|
||||
## Creating API Keys
|
||||
|
||||
<Steps>
|
||||
<Step title="Access the admin panel">
|
||||
Navigate to your admin panel:
|
||||
- **Organization keys**: Organization → API Keys
|
||||
- **Root keys**: Server Admin → API Keys (self-hosted only)
|
||||
</Step>
|
||||
|
||||
<Step title="Generate a new key">
|
||||
Click "Create API Key" and provide a descriptive name for the key.
|
||||
</Step>
|
||||
|
||||
<Step title="Configure permissions">
|
||||
Select the specific permissions your API key needs from the permissions selector.
|
||||
|
||||
<Frame caption="API key permissions selector showing available operations">
|
||||
<img src="/images/permissions.png" alt="API Key Permissions"/>
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Copy and secure your key">
|
||||
Copy the generated API key immediately. It won't be shown again.
|
||||
|
||||
<Warning>
|
||||
Store API keys securely and never commit them to version control. Use environment variables or secure secret management.
|
||||
</Warning>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## API Documentation
|
||||
|
||||
View the Swagger docs here: [https://api.pangolin.net/v1/docs](https://api.pangolin.net/v1/docs).
|
||||
|
||||
Interactive API documentation is available through Swagger UI:
|
||||
|
||||
<Frame caption="Swagger UI showing API endpoints and interactive testing">
|
||||
<img src="/images/swagger.png" alt="Swagger Docs"/>
|
||||
</Frame>
|
||||
|
||||
For self-hosted Pangolin, access the documentation at `https://api.your-domain.com/v1/docs`.
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: "Labels"
|
||||
description: "Attach reusable string labels to sites, clients, and resources for metadata, search, and filtering"
|
||||
---
|
||||
|
||||
<Note>
|
||||
Only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) and [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
Labels are string-based values you can assign to sites, machine clients, and resources. Use them to attach metadata, group related entities, and quickly find what you need across your organization.
|
||||
|
||||
## Supported Entities
|
||||
|
||||
You can attach labels to:
|
||||
|
||||
- **Sites**
|
||||
- **Machine clients**
|
||||
- **Resources**
|
||||
|
||||
Labels are shared across all three entity types. The same label can be applied to a site, a client, and a resource in the same organization. There is no limit to how many labels you can attach to a single entity.
|
||||
|
||||
## Organization Label Store
|
||||
|
||||
Each organization maintains a central store of all labels. The organization-wide labels page shows every label available in that organization in one place, so you can see what is in use before assigning labels to new entities.
|
||||
|
||||
## Search and Filtering
|
||||
|
||||
Once a label is attached to an entity, that entity becomes searchable by the label string. You can also filter by label in the table view for sites, clients, and resources.
|
||||
|
||||
For example, filtering resources by `warehouse-1` shows only resources tagged with that label. The same label on a site or client makes those entities searchable and filterable the same way.
|
||||
|
||||
## Use Labels for Metadata
|
||||
|
||||
Labels are flexible metadata. Because they are plain strings, you can use any naming convention that fits your workflow. Common examples include:
|
||||
|
||||
| Category | Example labels |
|
||||
| --- | --- |
|
||||
| Environment | `prod`, `staging`, `dev` |
|
||||
| Operating system | `linux`, `mac`, `windows` |
|
||||
| Customer | `customer-1`, `customer-2` |
|
||||
| Location | `us-east`, `warehouse-1`, `eu-west` |
|
||||
|
||||
These are only examples. You can define labels for teams, cost centers, compliance tiers, or anything else that helps you organize your infrastructure.
|
||||
|
||||
## Group Entities Across Types
|
||||
|
||||
Labels work across entity types, which makes them useful for grouping things that belong together but are not the same kind of object.
|
||||
|
||||
Want to see everything related to `warehouse-1`? Tag the site, the machine clients, and the resources for that location with the same label. You can then search and filter each table by that label to find the related entities quickly.
|
||||
|
||||
## Managing Labels
|
||||
|
||||
You can manage labels in two ways:
|
||||
|
||||
### Inline on Entity Tables
|
||||
|
||||
When viewing sites, clients, or resources, add labels directly from the entity table. If a label does not exist yet, you can create it on the spot and attach it immediately.
|
||||
|
||||
### Organization-wide Labels Page
|
||||
|
||||
Go to the organization labels page to see all labels in one place. From there you can create, edit, and delete labels across the organization. Changes to a label are reflected everywhere that label is used.
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: "Manage Organizations"
|
||||
description: "View every organization on a self-hosted instance from the server admin panel"
|
||||
---
|
||||
|
||||
The server admin panel is for the people who run a self-hosted Pangolin instance. It already includes a [master list of every user](/manage/access-control/create-user) on the server, across all organizations. Pangolin 1.23 adds the same kind of list for organizations.
|
||||
|
||||
## Organizations Table
|
||||
|
||||
Open **Server Admin → Organizations** to see every organization on the instance in one place. Each row shows the organization name, ID, owner, and counts for users, sites, and resources.
|
||||
|
||||
<Frame caption="Organizations table in the server admin panel, with name, ID, owner, and counts for users, sites, and resources.">
|
||||
<img src="/images/orgs-table.png" alt="Manage Organizations table in the Pangolin server admin panel"/>
|
||||
</Frame>
|
||||
|
||||
You can search the table and delete an organization from it.
|
||||
|
||||
## Visibility and Membership
|
||||
|
||||
Server admins can see this list even if they are not a member of an organization. That visibility is limited to the table. It does not add the server admin as a member, and it does not let them impersonate one.
|
||||
|
||||
To see an organization's contents in the dashboard, a server admin still has to be a member of that organization.
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
title: "Organization ID"
|
||||
description: "Understand organization IDs and how to locate yours in the dashboard"
|
||||
---
|
||||
Pangolin is multi-tenant. All sites, resources, clients, and other items belong to an organization, and a server can host more than one organization.
|
||||
|
||||
Each organization has a unique ID that Pangolin generates when you create the organization. You cannot change the ID after creation, and it is separate from the display name.
|
||||
|
||||
## Finding Your Organization ID
|
||||
|
||||
You can find your organization ID in these ways:
|
||||
|
||||
1. Check the general settings page for your organization. The ID appears at the top of the info card.
|
||||
|
||||
2. Check the URL path when viewing your dashboard. The org ID is the first slug in the URL. For example, in `https://app.pangolin.net/home-lab/settings/sites`, the org ID is `home-lab`.
|
||||
|
||||
3. On a self-hosted instance, as the server admin account, open the [organizations table](/manage/organizations/manage-organizations) in the server admin panel. The Organization ID column lists every org on the server.
|
||||
@@ -0,0 +1,275 @@
|
||||
---
|
||||
title: "Site to Cloud Backhaul"
|
||||
description: "Turn a remote node into a network concentrator that routes traffic between a cloud VPC and your Pangolin sites for edge to cloud backhaul"
|
||||
---
|
||||
|
||||
<Note>
|
||||
Site to cloud is an advanced [remote node](/manage/remote-node/understanding-nodes) configuration available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) and [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
A standard remote node terminates WireGuard tunnels and proxies HTTP(S) traffic to your resources. A **backhaul** node goes a step further: it acts as an IP router between an entire network, such as a cloud VPC, and your Pangolin sites. Instead of just forwarding individual resource connections, the node forwards whole subnets, letting hosts on either side reach each other without needing to install site and client on every machine.
|
||||
|
||||
This is useful when you want to bridge a cloud network (for example, an AWS VPC) with your on-prem or office network through Pangolin, using a node running in that VPC as the concentrator.
|
||||
|
||||
Another common use case is connecting edge devices to a cloud network. Edge devices can send traffic to a service in the cloud, and the cloud can send traffic down to a specific edge device. In both cases, the persistent backhaul tunnel carries the traffic.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/basic-backhaul-diagram.png" alt="Diagram of a backhaul remote node in a cloud network with bidirectional tunnels to sites on remote private networks" />
|
||||
</Frame>
|
||||
|
||||
<Tip>
|
||||
Site to cloud builds on top of a normal remote node install. Read [Remote Nodes](/manage/remote-node/understanding-nodes) and [Quick Install Guide](/manage/remote-node/quick-install-remote) first if you haven't deployed a node yet.
|
||||
</Tip>
|
||||
|
||||
## How It Works
|
||||
|
||||
- **The node routes, it doesn't just proxy.** Gerbil creates the WireGuard interface directly on the host and Docker forwards packets between it and the host's network, rather than isolating traffic inside a container network namespace.
|
||||
- **The cloud network learns to route to the node.** You add a route in your VPC (or other network) so that traffic destined for the Pangolin overlay is sent to the node.
|
||||
- **The node learns to route to the cloud network.** You register the VPC's CIDR on the node so it's pushed down to your sites as a route.
|
||||
- **Sites route natively.** Sites connect with native routing mode so the tunnel subnet is a real route on the site's host, not just an application-layer proxy.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A Linux server with Docker and root access, dedicated to running the backhaul node.
|
||||
- Administrative access to the cloud network you want to bridge (for example, an AWS VPC) so you can edit security groups, route tables, and instance settings.
|
||||
|
||||
## Enable IP Forwarding on the Host
|
||||
|
||||
Because the node forwards packets between the WireGuard tunnel and the host network instead of only terminating them, the host must have IP forwarding enabled.
|
||||
|
||||
```bash
|
||||
sudo sysctl -w net.ipv4.ip_forward=1
|
||||
```
|
||||
|
||||
Make it permanent by adding `net.ipv4.ip_forward=1` to `/etc/sysctl.d/99-pangolin-backhaul.conf`.
|
||||
|
||||
Docker enables forwarding in its own iptables rules by default, but if you've disabled Docker's iptables management, or packets are still being dropped, allow forwarded traffic through Docker's chain in `/etc/docker/daemon.json`:
|
||||
|
||||
```json title="/etc/docker/daemon.json"
|
||||
{
|
||||
"ip-forward-no-drop": true
|
||||
}
|
||||
```
|
||||
|
||||
Restart Docker after changing this file. See Docker's [packet filtering and firewalls guide](https://docs.docker.com/engine/network/packet-filtering-firewalls/#docker-on-a-router) for background on running Docker on a router.
|
||||
|
||||
## Deploy the Node with Host Networking
|
||||
|
||||
The standard node install runs Gerbil and Traefik on a bridge network, with Traefik sharing Gerbil's network namespace. For backhaul, all three services run with `network_mode: host` instead. This gives Gerbil direct access to the host's network stack and routing table, which is required for it to create routes for the subnets you register instead of only handling its own tunnel traffic.
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
name: pangolin
|
||||
services:
|
||||
pangolin:
|
||||
image: docker.io/fosrl/pangolin-node:latest # We recommend locking to a specific version for stability.
|
||||
container_name: pangolin
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
- pangolin-data-certificates:/var/certificates
|
||||
- pangolin-data-dynamic:/var/dynamic
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
|
||||
interval: "10s"
|
||||
timeout: "10s"
|
||||
retries: 15
|
||||
|
||||
gerbil:
|
||||
image: docker.io/fosrl/gerbil:latest
|
||||
container_name: gerbil
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
depends_on:
|
||||
pangolin:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- --reachableAt=http://localhost:3003
|
||||
- --generateAndSaveKeyTo=/var/config/key
|
||||
- --remoteConfig=http://localhost:3001/api/v1/
|
||||
volumes:
|
||||
- ./config/:/var/config
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- SYS_MODULE
|
||||
|
||||
traefik:
|
||||
image: docker.io/traefik:v3.7
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
depends_on:
|
||||
pangolin:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- --configFile=/etc/traefik/traefik_config.yml
|
||||
volumes:
|
||||
- ./config/traefik:/etc/traefik:ro
|
||||
- ./config/traefik/logs:/var/log/traefik
|
||||
- pangolin-data-certificates:/var/certificates:ro
|
||||
- pangolin-data-dynamic:/var/dynamic:ro
|
||||
|
||||
volumes:
|
||||
pangolin-data-dynamic:
|
||||
pangolin-data-certificates:
|
||||
```
|
||||
|
||||
<Note>
|
||||
With `network_mode: host`, containers bind to ports directly on the host, so there's no `ports` mapping and no `network_mode: service:gerbil` on Traefik like there is in the [standard install](/self-host/manual/docker-compose). Gerbil and Traefik reach each other and Pangolin over `localhost`.
|
||||
</Note>
|
||||
|
||||
## Configure the Node
|
||||
|
||||
The `config.yml` for a backhaul node uses the same fields as any [remote node](/manage/remote-node/config-file), mounted at `config/config.yml`:
|
||||
|
||||
```yaml title="config/config.yml"
|
||||
gerbil:
|
||||
start_port: 51820
|
||||
base_endpoint: "203.0.113.10" # REPLACE WITH YOUR NODE'S PUBLIC IP OR DOMAIN
|
||||
reachable_at: http://localhost:3003
|
||||
server:
|
||||
internal_port: 3001
|
||||
internal_hostname: localhost
|
||||
managed:
|
||||
id: "he4g78wevj25msf" # REPLACE WITH YOUR NODE'S ID
|
||||
secret: "n7sd18twfko0q0vrb7wyclqzbvvnx1fqt7ezv8xewhdb9s7d" # REPLACE WITH YOUR NODE'S SECRET
|
||||
```
|
||||
|
||||
See the [configuration file reference](/manage/remote-node/config-file) for every available option.
|
||||
|
||||
## Configure Traefik
|
||||
|
||||
Traefik's config is largely the same as a standard node, with `trustedIPs` opened up to accept the proxy protocol header from Gerbil on the host network:
|
||||
|
||||
```yaml title="config/traefik/traefik_config.yml"
|
||||
api:
|
||||
insecure: true
|
||||
dashboard: true
|
||||
|
||||
providers:
|
||||
file:
|
||||
directory: "/var/dynamic"
|
||||
watch: true
|
||||
|
||||
experimental:
|
||||
plugins:
|
||||
badger:
|
||||
moduleName: "github.com/fosrl/badger"
|
||||
version: "v1.7.0" # Check GitHub Releases for latest version tag
|
||||
|
||||
log:
|
||||
level: "INFO"
|
||||
format: "common"
|
||||
maxSize: 100
|
||||
maxBackups: 3
|
||||
maxAge: 3
|
||||
compress: true
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
websecure:
|
||||
address: ":443"
|
||||
proxyProtocol:
|
||||
trustedIPs:
|
||||
- 0.0.0.0/0 # This is coming from Gerbil
|
||||
- ::1/128
|
||||
transport:
|
||||
respondingTimeouts:
|
||||
readTimeout: "30m"
|
||||
|
||||
serversTransport:
|
||||
insecureSkipVerify: true
|
||||
|
||||
ping:
|
||||
entryPoint: "web"
|
||||
```
|
||||
|
||||
## Configure the Cloud Network
|
||||
|
||||
This section uses AWS as an example, but the same steps apply to any VPC-style network: the node needs to be allowed to receive and forward traffic for the whole network, and the network needs a route pointing back at the node.
|
||||
|
||||
<Steps>
|
||||
<Step title="Allow all traffic into the node">
|
||||
The node is acting as a concentrator for the whole network, not just serving its own ports, so its security group must accept all traffic from the VPC's CIDR. Otherwise the security group blocks the inbound traffic it's meant to forward.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/security_group_inbound_to_node.png" alt="AWS security group inbound rule allowing all VPC traffic" />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Route the Pangolin overlay through the node">
|
||||
Add a route in the VPC's route table for the WireGuard overlay subnet assigned to your node (shown as the node's **Address** on its page in the [Pangolin dashboard](https://app.pangolin.net)), targeting the node's instance or network interface. This tells the rest of the VPC to send anything destined for the Pangolin overlay - other sites and clients - to the node.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/subnet_in_route_table.png" alt="AWS route table entry routing the overlay subnet to the node" />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Disable source/destination check">
|
||||
By default, AWS drops any packet where an instance isn't the source or destination, which would silently break a node that's forwarding traffic on behalf of others. Disable the source/destination check on the node's instance so it's allowed to route traffic that isn't addressed to itself.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/source_destination_check_menu.png" alt="AWS instance menu option to change source/destination check" />
|
||||
</Frame>
|
||||
|
||||
<Frame>
|
||||
<img src="/images/source_destination_check_box.png" alt="AWS dialog checkbox disabling the source/destination check" />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Advertise the VPC subnet on the node">
|
||||
In the node's settings in the Pangolin dashboard, add the VPC's CIDR (for example, `172.31.0.0/16`) to **Remote Subnets** under the **Networking** tab. This is pushed down to your sites as a route, so they know to send traffic for that subnet through this node's tunnel.
|
||||
|
||||
You can also set **Preference Labels** on the node and apply matching labels to sites, which enforces that those sites connect through this remote node specifically. This is useful once you have more than one node or point of presence and want particular sites to always backhaul through this one.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/subnet_programmed_on_node.png" alt="Pangolin dashboard remote subnets setting with VPC CIDR added" />
|
||||
</Frame>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Enable Native Routing on Your Sites
|
||||
|
||||
For the VPC and a site's network to route to each other, the tunnel needs to be a real route on the site's host, not just something the Pangolin Site proxies at the application layer. Start the site with `--native-main` so it creates the network interface and routes directly on the host:
|
||||
|
||||
```bash
|
||||
sudo pangolin up site \
|
||||
--id 34s48my8iba5wsl \
|
||||
--secret ts1v7480qqw01j7ba4uiw5y7l58skw7anpji2ndludhgzloh \
|
||||
--endpoint https://app.pangolin.net \
|
||||
--native-main
|
||||
```
|
||||
|
||||
On Windows, set `nativeMain` in `C:\Users\<Username>\.config\pangolin\site.json` instead:
|
||||
|
||||
```json title="C:\Users\<Username>\.config\pangolin\site.json"
|
||||
{
|
||||
"id": "34s48my8iba5wsl",
|
||||
"secret": "ts1v7480qqw01j7ba4uiw5y7l58skw7anpji2ndludhgzloh",
|
||||
"endpoint": "https://app.pangolin.net",
|
||||
"nativeMain": true
|
||||
}
|
||||
```
|
||||
|
||||
<Warning>
|
||||
If you are using a Pangolin Site on Windows, `wintun.dll` is required to run in native mode. If not already provided by the installer, you can download from https://www.wintun.net/
|
||||
</Warning>
|
||||
|
||||
<Warning>
|
||||
Windows doesn't respond to ICMP echo requests (ping) over the tunnel interface by default. If you need to ping a Windows site, enable the built-in firewall rule for it:
|
||||
|
||||
```powershell
|
||||
Enable-NetFirewallRule -DisplayName "File and Printer Sharing (Echo Request - ICMPv4-In)"
|
||||
```
|
||||
</Warning>
|
||||
|
||||
## Resolving a Site's Address Directly
|
||||
|
||||
If something outside of Pangolin's normal resource routing needs to reach a site directly, for example a static route configured elsewhere in your network or a cloud service sending traffic to a specific edge device, you can resolve `<siteId>.site.pangolin.net` (the site ID is the one used in the `--id` argument when running the site) to get the site's current routable address.
|
||||
|
||||
## High Availability
|
||||
|
||||
Deploy multiple nodes in your network. Each node will have a different routable subnet. Repeating the above steps will ensure they are routed to uniquely. Sites will connect to one of the nodes in the network and if it does down fail over to another online node.
|
||||
|
||||
Make sure to use the site's DNS address to connect to it as when it moves between nodes its address may change.
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: "Configuration File"
|
||||
description: "Configure your remote node using the config.yml file"
|
||||
---
|
||||
The `config.yml` file includes basic config variable for your remote node. This file is mounted at `config/config.yml` in your Docker container.
|
||||
|
||||
Minimal configuration file:
|
||||
|
||||
```yaml title="config.yml"
|
||||
gerbil:
|
||||
start_port: 51820
|
||||
base_endpoint: "154.123.45.67" # REPLACE WITH YOUR IP OR DOMAIN
|
||||
|
||||
managed:
|
||||
id: "he4g78wevj25msf"
|
||||
secret: "n7sd18twfko0q0vrb7wyclqzbvvnx1fqt7ezv8xewhdb9s7d"
|
||||
```
|
||||
|
||||
### Gerbil Tunnel Controller
|
||||
|
||||
<ResponseField name="gerbil" type="object" required>
|
||||
Gerbil tunnel controller settings for WireGuard tunneling.
|
||||
|
||||
<Expandable title="Gerbil">
|
||||
<ResponseField name="base_endpoint" type="string" required>
|
||||
Domain name included in WireGuard configuration for tunnel connections.
|
||||
|
||||
**Example**: `pangolin.example.com`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="start_port" type="integer">
|
||||
Starting port for WireGuard tunnels.
|
||||
|
||||
**Example**: `51820`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Remote Node Configuration
|
||||
|
||||
<ResponseField name="managed" type="object">
|
||||
Settings for connecting the remote node to the Pangolin head server.
|
||||
|
||||
<Expandable title="Managed">
|
||||
{/* <ResponseField name="name" type="string">
|
||||
Display name for the managed deployment.
|
||||
|
||||
**Example**: `My Self-Hosted Instance`
|
||||
</ResponseField> */}
|
||||
|
||||
<ResponseField name="id" type="string">
|
||||
Unique identifier for the managed deployment. Generated from the installer or the [Pangolin dashboard](https://app.pangolin.net).
|
||||
|
||||
**Example**: `he4g78wevj25msf`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="secret" type="string">
|
||||
Secret key for authenticating with the managed service. Generated from the installer or the [Pangolin dashboard](https://app.pangolin.net).
|
||||
|
||||
**Example**: `n7sd18twfko0q0vrb7wyclqzbvvnx1fqt7ezv8xewhdb9s7d`
|
||||
|
||||
<Warning>
|
||||
Keep this secret secure and do not share it publicly.
|
||||
</Warning>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="endpoint" type="string">
|
||||
The managed service endpoint to connect to. This can only change with enterprise deployments.
|
||||
|
||||
**Example**: `https://app.pangolin.net`
|
||||
|
||||
**Default**: `https://app.pangolin.net`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="redirect_endpoint" type="string">
|
||||
Custom redirect endpoint for authentication flows. This can only change for enterprise deployments.
|
||||
|
||||
**Example**: `https://my-pangolin.example.com`
|
||||
|
||||
<Note>
|
||||
If not specified, the default dashboard URL will be used.
|
||||
</Note>
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
@@ -0,0 +1,108 @@
|
||||
---
|
||||
title: "Quick Install Guide"
|
||||
description: "Deploy your own remote Pangolin node in under 10 minutes with our automated installer"
|
||||
---
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have:
|
||||
|
||||
- **Pangolin Cloud** account. You can [create a free account here](https://app.pangolin.net/auth/signup) OR a [clustered self-hosted Pangolin Enterprise deployment](/self-host/clustering/understanding-clustering).
|
||||
- **Linux server** with root access and public IP address.
|
||||
- **Open ports on firewall** for 80 (TCP), 443 (TCP), 51820 (UDP), and 21820 (UDP for clients).
|
||||
|
||||
<Tip>
|
||||
**Recommended**: Ubuntu 20.04+ or Debian 11+ for best compatibility and performance.
|
||||
</Tip>
|
||||
|
||||
## Choose Your Server
|
||||
|
||||
Need help choosing? See our [complete VPS guide](/self-host/choosing-a-vps) for suggestions.
|
||||
|
||||
## Networking
|
||||
|
||||
Before installing Pangolin, ensure you've opened the required port on your firewall. See our guide on [networking](/self-host/dns-and-networking#port-configuration) for more information.
|
||||
|
||||
## Installation Process
|
||||
|
||||
<Steps>
|
||||
<Step title="Download the installer">
|
||||
Connect to your server via SSH and download the installer:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://static.pangolin.net/get-node-installer.sh | bash
|
||||
```
|
||||
|
||||
The installer supports both AMD64 (x86_64) and ARM64 architectures.
|
||||
</Step>
|
||||
|
||||
<Step title="Run the installer">
|
||||
Execute the installer with root privileges:
|
||||
|
||||
```bash
|
||||
sudo ./installer
|
||||
```
|
||||
|
||||
The installer places all files in the current directory. Move the installer to your desired installation directory before running it.
|
||||
|
||||
<Note>
|
||||
If you're not using Pangolin Cloud (`app.pangolin.net`) and instead have a self-hosted Pangolin Enterprise deployment, pass its URL with the `--pangolin-endpoint` flag so the installer requests node credentials from your instance instead:
|
||||
|
||||
```bash
|
||||
sudo ./installer --pangolin-endpoint https://pangolin.example.com
|
||||
```
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
<Step title="Do you already have credentials from the dashboard?">
|
||||
|
||||
If you are on your game and have already generated credentials for this node in the dashboard enter them here.
|
||||
|
||||
<Tip>
|
||||
If you don't have them yet you can just select no when asked and we will create it in a later step.
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="The public addressable IP address for this node">
|
||||
|
||||
Enter either the public IP address of your server or a domain name that resolves to it.
|
||||
|
||||
<Note>
|
||||
The installer will attempt resolve and prefill your public IP address. Verify this is correct before preceding.
|
||||
</Note>
|
||||
|
||||
<Warning>
|
||||
If you choose to use a domain keep in mind this just resolves your node on the internet while the actual subdomains for resources will be managed in the server.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Generate credentials">
|
||||
|
||||
If you did not enter credentials earlier then you should see something like the following:
|
||||
|
||||
```
|
||||
Your managed credentials have been obtained successfully.
|
||||
ID: he4g78wevj25msf
|
||||
Secret: n7sd18twfko0q0vrb7wyclqzbvvnx1fqt7ezv8xewhdb9s7d
|
||||
```
|
||||
|
||||
Go to the [Pangolin dashboard](https://app.pangolin.net/) (or your own panel's URL if you passed `--pangolin-endpoint`) and log in. Navigate to the "Self-hosted" section and add a new node. Select the adopt method. Use this ID and secret to register your node.
|
||||
|
||||
<Tip>
|
||||
More than one account can use the same node credentials. This is useful for teams.
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## Post-Installation Setup
|
||||
|
||||
Once installation completes successfully, you'll see:
|
||||
|
||||
```
|
||||
Installation complete!
|
||||
```
|
||||
|
||||
Navigate to the [Pangolin dashboard](https://app.pangolin.net/) (or your own dashboard if self-hosted) and create sites, resources, and targets for your remote node.
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: "Remote Nodes"
|
||||
description: "Control your own Pangolin node with cloud management"
|
||||
---
|
||||
<Note>
|
||||
Remote nodes are only available in [Pangolin
|
||||
Cloud](https://app.pangolin.net/auth/signup) and [Enterprise
|
||||
Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
Remote nodes, you run your own Pangolin node - your tunnels, SSL termination, and traffic all stay on your server and use your bandwidth. The difference is that management and monitoring are handled through our cloud. The node just handles terminating Wireguard tunnels, serving HTTP(S) traffic, and routing relayed client connections - it is essentially a remote networking hub.
|
||||
|
||||
Think of different nodes as the "front doors" to your applications - users connect to the closest one, and it securely routes their requests to your backend services.
|
||||
|
||||
<Tip>
|
||||
You can deploy a remote Pangolin node [using the installer](/manage/remote-node/quick-install-remote).
|
||||
</Tip>
|
||||
|
||||
## How It Works
|
||||
|
||||
- **Host the Node**: Run Gerbil, Traefik, and a light weight agent on your server that communicates with the central Pangolin control plane with a websocket connection.
|
||||
- **Delegate the DNS**: Your domain and DNS config is still controlled by the Pangolin control plane and the central DNS server routes to the right node when connecting.
|
||||
- **Certificates and Config**: The control plane pushes down WireGuard configs, SSL certificates, and routing rules to your node as you create resources and sites.
|
||||
- **Failover**: If you have multiple nodes, the control plane will failover between them. If one node becomes unavailable, traffic can optionally fail over to our cloud infrastructure or other nodes until you restore service.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/ha.png" alt="Diagram of high availability failover across remote nodes" width="400"/>
|
||||
</Frame>
|
||||
|
||||
## Benefits
|
||||
|
||||
### Automatic Updates and Less Maintenance
|
||||
The cloud dashboard evolves quickly, so you get new features and bug fixes without having to manually pull new containers every time. Because the remote node is just handling the networking you do not need to do database migrations, or backups.
|
||||
|
||||
### Cloud Failover
|
||||
If your nodes goes down, your tunnels can temporarily fail over to our cloud points of presence or another node until you bring it back online. This ensures continuous availability.
|
||||
|
||||
### High Availability (PoPs)
|
||||
You can attach multiple nodes to your account for redundancy and better performance. For example, deploy nodes across different regions or providers for decreased latency.
|
||||
|
||||
### Better Bandwidth
|
||||
Since all tunnel traffic flows through your own server, you benefit from your own network's bandwidth capacity. This means you are not constrained by shared cloud infrastructure limits - if your server has high-throughput connectivity, your tunnels will too.
|
||||
|
||||
## Restrictions
|
||||
|
||||
### AI Gateway Providers
|
||||
AI Gateway providers are only routed to the Pangolin server nodes. Sites that are connected to remote nodes will not be accessible to be routed to as targets on providers. DNS resolution of gateway resources will not point at remote nodes. Users using gateway resources with custom targets on sites may wish to create a separate org for these resources so sites are able to connect to regular nodes.
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: "Resource Launcher"
|
||||
description: "Find and open the resources you can access from one organization home page"
|
||||
---
|
||||
|
||||
The Resource Launcher is the organization home page. It lists every [public](/manage/resources/understanding-resources#public-resource-types) and [private](/manage/resources/understanding-resources#private-resource-types) resource the signed-in user can access through an explicit grant on their user or [role](/manage/access-control/create-user#roles). Non-administrators land here when they sign in. Administrators can use it the same way, then jump into settings when they need to change a resource.
|
||||
|
||||
<Frame caption="The Resource Launcher, grouped by site, showing the resources a user can open.">
|
||||
<img src="/images/resource-launcher.png" alt="Resource Launcher in the Pangolin dashboard"/>
|
||||
</Frame>
|
||||
|
||||
## Find Resources
|
||||
|
||||
Switch between **Grid** and **List** layouts. Search updates as you type across names, domains, sites, labels, destinations, aliases, and related fields. Filter by one or more [sites](/manage/sites/understanding-sites) and [labels](/manage/labels). Sort by name, and group by **Site**, **Label**, or **None** for a flat list.
|
||||
|
||||
Site groups show an online or offline indicator. You can expand or collapse groups. The launcher remembers that state for the current organization and view.
|
||||
|
||||
In very large organizations, the launcher may ask you to search or filter before browsing, and it may simplify grouping until the list is smaller.
|
||||
|
||||
## Open a Resource
|
||||
|
||||
Selecting a resource always expands a details panel. The panel shows what matters for that type: access URL or alias, health and auth for public resources, destination and certificate status for private resources, and port restrictions when they apply.
|
||||
|
||||
<Frame caption="Resource Launcher detail panel. This example shows a private SSH resource.">
|
||||
<img src="/images/resource-launcher-expanded.png" alt="Resource Launcher detail panel showing a private SSH resource as an example."/>
|
||||
</Frame>
|
||||
|
||||
From a card, list row, or the panel you can open HTTP-style resources in a new tab (HTTP/HTTPS, SSH, RDP, VNC, AI Gateway) or copy the access string. Private resources remind you to connect with a [Pangolin client](/manage/clients/install-client).
|
||||
|
||||
For [AI Gateway](/manage/ai/overview) resources, the panel also lists available models, [virtual API keys](/manage/ai/virtual-api-keys), and copy-paste setup for coding agents. Public gateways use your API key. Private gateways use the connected client as the credential.
|
||||
|
||||
The launcher URL stores the current search, filters, grouping, layout, and sort, so you can bookmark or share a view. Opening a resource from admin settings can deep-link into the launcher with that resource's panel already open.
|
||||
|
||||
## Saved Views
|
||||
|
||||
A saved view captures search, filters, grouping, layout, and sort. Start from **Default**, then save the current setup as a personal view. Switch views with the tabs at the top. The last view you used in that organization is restored when you come back.
|
||||
|
||||
- **Save for Me** stores your personal default. It overrides any organization-wide default for you only.
|
||||
- **Save as New View** creates a named personal view.
|
||||
- **Save to Current View** updates a personal view you own.
|
||||
- **Reset View** discards unsaved changes.
|
||||
|
||||
Organization-wide views are read-only unless you are an administrator.
|
||||
|
||||
## For Administrators
|
||||
|
||||
Use **View as Admin** in the header to open organization settings, or from a resource panel to open that resource's settings. From a resource settings page you can jump back to the launcher with that resource already open.
|
||||
|
||||
Administrators can share views with the organization:
|
||||
|
||||
- **Save for Everyone** on the default view sets the organization-wide default for members who have not saved a personal default.
|
||||
- **Save for Everyone** on a named view shares it with every member.
|
||||
- **Make Personal** turns an organization-wide view back into a personal view.
|
||||
|
||||
Set a useful organization-wide default (for example grouped by site, list layout) so people do not have to configure the launcher themselves. Users only see resources granted to their user or a role they belong to. Refresh reloads the latest resources so recent access or config changes show up.
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
title: "AI Gateway"
|
||||
description: "Reach an AI API over the Pangolin tunnel using the connected client's identity"
|
||||
---
|
||||
|
||||
A private AI Gateway resource exposes an AI API only to devices connected with the [Pangolin client](/manage/clients/install-client). Nothing is reachable from the public internet. Unlike [public AI Gateway](/manage/resources/public/ai-gateway), the gateway does not check a virtual API key. Identity comes from the active client connection.
|
||||
|
||||
This page covers how the **resource** works: reachability, access, and what you attach. Providers, model routing, the catalog, and client setup live in [AI Gateway](/manage/ai/overview).
|
||||
|
||||
## How It Works
|
||||
|
||||
1. You create a private resource with type **AI Gateway** on a Pangolin Site and attach one or more org-level [providers](/manage/ai/providers/overview).
|
||||
2. You grant [users, roles, or machines](/manage/resources/private/authentication) access, the same as any other private resource.
|
||||
3. The user connects with the Pangolin client. The machine running the AI client must be on that tunnel.
|
||||
4. The agent calls the resource URL. Pangolin attributes the call to the connected user and proxies to the selected provider.
|
||||
|
||||
Clients still need a value in the API key field. Use the literal string `none`. Deleting the field usually breaks the client.
|
||||
|
||||
## Providers, Not Destinations
|
||||
|
||||
Private AI Gateway resources attach providers on the resource's **AI Gateway** tab. They do not use a host or CIDR [destination](/manage/resources/private/destinations) as the model backend. Cloud APIs are called from Pangolin. [Custom](/manage/ai/providers/custom) providers can use **Site Targets** when the model server is on a site network.
|
||||
|
||||
Private resources, including this type, can only be created on [Pangolin Sites](/manage/sites/understanding-sites#pangolin-site-recommended).
|
||||
|
||||
## Authentication and Access Rules
|
||||
|
||||
Access is the private resource model: grant users, roles, or machines explicitly. See [Private Authentication](/manage/resources/private/authentication).
|
||||
|
||||
When the connected client maps to a user, Pangolin forwards that identity upstream as [`Remote-*` headers](/manage/ai/providers/configuration#identity-headers).
|
||||
|
||||
## More Than One Resource
|
||||
|
||||
Give different users and roles their own providers with more than one AI Gateway resource. Distinct hostnames are the usual approach. Unlike other private resource types, they can also share a FQDN because they all route to the gateway inside Pangolin. See [Multiple Gateway Resources](/manage/ai/multiple-gateway-resources).
|
||||
|
||||
## Compared to Public AI Gateway
|
||||
|
||||
| | Private AI Gateway | [Public AI Gateway](/manage/resources/public/ai-gateway) |
|
||||
|---|---|---|
|
||||
| **Reachability** | Pangolin client tunnel | Public FQDN |
|
||||
| **Auth** | Client identity; use `none` as the key placeholder | [Virtual API key](/manage/ai/virtual-api-keys) on every call |
|
||||
| **Who can call it** | Users, roles, and machines granted on the resource | Identity keys follow users and roles; manual keys grant access when created |
|
||||
|
||||
For providers, model routing, and connecting Claude Code, Codex, and other clients, see [AI Gateway](/manage/ai/overview).
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: "Aliases"
|
||||
description: "Friendly names for resources, overlaps, loopback on the site host, and DNS behavior"
|
||||
---
|
||||
Aliases provide a secondary, user-friendly address for any of your resources, allowing users to access the resource using this alternate name in addition to the original address.
|
||||
|
||||
For instance, a router with the address `10.0.0.1` could be assigned the alias `router.internal`, and users could connect using either. Aliases are accessible to anyone who has access to the resource, and they are exclusively accessible when connected with a Pangolin client, meaning they function without requiring any external DNS record setup. Furthermore, aliases are protocol agnostic, which means they will work with any network protocol, essentially acting as a pseudo-A record for an address that is only functional within the Pangolin environment.
|
||||
|
||||
## Overlapping Networks and Loopback on the Site Host
|
||||
|
||||
Several situations described on the [Destinations](/manage/resources/private/destinations) page are where an alias is especially important—either optional but strongly recommended, or effectively required.
|
||||
|
||||
Overlapping IP spaces across sites are common (for example the same RFC1918 subnet behind different Pangolin sites). Pangolin helps route connections without users picking a site by hand, but raw IPs or ambiguous names can still collide across environments. Assigning a distinct alias per resource gives clients a single hostname whose DNS resolution goes through Pangolin, so traffic consistently reaches the intended resource and site instead of whichever overlapping address would otherwise win. See [Overlapping destinations across sites](/manage/resources/private/destinations#overlapping-destinations-across-sites).
|
||||
|
||||
Loopback on the site host is another case: if the resource destination is `127.0.0.1` or `localhost` on the machine running the site, those strings still mean “this machine” on the user’s laptop or desktop—not the remote site. There is no safe way for users to type loopback literals and reach the service behind another host; an alias hostname is required so the client resolves the name via Pangolin and sends traffic over the tunnel to the site, which then forwards to its own loopback. See [Loopback on the site host](/manage/resources/private/destinations#loopback-on-the-site-host).
|
||||
|
||||
## CIDRs vs. IPs
|
||||
|
||||
An alias can only be created for a resource that is a single host (IP or FQDN). Aliases cannot be created for resources that are CIDR ranges because it would be ambiguous which host within the range the alias should point to.
|
||||
|
||||
## Domain Structure
|
||||
|
||||
Since aliases cannot be single-label domains, you must avoid using domain names that do not contain a dot (e.g., `pangolin`). A domain like `pangolin.net`, which includes a dot, is acceptable. Instead of a single-label domain, you should consider using a subdomain of a domain you control, such as `router.mywebsite.com`, or an existing private/internal domain name, like `router.internal` or `router.corp`.
|
||||
|
||||
### Wildcards
|
||||
|
||||
Wildcards allow you to define aliases that match multiple hostnames using special characters in the FQDN. For example, in an alias like `*.host-0?.autoco.internal`, the asterisk `*` matches any sequence of characters (including none), and the question mark `?` matches exactly one character.
|
||||
|
||||
If you use a wildcard such as `*.proxy.internal`, it will match any hostname that ends with `.proxy.internal` and has something before the dot—such as `host.proxy.internal`, `longerhost.proxy.internal`, or even `sub.host.proxy.internal`. However, the wildcard will not match the base domain itself (`autoco.internal` without anything before the dot).
|
||||
|
||||
### .local TLD
|
||||
|
||||
The `.local` TLD is reserved for local networking and multicast DNS (mDNS). mDNS is commonly used by Apple Bonjour, Linux zeroconf, and limited Windows features. Because of this, aliases that use `.local` may not resolve reliably across many devices. We recommend using a subdomain you control (for example, `alias.mywebsite.com`) or a private/internal domain such as `alias.internal` or `alias.corp`.
|
||||
|
||||
## Custom Upstream DNS
|
||||
|
||||
Aliases work by overriding the DNS of your computer running the client so that all DNS requests are sent to the Pangolin client for resolution. That behavior is controlled by the Enable Aliases (Override DNS) preference; see [Configure Clients](/manage/clients/configure-client#enable-aliases-override-dns). The DNS server on your computer is typically `100.96.128.1` (the first address inside of your utility subnet on the org) when connected to the tunnel, which forwards requests to an upstream server. By default, we use `1.1.1.1`, but this upstream address can be configured in the CLI or in the client settings.
|
||||
|
||||
**If you are attempting to set an upstream DNS server that is only accessible via the tunnel, ensure that you create a resource and check the tunnel DNS option in the client configuration settings.** Otherwise, connectivity to the server may fail when connected to the tunnel. Enable Aliases (Override DNS) must also be on—see [Configure Clients](/manage/clients/configure-client#enable-aliases-override-dns)—so the client can intercept DNS and forward queries to the upstream server.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user