seubert-claims/.claude/skills/pangolin/SKILL.md

12 KiB


name: pangolin description: Manage Pangolin reverse proxy — sites, resources (HTTP/TCP services), targets (backends), domains, roles, users, clients, and access control via the Pangolin Integration API. Use when the user asks about their Pangolin tunnels, reverse proxy resources, exposed services, or site connectivity. argument-hint: [action] [resource] [options] allowed-tools: Bash(curl *) Read

Pangolin API Skill

Manage a self-hosted Pangolin tunneled reverse proxy via the Integration API.

Authentication

The API key is stored in PANGOLIN_API_KEY and base URL in PANGOLIN_URL.

Authorization: Bearer $PANGOLIN_API_KEY

The key format is {apiKeyId}.{apiKeySecret}.

Base URL

https://api.$PANGOLIN_URL/v1

The Integration API lives at api. prefixed to the Pangolin domain, path prefix /v1.

Standard Headers

-H "Authorization: Bearer $PANGOLIN_API_KEY" \
-H "Content-Type: application/json"

How to Make Requests

curl -s "https://api.$PANGOLIN_URL/v1/org/$PANGOLIN_ORG/sites" \
  -H "Authorization: Bearer $PANGOLIN_API_KEY" \
  -H "Content-Type: application/json" | jq .

Organization

The org ID for this instance is: wulf-consulting

Always use PANGOLIN_ORG="wulf-consulting" in requests.

Response Format

{
  "data": {
    "resources": [ ... ],
    "pagination": {
      "total": 42,
      "limit": 25,
      "offset": 0
    }
  },
  "success": true,
  "error": false,
  "message": "Human-readable message",
  "status": 200
}

Always check the success field.

Pagination

List endpoints return a pagination object inside data with total, limit, and offset fields. The default limit is typically 25 or 1000 depending on the endpoint.

To paginate, add ?limit=N&offset=N query parameters:

# First page
curl -s "$BASE/org/$ORG/resources?limit=25&offset=0" ...
# Second page
curl -s "$BASE/org/$ORG/resources?limit=25&offset=25" ...

Always check pagination.total against the number of items returned. If total exceeds the current page size, fetch additional pages until all items are retrieved. Example pattern:

# Fetch all pages of a resource list
OFFSET=0
LIMIT=100
while true; do
  RESP=$(curl -s "$BASE/org/$ORG/resources?limit=$LIMIT&offset=$OFFSET" \
    -H "Authorization: Bearer $PANGOLIN_API_KEY")
  # Process items from this page...
  TOTAL=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['pagination']['total'])")
  OFFSET=$((OFFSET + LIMIT))
  if [ $OFFSET -ge $TOTAL ]; then break; fi
done

When listing resources, sites, targets, users, etc., always use ?limit=1000 to minimize round trips, and paginate if total exceeds the returned count.

Important: HTTP Method Conventions

Pangolin uses non-standard HTTP method semantics:

  • PUT = Create a new resource
  • POST = Update an existing resource
  • DELETE = Delete
  • GET = Read/list

This is the opposite of typical REST conventions. Be careful!

API Endpoints Reference

Organizations

Action Method Endpoint
Check org ID GET /org/checkId
Create org PUT /org
List orgs GET /orgs (root key only)
Get org GET /org/:orgId
Update org POST /org/:orgId
Delete org DELETE /org/:orgId

Sites (Tunnel Endpoints)

Action Method Endpoint
List sites GET /org/:orgId/sites
Create site PUT /org/:orgId/site
Get site defaults GET /org/:orgId/pick-site-defaults
Get site by niceId GET /org/:orgId/site/:niceId
Get site by ID GET /site/:siteId
Update site POST /site/:siteId
Delete site DELETE /site/:siteId

Site types: "newt" (tunnel client), "wireguard", "local"

Resources (Public HTTP/TCP Services)

Action Method Endpoint
List resources GET /org/:orgId/resources
List resource names GET /org/:orgId/resource-names
Create resource PUT /org/:orgId/resource
Create for site PUT /org/:orgId/site/:siteId/resource
Get resource GET /resource/:resourceId
Get by niceId GET /org/:orgId/resource/:niceId
Update resource POST /resource/:resourceId
Delete resource DELETE /resource/:resourceId
List site resources GET /site/:siteId/resources

Create HTTP resource body:

{
  "name": "My App",
  "subdomain": "app",
  "http": true,
  "protocol": "tcp",
  "domainId": "domain-id",
  "stickySession": false
}

Update resource fields: name, subdomain, ssl, sso, blockAccess, enabled, emailWhitelistEnabled, domainId, stickySession, tlsServerName, setHostHeader

Targets (Backends/Upstreams)

Action Method Endpoint
List targets GET /resource/:resourceId/targets
Create target PUT /resource/:resourceId/target
Get target GET /target/:targetId
Update target POST /target/:targetId
Delete target DELETE /target/:targetId

Target body:

{
  "siteId": 1,
  "ip": "192.168.1.100",
  "port": 8080,
  "method": "round-robin",
  "enabled": true
}

Health check fields: hcEnabled, hcPath, hcScheme, hcInterval (min 5s), hcTimeout (min 1s), hcStatus

Resource Rules

Action Method Endpoint
List rules GET /resource/:resourceId/rules
Create rule PUT /resource/:resourceId/rule
Update rule POST /resource/:resourceId/rule/:ruleId
Delete rule DELETE /resource/:resourceId/rule/:ruleId

Resource Access Control

Action Method Endpoint
List roles GET /resource/:resourceId/roles
List users GET /resource/:resourceId/users
Set roles POST /resource/:resourceId/roles
Set users POST /resource/:resourceId/users
Add role POST /resource/:resourceId/roles/add
Remove role POST /resource/:resourceId/roles/remove
Add user POST /resource/:resourceId/users/add
Remove user POST /resource/:resourceId/users/remove
Set password auth POST /resource/:resourceId/password
Set pincode auth POST /resource/:resourceId/pincode
Set header auth POST /resource/:resourceId/header-auth
Set email whitelist POST /resource/:resourceId/whitelist
Get email whitelist GET /resource/:resourceId/whitelist
Add to whitelist POST /resource/:resourceId/whitelist/add
Remove from whitelist POST /resource/:resourceId/whitelist/remove

Access Tokens

Action Method Endpoint
Generate token POST /resource/:resourceId/access-token
Delete token DELETE /access-token/:accessTokenId
List org tokens GET /org/:orgId/access-tokens
List resource tokens GET /resource/:resourceId/access-tokens

Domains

Action Method Endpoint
List domains GET /org/:orgId/domains
Create domain PUT /org/:orgId/domain
Get domain GET /org/:orgId/domain/:domainId
Update domain PATCH /org/:orgId/domain/:domainId
Delete domain DELETE /org/:orgId/domain/:domainId
Get DNS records GET /org/:orgId/domain/:domainId/dns-records
Restart cert POST /org/:orgId/domain/:domainId/restart

Domain types: "ns", "cname", "wildcard"

Users

Action Method Endpoint
Create user PUT /org/:orgId/user
List users GET /org/:orgId/users
Get user GET /org/:orgId/user/:userId
Get by username GET /org/:orgId/user-by-username
Update user POST /org/:orgId/user/:userId
Remove user DELETE /org/:orgId/user/:userId

Roles

Action Method Endpoint
List roles GET /org/:orgId/roles
Create role PUT /org/:orgId/role
Get role GET /role/:roleId
Update role POST /role/:roleId
Delete role DELETE /role/:roleId

Clients (VPN / Olm Devices)

Action Method Endpoint
List clients GET /org/:orgId/clients
Create client PUT /org/:orgId/client
Get client GET /client/:clientId
Get by niceId GET /org/:orgId/client/:niceId
Update client POST /client/:clientId
Delete client DELETE /client/:clientId
Archive POST /client/:clientId/archive
Unarchive POST /client/:clientId/unarchive
Block POST /client/:clientId/block
Unblock POST /client/:clientId/unblock

Site Resources (Private / VPN-accessible)

Action Method Endpoint
List all GET /org/:orgId/site-resources
List for site GET /org/:orgId/site/:siteId/resources
Create PUT /org/:orgId/site-resource
Get GET /site-resource/:id
Update POST /site-resource/:id
Delete DELETE /site-resource/:id

Invitations

Action Method Endpoint
List invitations GET /org/:orgId/invitations
Create invite POST /org/:orgId/create-invite
Remove invite DELETE /org/:orgId/invitations/:inviteId

Blueprints (IaC)

Action Method Endpoint
Apply blueprint PUT /org/:orgId/blueprint
List blueprints GET /org/:orgId/blueprints
Get blueprint GET /org/:orgId/blueprint/:blueprintId

Audit Logs

Action Method Endpoint
Query request logs GET /org/:orgId/logs/request
Export request logs GET /org/:orgId/logs/request/export
Query analytics GET /org/:orgId/logs/analytics

API Keys (Org-scoped)

Action Method Endpoint
List keys GET /org/:orgId/api-keys
Create key PUT /org/:orgId/api-key
Get key GET /org/:orgId/api-key/:apiKeyId
Delete key DELETE /org/:orgId/api-key/:apiKeyId
List actions GET /org/:orgId/api-key/:apiKeyId/actions
Set actions POST /org/:orgId/api-key/:apiKeyId/actions

Identity Providers

Action Method Endpoint
List IdPs GET /idp
Create OIDC IdP PUT /idp/oidc
Update IdP POST /idp/:idpId/oidc
Delete IdP DELETE /idp/:idpId

Health & Info

Action Method Endpoint
Health check GET /
Server info GET /server-info

Known Configuration

Sites (10 total)

ID Name Type NiceId
1 215-node01 newt infamous-common-box-turtle
2 hq-dev01 newt great-scytodes-thoracica
3 Seubert-DC newt well-groomed-texas-coral-snake
7 kaercher5630 newt rapid-texas-lined-snake
8 WulfHQDev newt edible-forficula-auricularia
9 WulfHQProd newt yawning-campbells-dwarf-hamster
10 WulfExp newt flawed-indian-desert-jird
11 Seubert-HQ newt productive-chilean-rock-rat
13 wulfhqclaw newt flickering-common-box-turtle
14 robot01 newt impartial-savannah-forest-tree-frog

Domains

ID Domain Type Verified
domain1 wulfconsulting.cloud wildcard yes
rkkbfx2qxp8bvi5 wulf.cloud wildcard yes
4zl519ktyb1acra kaercherfamily.com wildcard yes

Common Workflows

Check what's exposed publicly

  1. List all resources: GET /org/wulf-consulting/resources
  2. For each resource, list targets: GET /resource/:resourceId/targets

Add a new service

  1. Create resource: PUT /org/wulf-consulting/resource with subdomain, domain, name
  2. Add target: PUT /resource/:resourceId/target with site, IP, port
  3. Optionally configure SSO, whitelist, or password auth

Check site connectivity

  1. List sites: GET /org/wulf-consulting/sites — check online field
  2. Get specific site: GET /site/:siteId for detailed status

Manage access

  1. Get resource: GET /resource/:resourceId
  2. Set SSO: POST /resource/:resourceId with {"sso": true}
  3. Add email whitelist: POST /resource/:resourceId/whitelist/add with {"email": "..."}

Argument Handling

  • $ARGUMENTS contains the full user request
  • If no specific action given, show: sites (with online status), resources, and domains
  • Parse natural language: "what's exposed", "list sites", "add service", "check tunnels"