- TypeScript 90.6%
- Shell 6.1%
- JavaScript 3.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .forgejo | ||
| build | ||
| docs | ||
| readme | ||
| scripts | ||
| src | ||
| test/unit | ||
| .gitignore | ||
| LICENSE | ||
| NOTICE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| tsconfig.types.json | ||
| vite.loader.config.ts | ||
| vite.npm.config.ts | ||
| vitest.config.ts | ||
pigeons-loader
English · Português (Brasil) · Español
The script you add to your site to show the Pigeons chat. It draws the chat button, opens the chat in an iframe served from the Pigeons domain, and passes your commands to it. This repository holds the loader's source, so you can read exactly what runs on your page and check that the published file was built from it.
| Desktop | Phone |
|---|---|
![]() |
![]() |
What runs on your page
- Only the loader. No dependencies, about 7 KB compressed. It never calls the Pigeons API, never loads third-party scripts and never sees the visitor's session.
- The chat lives in an iframe on the Pigeons domain. Your scripts and CSS cannot read the conversation or break the chat, and the chat cannot read your page.
- A checked channel between the two. Every message uses an exact target origin (never
*), both sides check the sender's origin and window, and after a handshake they talk over a privateMessageChannel. Anything that does not match the versioned envelope is dropped.
Quick start
<script>
(function (w) { w.Pigeons = w.Pigeons || function () { (w.Pigeons.q = w.Pigeons.q || []).push(arguments); }; })(window);
Pigeons('init', { scope: '<scope uuid>' });
</script>
<script async src="https://widget.pigeons.digital/loader/<version>/loader.js" integrity="sha384-<hash>" crossorigin="anonymous"></script>
Paste it before </body> and replace <scope uuid> with the uuid of your platform or organization in Pigeons.
The newest src and integrity are always in https://widget.pigeons.digital/loader/latest.json
({"version", "url", "integrity"}).
Or, with a bundler: npm install @echosistema/pigeons-loader, then import { Pigeons } from '@echosistema/pigeons-loader'
(typed, same commands; see install.md).
Where to get it
| Source | Address | Updates |
|---|---|---|
| Pigeons | https://widget.pigeons.digital/loader/<version>/loader.js |
never: each version is immutable, so its integrity never breaks |
| Pigeons | https://widget.pigeons.digital/loader/<version>/integrity.txt |
never: the SRI of that file |
| Pigeons | https://widget.pigeons.digital/loader/latest.json |
every release: version, url and integrity of the newest one |
| Pigeons | https://widget.pigeons.digital/loader/latest/loader.js |
every release (5 min cache); use it without integrity |
| npm | @echosistema/pigeons-loader |
when you update the package; a new version appears once Pigeons approves it on npm |
| jsDelivr | https://cdn.jsdelivr.net/npm/@echosistema/pigeons-loader@<version>/dist/loader.js |
never (same file as npm) |
All of them are the same file for a given version, with the same SRI. Versions follow semver: a patch or minor release never breaks the commands or events. The list of versions is in the tags.
Commands
| Command | Effect |
|---|---|
Pigeons('init', { scope, locale?, theme? }) |
Mounts the widget |
Pigeons('identify', { userId, hash }) |
Visitor logged in on your site (hash computed on your backend) |
Pigeons('reset') |
Logout: the next visitor starts a new conversation |
Pigeons('open') / Pigeons('close') |
Opens or closes the chat |
Pigeons('setTheme', { color?, style?, mode? }) / Pigeons('setLocale', 'pt' | 'en' | 'es') |
Changes look or language without a reload |
Pigeons('on', 'unread' | 'opened' | 'closed', fn) |
Events |
Guides
| Guide | English | Português | Español |
|---|---|---|---|
| Installation: snippet, options, logged-in visitors, events, CSP, privacy, troubleshooting | install.md | install.pt-BR.md | install.es.md |
| Theming: colours, styles, light/dark, your own CSS | theming.md | theming.pt-BR.md | theming.es.md |
| Features: what the chat does inside the iframe | features.md | features.pt-BR.md | features.es.md |
Check the published file
Build the tag of the release you use and compare its hash with the integrity of your snippet:
git checkout v<version>
npm ci
npm run build
npm run sri # prints sha384-… for dist/loader.js
The two values must be the same. Use Node 22, the release builds' version: another Node major can produce other
bytes. The build uses the production iframe address
(https://widget.pigeons.digital/); PIGEONS_WIDGET_URL points it elsewhere.
Development
npm install
npm run typecheck
npm test
npm run build # dist/loader.js (script tag) + dist/index.js and dist/types/ (npm module)
npm run size # budget: 15 KB gzip
Layout: src/loader/ is the code that runs on your page, src/shared/ holds the bridge protocol, the languages and
the theme rules it shares with the chat.
This repository is published from the Pigeons widget's main repository at each release, so pull requests are not merged here. To report a problem, open an issue; for a security problem, do not open a public issue: contact Pigeons support privately.
Limits
- A page that sends
Cross-Origin-Embedder-Policy: require-corpcannot frame the chat: the iframe loads Turnstile, the realtime service and media from other origins. The loader itself loads fine there (CORS withcrossorigin). - The chat iframe is created on
init, on every page view; it stays hidden until it is opened.
License
Apache License 2.0. See NOTICE.

