Model or dataset
beelzebub-labs/beelzebub avatar
beelzebub-labs/beelzebub

Beelzebub: An LLM-Backed Deception Runtime for SSH, HTTP, TCP, TELNET and MCP

A secure low code deception runtime framework, leveraging AI for System Virtualization.

2,176 stars209 forksGoGPL-3.0

At a glance

What is it?
Beelzebub is a Go deception runtime that answers attackers with LLM-generated text instead of canned banners, and watches MCP endpoints for prompt injection. It is worth adopting if you can accept a GPL-3.0 runtime, an external model dependency, and a configuration surface that punishes careless reuse.
Who is it for?
Adopt Beelzebub if you already run internal-facing decoys and want LLM-generated responses plus an MCP listener in a single Go binary, and if GPL-3.0 across the runtime is acceptable for how you distribute it. Do not adopt it if you need a host that survives a determined attacker, if you cannot host or pay for a model endpoint, or if you only need a static banner responder, where a smaller tool will do.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The gap Beelzebub is aimed at: static decoys and unguarded MCP endpoints

Classic honeypots answer with fixed strings. An attacker who sends an unexpected command gets a canned reply or a dropped connection, and that mismatch is often enough for them to walk away. Beelzebub's stated goal is the opposite: the README describes it as a deception runtime that "goes beyond passive honeypots by actively engaging attackers in realistic interactions". The engagement is produced by an LLM, so the reply to an unseen command is generated rather than looked up.

The second target is newer. The project lists MCP among its supported protocols and names prompt injection detection against AI agents as a feature. MCP deception is a decoy MCP server: an agent that connects to it is an attacker or a misconfigured client, and the traffic it sends is the signal. This is the part of the project that is hardest to replace with an off-the-shelf honeypot, because most honeypots have no MCP listener at all.

The audience follows from that. It is for teams that already accept deception as a detection layer and want the decoy to hold a conversation, and for teams putting agents on internal networks who want a place for a stray agent to land. It is not a scanner, not an EDR, and not a tool for hardening the hosts it runs on.

How a request becomes an LLM reply: services, regex matching, plugins

The runtime is built from two configuration layers. A core configuration file holds runtime settings and is passed with --conf-core, defaulting to ./configurations/beelzebub.yaml. A services directory holds one definition per decoy and is passed with --conf-services, defaulting to ./configurations/services/. The README calls the service layer low-code: YAML plus regex command matching, with no custom code needed to stand up a new decoy.

When a connection arrives, the service definition decides what happens. Matched commands can be answered from configuration, and unmatched or open-ended input is where the LLM integration comes in. The README states that OpenAI and Ollama are the supported integrations and that responses are generated "in real time". The model is therefore a runtime dependency of the deception path, not an offline training step. If the model endpoint is unreachable, the quality of the interaction degrades to whatever the YAML can answer on its own.

Everything else hangs off the plugin system, which the README describes as a stable public SDK at pkg/plugin. Three interfaces are documented. CommandPlugin returns a string and covers SSH, TCP, TELNET and HTTP. HTTPPlugin returns a full HTTPResponse with status code, headers and body. WirePlugin operates on binary TCP exchanges and can observe or rewrite them, with an optional WireSessionCloser to release per-connection state. Plugins register via init(), which is why the Makefile installs declared plugins and compiles them in rather than loading them at runtime. Wire plugins are not implicit: a TCP service lists them by name and in execution order under wirePlugins, for example a single entry of vnc.

Getting it running: installer, Makefile targets, Helm, and validation

The README gives four entry points. The installer script asks whether you want local or Docker, checks prerequisites and starts the runtime; the non-interactive forms are ./install.sh --local and ./install.sh --docker, and ./install.sh --local --no-run installs and builds without starting. It also notes a constraint worth reading twice: on non-root hosts, local installation does not auto-start when the default configuration includes privileged ports. SSH and TELNET decoys on their standard ports are exactly that case.

The Go path is make start, which the README says installs any declared plugins, compiles them in, and runs. The container path is make docker, which builds an image with declared plugins baked in. Kubernetes uses the bundled chart: helm install beelzebub ./beelzebub-chart, with helm upgrade for later revisions.

The runtime itself is one command with three flags. beelzebub run takes -c or --conf-core, -s or --conf-services, and -m or --mem-limit-mib, an integer in MiB where -1 disables the limit and the default is 100. That default is the first thing to change on a host running several decoys, since it is a per-service ceiling and LLM-backed sessions are not free.

beelzebub validate parses and validates the whole configuration set without starting anything, and the README points at docs/configuration-validation.md for the rule reference. Running validate in CI is the cheapest way to catch a broken service file, because a malformed decoy that fails at startup is a decoy that is not collecting anything. beelzebub version prints version, commit SHA, build date and Go runtime information, which is what you want in a bug report. Plugin management is three subcommands: beelzebub plugin install github.com/your-org/beelzebub-myplugin, beelzebub plugin list, and beelzebub plugin remove myplugin.

Where the design bites: model dependency, per-service memory, and config reuse

The honest limitation is that the headline feature is also the weakest link. Adaptive responses require a reachable model, and the README does not describe a fallback path for when the provider is down or slow. An attacker who notices that replies arrive with uneven latency, or that the decoy suddenly stops improvising, has a fingerprint. The same applies to cost: every generated reply is a billable or resource-consuming inference, and a decoy that invites long conversations invites long inference.

The memory limit is a blunt instrument. --mem-limit-mib is a single integer applied to the runtime, defaulting to 100 MiB. It protects the host from a runaway decoy, but it is not a per-conversation budget, so it does not bound how much inference a chatty attacker can trigger.

Configuration reuse is the third trap. Service definitions are YAML with regex matching, which makes copying one decoy into another environment easy and makes copying its hostnames, banners and credentials just as easy. The README's own install note about privileged ports is a reminder that the default configuration is written for a root host, and defaults travel with copies.

Finally, the project describes itself as a research project in its repository topics. Treat it as software that changes: three releases landed in roughly three months between v3.8.0 and v3.9.1. A decoy is only useful if it stays up, so pin the version you deploy and read the release notes before moving.

Observability and the operational shape of a deployment

The README lists two output paths. Prometheus metrics cover the runtime's own state, and RabbitMQ integration streams events off the host. The split matters: metrics tell you the decoy is alive and receiving connections, while the event stream is where interaction content goes for analysis. If you only scrape metrics, you learn that someone connected, not what they typed.

That is also where the data handling question sits. Beelzebub exists to collect high-fidelity threat intelligence from interactions, which means attacker-supplied text is the payload. When the LLM integration is active, that text becomes prompt text sent to OpenAI or Ollama. The README does not describe prompt logging or retention behaviour on the provider side, so the provider's terms govern it. For an Ollama instance you control, the boundary is your own network. For a hosted API, it is not.

The plugin interfaces reinforce the operational model. CommandPlugin and HTTPPlugin are synchronous, returning a value per request. WirePlugin is different: it receives a WireContext with raw request and response bytes, the matched command, service identity, history and connection information, and may rewrite the exchange. Rewriting binary protocol traffic is powerful and easy to get wrong, which is why wire plugins are opt-in per TCP service and ordered explicitly in YAML rather than enabled globally.

What Beelzebub is not: the case for a static honeypot instead

The obvious alternative is a conventional low-interaction honeypot such as Cowrie, or a static banner responder written as a small service. The difference is not quality, it is mechanism. A static honeypot matches input against a fixed set of emulated commands and returns pre-written output; it has no model in the loop, no inference latency, no per-reply cost, and no external dependency that can fail mid-session. Its behaviour is identical on every deployment, which makes it reproducible and easy to reason about in a report.

Beelzebub trades that determinism for coverage of inputs nobody anticipated. If your threat model is opportunistic scanning, a static responder logs the scan and costs nothing. If your threat model includes a human or an agent that stays in the session and probes for inconsistency, generated replies are the point, and the model dependency is the price. Pick based on which of those two you actually face, not on which sounds more advanced.

The MCP side has no direct equivalent in the static tools. If the reason you are evaluating Beelzebub is agent-facing deception, the comparison to a classic honeypot is not the relevant one, because a classic honeypot will not speak MCP at all.

Licence and maintenance: GPL-3.0 across the runtime

Beelzebub is GPL-3.0. That is a copyleft licence, and it applies to the runtime you deploy. The practical consequence most teams hit is distribution: if you ship a modified Beelzebub inside a product, or link it into something you distribute, the GPL's source-availability terms come into play. Running it internally as a decoy service is a different situation from redistributing it, and the plugin SDK raises its own question about whether a plugin compiled into the binary via init() is a derivative work. The README does not address that question, and this is not legal advice; if your deployment involves redistribution, get an answer from counsel rather than from a README.

Maintenance cost is dominated by two things. First, the release cadence: v3.8.0 in June, v3.9.0 in August, v3.9.1 at the end of August, all in 2026, with the repository last pushed in September 2026. Each upgrade is a chance that a service definition or plugin interface moved. Second, the model integration: OpenAI and Ollama are named as the supported providers, and provider API changes land on their own schedule, independent of Beelzebub's releases. Budget for both.

What to verify before you commit: run beelzebub validate against your real service directory, decide the --mem-limit-mib value you actually want rather than accepting 100, confirm whether your host can bind the ports your decoys need without root, and confirm where your prompts are stored.

Editorial conclusion

Adopt Beelzebub if you already run internal-facing decoys and want LLM-generated responses plus an MCP listener in a single Go binary, and if GPL-3.0 across the runtime is acceptable for how you distribute it. Do not adopt it if you need a host that survives a determined attacker, if you cannot host or pay for a model endpoint, or if you only need a static banner responder, where a smaller tool will do. Before deploying, run beelzebub validate against your service directory, inspect the memory limit you actually want instead of the 100 MiB default, and check whether the model provider you configure logs prompts, because everything an attacker types into a decoy becomes prompt text.

Official sources

  1. beelzebub-labs/beelzebub on GitHub
  2. License: GPL-3.0
  3. Project website
  4. README
  5. Releases
Community notes

Community notes