Skip to content

docs: add a Pangolin example - #61

Open
acouvreur wants to merge 2 commits into
mainfrom
feat/pangolin-example
Open

docs: add a Pangolin example#61
acouvreur wants to merge 2 commits into
mainfrom
feat/pangolin-example

Conversation

@acouvreur

@acouvreur acouvreur commented Aug 24, 2026

Copy link
Copy Markdown
Member

Adds a runnable example showing Sablier waking a container from a real Pangolin resource.

Pangolin isn't a reverse proxy of its own — it drives a Traefik v3 instance whose dynamic configuration it generates from its own database. So there's no Pangolin-specific plugin to build: this is the existing Traefik plugin, loaded next to Pangolin's own badger. What is Pangolin-specific is how the middleware gets attached to a resource, which is what the example is really about.

What's in it

examples/pangolin/ runs Pangolin 1.21.1, Traefik v3.7 (with the badger and sablier plugins), Sablier and a mimic workload on a single Docker host. It uses a Pangolin Local site, so no gerbil/newt tunnel is involved. setup.sh bootstraps the server admin, org, site and resource through Pangolin's API so the example works without clicking through the dashboard — the UI path is documented too.

Also adds a Pangolin section to the main README, under Usage.

Both attachment options are covered

Pangolin builds every router's chain as [badger, ...traefik.additional_middlewares] and has no per-resource middleware field. So:

  • Option A (what the example ships with): the global traefik.additional_middlewares knob. Clean and fully supported, but it applies to every HTTP resource — fine for one Sablier group, doesn't scale to a second app.
  • Option B: shadow the generated router with a higher-priority one in the file provider, reusing Pangolin's generated service via a cross-provider @http reference. This is what anyone with more than one app needs.

Verified

Ran the stack from a clean slate. The generated router comes back as:

{
  "name": "1-mimic-router@http",
  "rule": "Host(`mimic.localhost`)",
  "middlewares": ["badger@http", "sablier-mimic@file"],
  "service": "1-mimic-service",
  "priority": 100
}

Full lifecycle confirmed: first request → Sablier waiting page → container started and healthy → Mimic says hello! → after 1m idle, instance expired and the container stops.

Option B was verified separately: a priority-1000 file-provider router referencing 1-mimic-service@http takes over and serves the waiting page correctly.

Notes for reviewers

  • badger is pinned to v1.5.0. v1.6.x and v1.7.0 currently fail Traefik's plugin integrity check, and one bad plugin disables all plugins — so this silently breaks Sablier too. Upstream in the Traefik catalog, not something we control.
  • The root .gitignore has a bare traefik rule (presumably for a downloaded binary), which silently swallowed examples/pangolin/config/traefik/. I negated it in the example's own .gitignore rather than touching the shared rule — but anchoring it to /traefik instead would probably be the better fix.
  • Pangolin's gerbil config block turns out to be required even with no tunnel, hence the commented block in config/config.yml.

Companion docs PR in the main repo: sablierapp/sablier#1091 (closes sablierapp/sablier#1040)

🤖 Generated with Claude Code

Pangolin drives a Traefik v3 instance whose dynamic configuration it
generates from its own database, so no Pangolin-specific plugin is
needed. What is Pangolin-specific is how the middleware gets attached to
a resource, which is what this example demonstrates.

The stack runs Pangolin, Traefik (with the badger and sablier plugins),
Sablier and a mimic workload on one Docker host, using a Pangolin
"Local" site so no tunnel is involved. setup.sh bootstraps the admin,
org, site and resource through Pangolin's API.

Both attachment options are covered: the global
`traefik.additional_middlewares` knob used by the example, and a
higher-priority router in the file provider that reuses Pangolin's
generated service for per-resource control.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pangolin has no per-resource middleware field, so the example previously
relied on `traefik.additional_middlewares`, which applies to every
resource in the install. Middleware Manager -- a community service listed
in Pangolin's own docs -- reads resources from Pangolin's internal API
and re-serves Traefik's dynamic configuration with middlewares attached
per resource, which is what the example now demonstrates.

Traefik's http provider points at Middleware Manager instead of Pangolin,
and setup.sh attaches `sablier-mimic@file` to the single resource through
its API.

Documents all three attachment options with their trade-offs, including
one that is easy to get wrong: Middleware Manager always places its
additions before the router's existing middlewares, so Sablier runs
before `badger` and an unauthenticated request can wake the container.
The `priority` field only orders its own assignments and cannot move one
after `badger` -- verified against priorities 1 and -100.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@acouvreur

Copy link
Copy Markdown
Member Author

Pushed an update: the example now attaches Sablier per-resource via Middleware Manager rather than through the global traefik.additional_middlewares knob.

Middleware Manager is listed in Pangolin's own community guides. It reads resources from Pangolin's internal API and re-serves Traefik's dynamic configuration with middlewares attached per resource, so Traefik's http provider now points at it instead of at Pangolin. setup.sh attaches sablier-mimic@file to the one resource through its API.

Correction to an earlier claim

The original PR said badger always runs before Sablier, so the waiting page is only served to authenticated users. That is not true under Middleware Manager, and it's worth flagging because it's easy to assume.

Middleware Manager unconditionally places its own additions before the router's existing middlewares (config_proxy.go — "Merge middlewares (MW-manager additions first, then existing)"), and badger is one of the existing ones. The resulting chain is:

["sablier-mimic@file", "badger@http"]

The priority field only orders Middleware Manager's own assignments among themselves — it cannot move one after badger. I verified that empirically at priorities 1 and -100; the order did not change in either case.

The practical consequence is that an unauthenticated request can wake the container. Harmless for this public demo, but it matters on a zero-trust proxy, so the README now documents all three options as an explicit trade-off:

Per-resource Auth before wake Extra service
additional_middlewares ✗ global
Middleware Manager
Shadow the router

Also noted: Traefik's entire dynamic config now comes from Middleware Manager, so if that container stops, Traefik loses every Pangolin-generated router — not just the Sablier attachment.

Verified

Clean-slate run: setup.sh attached the middleware, the router came back as ['sablier-mimic@file', 'badger@http'] and enabled, first request served the Sablier waiting page, second returned Mimic says hello!.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Pangolin reverse proxy integration

1 participant