This plugin ships a vendored copy of chattolib and all its dependencies in
vendor/. Nothing has to be installed separately — the plugin brings its own
dependency tree.
hermes plugin install works immediatelyensure() or lazy_import mechanismsThree of the vendored packages contain compiled extension modules and are
therefore platform-specific: pyqwest (~15 MB, the HTTP core used by
connectrpc), protobuf and protobuf-py-ext. Everything else is pure
Python. The tree is split accordingly, so the shared part is stored once:
vendor/
├── __init__.py
├── common/ # pure-Python packages (~8 MB), all platforms
│ ├── chattolib/
│ ├── connectrpc/
│ ├── httpx/ httpcore/ h11/ anyio/ idna/ certifi/
│ └── opentelemetry/ protobuf/ typing_extensions.py
└── platform/ # compiled extensions, ~16 MB per platform
├── linux-x86_64/ # pyqwest/, protobuf_ext/, google/
├── linux-aarch64/
└── macos-arm64/
vendor_path.py derives the platform tag from platform.system() /
platform.machine() at import time and puts both directories on sys.path.
Both adapter.py and platform_config.py call setup_vendor_path() before
importing anything from chattolib.
| Directory | Position | Why |
|---|---|---|
platform/<tag> |
prepended | The compiled extensions must match the vendored chattolib. A host copy of protobuf at a different version would break the generated _pb code. |
common |
appended | The Hermes agent ships most of this itself — httpx (same pin, 0.28.1), certifi, anyio, httpcore, h11, idna, typing_extensions. A plugin must not shadow the host's pinned versions; Hermes pins certifi exactly, and prepending would substitute our CA bundle process-wide. |
So the host's pure-Python packages win, and our copies act as a fallback —
which keeps out-of-process use (the cron sender, hermes_standalone_sender_fn)
working in an environment without Hermes.
The packages Hermes does not provide — chattolib, connectrpc, pyqwest,
protobuf, protobuf-py, protobuf-py-ext — always come from vendor/. They
are also the bulk of it: pyqwest alone is ~15 MB per platform, so dropping
the packages Hermes already ships would save only ~2.7 MB of ~59 MB and buy a
dependency on Hermes' exact pins. Not worth it.
chattolib requires httpx>=0.27 and protobuf>=5.28; connectrpc requires
protobuf-py==0.1.1 and pyqwest>=0.5.1. If a future Hermes release pins
httpx below 0.27, the appended fallback no longer helps — the host copy would
be found first and be too old.
If the current platform was not vendored, the import fails with an explicit
message naming the available platforms — rather than a cryptic dlopen error
about an invalid Mach-O/ELF file.
# Default platform set: linux-x86_64, linux-aarch64, macos-arm64
./vendor_chattolib.sh
# A single platform
PLATFORMS="linux-x86_64" ./vendor_chattolib.sh
# Add another one (see the table below for valid tags)
PLATFORMS="linux-x86_64 linux-aarch64 macos-arm64 windows-amd64" ./vendor_chattolib.sh
# Pin a specific chattolib version
CHATTOLIB_SPEC="chattolib==0.4.20" ./vendor_chattolib.sh
The script wipes vendor/, installs the dependency tree once per platform via
uv pip install --python-platform …, splits the result with vendor_split.py,
and verifies that import chattolib works on the machine you ran it on.
Afterwards: git add vendor/.
| Tag | uv --python-platform |
vendored by default |
|---|---|---|
linux-x86_64 |
x86_64-unknown-linux-gnu |
✅ |
linux-aarch64 |
aarch64-unknown-linux-gnu |
✅ |
macos-arm64 |
aarch64-apple-darwin |
✅ |
linux-x86_64-musl |
x86_64-unknown-linux-musl |
— |
linux-aarch64-musl |
aarch64-unknown-linux-musl |
— |
macos-x86_64 |
x86_64-apple-darwin |
— |
windows-amd64 |
x86_64-pc-windows-msvc |
— |
All seven are available as binary wheels upstream; only the default three are
committed, to keep the repository at a reasonable size (~59 MB for vendor/).
The script resolves wheels for Python 3.11 (PYTHON_VERSION), which is the
project minimum. At that version the three binary packages still provide
cp310-abi3 wheels, and the stable ABI is forward compatible — so one build
covers 3.11 through 3.14. Resolving for 3.12+ would instead pull
version-specific wheels (cp312-…) that only work on that exact version.
If you raise PYTHON_VERSION, check that the resulting .so files are still
named *.abi3.so. If they are not, the vendored tree silently becomes
Python-version-specific.
pip install chattolib
A system-installed chattolib is only used if vendor/ is absent — the vendored
copy is deliberately prepended to sys.path and therefore wins.
The vendored chattolib is licensed under MPL-2.0 + Apache-2.0 (see
vendor/common/chattolib-*.dist-info/). Upstream source:
https://github.com/chattocorp/chatto