{"id":51162280,"url":"https://github.com/awelzel/zeekpy","last_synced_at":"2026-06-26T15:01:19.767Z","repository":{"id":361878332,"uuid":"1256240824","full_name":"awelzel/zeekpy","owner":"awelzel","description":"Pure Python library for consuming and publishing events via Zeek's WebSocket API heavily using type hints","archived":false,"fork":false,"pushed_at":"2026-06-08T16:42:46.000Z","size":57,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-08T18:25:00.516Z","etag":null,"topics":["async","asyncio","events","python","typehints","typing","websocket","zeek"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/awelzel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-01T15:35:57.000Z","updated_at":"2026-06-08T16:42:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/awelzel/zeekpy","commit_stats":null,"previous_names":["awelzel/zeekpy"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/awelzel/zeekpy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awelzel%2Fzeekpy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awelzel%2Fzeekpy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awelzel%2Fzeekpy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awelzel%2Fzeekpy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/awelzel","download_url":"https://codeload.github.com/awelzel/zeekpy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awelzel%2Fzeekpy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34821764,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-26T02:00:06.560Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","asyncio","events","python","typehints","typing","websocket","zeek"],"created_at":"2026-06-26T15:01:18.886Z","updated_at":"2026-06-26T15:01:19.756Z","avatar_url":"https://github.com/awelzel.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"zeekpy\n======\n\nPure Python library for consuming and publishing events via Zeek's WebSocket\nAPI heavily using type hints.\n\n\u003e This library isn't an official Zeek project. It's an exploration of\n\u003e an alternative Python binding API for Zeek and inspired primarily by\n\u003e [ZeekJS](https://github.com/corelight/zeekjs) and [FastAPI](https://github.com/fastapi/fastapi).\n\nUsage\n-----\n\nYou construct a zeekpy.Zeek object, passing it the WebSocket URI of the Zeek\ncluster to connect to and the optional topics to which you want to subscribe.\nThe WebSocket connection and Zeek handshake are done when entering the Zeek\nobject's context manager:\n\n```python\nzeek = Zeek(\"ws://127.0.0.1:27759/v1/messages/json\", topics=[\"/test/\"])\n\nwith zeek:\n    # Now connected to Zeek and subscribed to /test/\n    ...\n```\n\nTo handle events, you implement handler functions that have appropriate type annotations.\nYou use the types listed in the EventArg union in the zeekpy module.\n\nFor example, to register a handler function for the NetControl::pubsub_add_rules\nevent that receives a topic string, a pubsub_id that's a count and a vector of\nRule records, the Python code looks like:\n\n```python\nimport dataclasses\nfrom zeekpy import Zeek, addr, count\n\n@dataclasses.dataclass\nclass Rule:\n    a: addr\n    c: count\n    comment: None | str = None\n\nzeek = Zeek(\"ws://127.0.0.1:27759/v1/messages/json\", topics=[\"/test/\"])\n\n@zeek.on(\"NetControl::pubsub_add_rules\")\ndef handle_pubsub_add_rules(topic: str, pubsub_id: count, rules: list[Rule]):\n    print(topic, pubsub_id, rules)\n\nwith zeek:\n    zeek.consume()\n```\n\n\nPublishing Events\n-----------------\n\nUse Zeek.publish() to publish Zeek events to a topic. If an event argument is\nof a type that's in EventArg, it's serialized properly (think count, enum or port).\nIf an argument is a dict and has only \"@data-type\" and \"data\" keys, it is used\ndirectly in the JSON payload. To publish a Python int as a Zeek count, you need\nto wrap the int in a count instance: count(42). Otherwise, the 42 would be encoded\nas an integer. This is similar to the ZeekJS BigInt case. Similarly, for enum,\nthe library treats an enum like a str. For publishing a string as an enum, wrap\nit: enum(\"NetControl::DROP\").\n\n```python\n# Zeek event declaration:\n# global ev: event(c: count);\nfrom zeekpy import Zeek, count\n\nwith Zeek(\"ws://127.0.0.1:27759/v1/messages/json\") as zeek:\n    zeek.publish(\"/the/topic/\", \"ev\", [count(42)])\n```\n\nIt's okay to publish from within event handlers, replying to every ping\nevent with a pong event looks as follows:\n\n```python\nfrom zeekpy import Zeek, count\n\nzeek = Zeek(\"ws://127.0.0.1:27759/v1/messages/json\", topics=[\"/pings/\"])\n\n@zeek.on(\"ping\")\ndef handle_ping(c: count):\n    print(\"got ping, sending pong\", c)\n    zeek.publish(\"/pongs/\", \"pong\", [c])\n\nwith zeek:\n    zeek.consume()\n```\n\nEvent handlers are executed by the thread blocked in consume(), so the publish()\nis done in the context of that thread. You can also call zeek.publish() from a\ndifferent thread.\n\nType Conversions\n----------------\n\nIf a parameter of an event handler has no type annotation, the handler function\nreceives the corresponding dict from the parsed WebSocket JSON payload. You can\nalso annotate such parameters with RawArg to make this behavior explicit.\n\nWhen you annotate a parameter with addr, the handler function will receive\neither an ipaddress.IPv4Address or ipaddress.IPv6Address as produced by\nipaddress.ip_address(). The same applies to subnet. When annotating a parameter with\nport, the result will be an object that has two fields: port (int) and proto (str).\n\nFor records, use dataclasses.dataclass and standard type hints. The implementation\nknows how to instantiate and populate instances based on the listed fields.\nFor \u0026optional, use typing.Optional or the type | None notation. A fairly complex\nexample of a vector of records containing optional fields follows:\n\n```python\n# Zeek type and event declaration:\n# type R: {\n#     c: count;\n#     a: addr;\n#     oa: addr \u0026optional;\n#     f: double \u0026optional;\n# }\n#\n# global ev: event(rvec: vector of R);\nfrom zeekpy import Zeek, addr, count\n\n# Python type declaration for R.\n@dataclasses.dataclass\nclass R:\n    c: count\n    a: addr\n    oa: addr | None = None\n    f: float | None = None\n\n# Usage\nzeek = Zeek(...)\n\n@zeek.on(\"ev\")\ndef ev(rvec: list[R]):\n    pass\n\nwith zeek:\n    zeek.consume()\n```\n\nNote on Types\n-------------\n\nIf you look at the types listed in the EventArg union, you'll find a mix of native\nZeek and native Python types. This is on purpose. The rough rule is that the native\nPython type is used when there's a direct mapping possible (bool, datetime, list,\nfloat, str, dataclasses). Otherwise, when there's no direct mapping (addr, subnet),\nit's just a tagged Python type (count, enum), or the port type that's really a\ncomposite type.\n\nSets and Tables\n---------------\n\nSets and tables are not implemented. The author doesn't think it's a good idea\nto use them for remote events. Annotate them with RawArg and convert them\nyourself from the raw dictionary. Zeek's support for composite keys makes this\ncumbersome and the use cases aren't clear. It could technically make sense to\nallow composite keys using tuples, e.g., set[tuple[int, str]]. Feel free top\nopen a PR if you need this.\n\nAsync Support\n-------------\n\nThe zeekpy module contains an AsyncZeek class that mirrors the Zeek class.\nYou use async with and await to work with it. It's otherwise very similar\nto the Zeek class. Not that you'll get concurrent event handler invocations\nwhen handlers use await for IO which will change execution order and may be\nconfusing. Only use if you're comfortable writing async code.\n\n```python\nimport asyncio\nfrom zeekpy import AsyncZeek, count\n\nzeek = AsyncZeek(\"ws://127.0.0.1:27759/v1/messages/json\", topics=[\"/pings/\"])\n\n@zeek.on(\"ping\")\nasync def handle_ping(c: count):\n    print(\"got ping, sending pong\", c)\n    await zeek.publish(\"/pongs/\", \"pong\", [c])\n\nasync def main():\n    async with zeek:\n        await zeek.consume()\n\nasyncio.run(main())\n```\n\nContributing\n------------\n\nContributions are welcome. Keep it simple.\n\nThe context manager is used to connect and disconnect properly and consume()\nand stop() to cancel. If you find edge cases or bugs in that area, feel free\nto open PRs to improve this logic, I'm not an asyncio expert.\n\nAfter committing changes, run the following commands to verify everything\nis still working and looks good:\n\n```bash\nuv run ruff check\nAll checks passed!\n\nuv run ruff format\n11 files left unchanged\n\nuv run ruff check\nAll checks passed!\n\nuv run pytest\n...\n```\n\nIf ``zeek`` is in your PATH, integration tests against a live\nZeek WebSocket will be executed, otherwise they're skipped.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawelzel%2Fzeekpy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fawelzel%2Fzeekpy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawelzel%2Fzeekpy/lists"}