Why I Built Trishul SMI and Trishul SNMP — And What They Fixed
The real failures, architecture decisions and project results behind two independent open-source Python libraries for MIB compilation and SNMP.
Tools in this post: Trishul SMI, Trishul SNMP, Trishul SNMP Suite, Trishul RAM
I set out to build better network-management tools. Instead, I kept spending time fixing the foundations they depended on.
My early Trishul projects were about practical telecom work: managing MIBs, decoding notifications, querying devices and building tools that engineers could use without assembling a different set of scripts for every task. But as the projects grew, the same MIB compilation and SNMP integration problems kept coming back.
It was not that PySMI and PySNMP were useless. They were capable tools, and they are maintained projects. The problem was the fit between their architecture and what I wanted my applications to do. Every workaround I added became another thing to maintain.
Eventually, I had to ask a different question: what if the MIB compiler and the SNMP runtime were small, reusable foundations I controlled, rather than problems each application had to work around?
That question led to Trishul SMI and Trishul SNMP. The two libraries are independent on purpose. This is why I built them, the decisions that mattered, and where the effort has — and has not — paid off.
The work I actually wanted to do
The original Trishul FCAPS toolbox and the early Trishul SNMP Suite dealt with the messy parts of everyday SNMP work: translating numeric OIDs into meaningful names, handling vendor MIB files, simulating devices, sending and receiving traps, and inspecting walk results.
In parallel, I was developing Trishul RAM, a telecom data-mediation engine. It also needed MIB/schema support and SNMP integrations. Different applications were encountering the same underlying issues.
The repository history records concrete warning signs, not just a vague preference for newer libraries. In April 2026, the TRAM development record included a PySNMP MibBuilder failure and a package-version mismatch. The original SMI design notes documented failures and weak spots in the then-studied PySMI code paths: uninitialized variables in edge cases, missing HTTP timeouts, unclosed sessions or file handles, circular imports and an opaque **options interface.
Those are specific observations from the versions and integration paths I examined; they are not a claim that every current PySMI or PySNMP release still has all those problems. But they explain why I no longer wanted critical application behavior to depend on another round of local workarounds.
There was also an architectural cost. The old Suite ran SNMP work through separate worker processes and loopback communication. MIB loading and SNMP execution had become operational concerns — processes, coordination and failure handling — instead of straightforward library calls.
For an application developer, that is the wrong place to spend energy.
Why I didn’t simply keep patching the old stack
There were legitimate alternatives: fix individual issues, wrap the existing libraries more carefully, or build components with a different contract. I chose the last path for the parts I needed.
My original Trishul SMI design notes say the important part plainly: the difficulty was architectural, not just a bug list. Reader, parser, resolver and output-generation responsibilities were tightly entangled for my use case. A complete rewrite was not the cheapest way to compile one MIB; it was a way to stop paying the integration cost across several projects.
That choice came with responsibility. Owning a compiler and a protocol implementation means owning correctness, security, interoperability, regressions and releases too. I did not assume a rewrite would automatically be faster or more reliable. The later test and benchmark work was necessary precisely because the decision needed evidence.
Trishul SMI: make MIB knowledge useful beyond one runtime
I began Trishul SMI on April 29, 2026. Its job is specific: read SMIv1/SMIv2 MIB definitions, resolve imports and numeric OIDs, and compile them into structured, portable data.
The decisive design choice was JSON first.
PySMI can generate JSON too. But my application architecture had been organized around PySNMP-style generated Python modules. Those modules are useful for that ecosystem, yet a web UI, service, simulator or a runtime written in another language should not have to load Python registration code just to understand a MIB.
Trishul SMI instead emits versioned JSON modules, with optional bundle manifests and OID indexes. The data can preserve information such as numeric OID paths, textual conventions, enumerations, units, descriptions and constraints. Consumers can build symbolic lookup, search and validation behavior on top of the same artifact.
That has practical consequences:
- The MIB compiler is not tied to the polling process. Compile a collection once, then load the resulting artifacts wherever they are needed.
- Failures are visible. A module with unresolved required dependencies is reported as failed rather than silently being treated as a complete semantic artifact.
- The compiler fits development workflows. It supports local and optional online sources, caching, lint checks, conservative auto-fixes, watch mode and reproducible output.
- The output contract can be reused. The Python SNMP library and the later Rust runtime can consume compatible bundles without embedding the compiler.
The first releases still exposed a hard lesson: passing our hand-made fixtures did not mean compiling real vendor MIBs. The first published version ran into real IF-MIB/IP-MIB compatibility problems, which led to stronger release smoke tests. Ownership made the fixes possible, but it did not make the mistakes disappear.
A later design decision reinforced the JSON-first direction. In September, I removed the old built-in PySNMP .py formatter from Trishul SMI’s core and supported that compatibility path through an optional formatter plugin. Applications that need PySNMP output can opt in; the compiler’s primary purpose stays focused.
Why Trishul SNMP needed to be a separate library
A better MIB compiler does not solve SNMP execution. Devices still need to be queried; traps still need to be decoded; requests, timeouts and protocol/security handling still need a runtime.
I started Trishul SNMP on May 6, 2026 as an async-first Python implementation of that runtime. Its BER/ASN.1 and SNMP wire handling live in the package rather than depending on PySNMP. The base package has no required runtime dependencies; SNMPv3 privacy features use an optional crypto extra.
Just as important is what it does not do. Trishul SNMP can poll numeric OIDs without installing or importing Trishul SMI. A JSON MIB bundle is optional enrichment for human-readable symbols and richer values, not a prerequisite for sending an SNMP request.
I deliberately kept the scope bounded: read-only manager operations, walks, notifications, offline decoding and a narrow responder/simulator. It is not a full SNMP agent framework or a drop-in implementation of every PySNMP API. In particular, SET operations are outside its current documented scope.
That separation is the architectural payoff:
Trishul SMI knows what a MIB means. Trishul SNMP knows how to communicate over SNMP. An application can use either one, or combine them.
What changed in my actual projects
A library is useful when it improves an application, not merely when it has an attractive README. Two projects give the clearest evidence.
1. Trishul SNMP Suite: from process plumbing to in-process components
On May 21, 2026, the Suite’s 2.0 rewrite adopted Trishul SNMP for protocol operations and Trishul SMI for MIB compilation and bundle handling. The larger rewrite also changed the backend and persistence architecture, so it would be misleading to credit every improvement to these libraries alone.
The meaningful change was simpler responsibility boundaries: SNMP operations could run in-process, while compiled MIB data could support the browser, simulator and operator tools without the old PySNMP subprocess arrangement.
That architecture continued to be tested against realistic collections. The October 2.2.4 Suite release documented validation involving 429 compiled modules and 104,429 objects. The point is not the biggest number; it is that the application could use an independently compiled MIB catalog as a shared foundation.
2. Trishul RAM: a measured difference in polling overhead
TRAM adopted the new stack more cautiously. The September integration was initially controlled by a feature flag while SNMPv1 and SNMPv3 interoperability gaps were closed. After wire-level comparison and additional hardening, the Trishul stack became the default in TRAM 1.8.0.
The October 8 polling study measured 93.9 ms median for a legacy-stack GET poll versus 3.0 ms for the Trishul stack on the same local synthetic responder — approximately 31× faster for that specific operation.
That result needs its context: the legacy path paid roughly 90 ms of PySNMP engine setup on each poll. The new stack avoided that overhead. These were 50 local loopback repetitions, not a promise of 31× faster end-to-end monitoring on real networks. When dispatch and sink writes dominated full pipeline runs, the two stacks were much closer. The study also recorded parity or smaller differences on walks, depending on workload.
For me, the useful conclusion was not simply “31× faster.” It was that removing an avoidable fixed cost in the runtime made a measurable difference to a real consumer application.
Did the compiler rewrite pay off? The benchmarks
In October I tested Trishul SMI 0.5.3, with its optional PySNMP formatter plugin, against PySMI 1.6.3 and PySMI 2.0.0. The benchmark campaign covered 2,782 unique MIB files across Cisco, Juniper, Nokia/Alcatel-Lucent and IETF sources. The detailed results and runners are currently private research artifacts, so this article presents a limited summary rather than claiming publicly reproducible evidence.
Here is a small, representative slice of the three-run WSL2 Linux results (cold-cache median; lower is better):
| 2,782-MIB workload | Trishul SMI | PySMI 1.6.3 | PySMI 2.0.0 |
|---|---|---|---|
Generate PySNMP .py modules, offline |
107 s | 195 s | 202 s |
| Generate JSON, with remote dependency fetching | 115 s | 415 s | 579 s |
| Generate JSON from local sources only | 91 s | 32 s | 57 s |
Two wins stand out: PySNMP-compatible Python generation and online dependency resolution. The JSON-online test particularly benefits from concurrent fetching. But for offline JSON-only generation, PySMI was faster and used less memory under these conditions. I do not want to hide that.
There are important comparison limits:
- The Trishul benchmark runner worked around 28 mutually importing Cisco MIBs so the aggregate run could proceed. That circular-dependency workaround was in the test harness, not in the Trishul SMI 0.5.3 release. Treat aggregate performance as patched-harness evidence, not unmodified release behavior.
- Compilers did not emit the same number or type of successful files. Trishul SMI was strict about required imports; permissive PySMI configurations could emit partial/stubbed outputs. That matters when comparing raw “successful” counts or output sizes.
- The earlier approximately 9× cold-speed figure in project documentation came from a smaller, different benchmark. It should not be substituted for these results.
- Online timing includes remote-server and network conditions. These are measured workloads, not universal speed guarantees.
The deeper lesson is about trust in the artifact. In the study, Trishul SMI isolated failures instead of silently fabricating missing symbols, whereas permissive PySMI paths could report partial outputs as compiled. That is a conscious trade-off: I would rather see an unresolved dependency while building a catalog than discover it later inside an application. It also means I must keep improving compatibility with imperfect vendor MIBs.
The underlying study also examines error classification, normalized text output, caching, memory, cross-platform runs and tsmi lint --fix. The raw datasets and harnesses should undergo a disclosure review before any separate public benchmark report is released.
What I would still improve
Building the stack did not remove the hard parts of SNMP. Real agent interoperability exposed protocol/security mistakes that unit fixtures missed. Vendor MIBs still contain grammar quirks, missing dependencies and cycles. The benchmark’s Cisco-cycle workaround remains a limitation to resolve in a released compiler rather than a feature to advertise today.
The two libraries also have deliberate boundaries. If you need a mature, broad PySNMP-compatible API or only a fast offline JSON conversion for a complete local corpus, compare alternatives honestly. Trishul’s value is its portable MIB contract, modular runtime, control over integration and suitability for the workflows it was built for, not a claim to replace every SNMP tool.
Try either tool independently
Use Trishul SMI if you are building a MIB browser, metadata catalog, telemetry pipeline, simulator or service that needs resolved, inspectable MIB data:
pip install trishul-smi
tsmi compile IF-MIB --online --emit-manifest --emit-oid-index -o ./out
The online option fetches missing source definitions; it is opt-in. See the source, CLI and Python API or PyPI package.
Use Trishul SNMP if you need an async Python SNMP manager, notification tooling or a focused simulator, even without a MIB compiler:
pip install trishul-snmp
See its Python API and examples or PyPI package. SNMPv3 privacy capabilities require the optional v3 extra; check the documentation for supported security modes.
Both projects are open source. You can try either one without adopting the rest of Trishul.
What started as recurring friction in my own projects became a reusable foundation across them. Building these libraries was not the shortest path to one successful SNMP request. It was a decision to stop solving the same class of problems in every application — then validate, with real MIBs and real consumers, whether that decision held up.