{"id":49119194,"url":"https://github.com/bubustack/livekit-webhook-impulse","last_synced_at":"2026-04-21T10:01:25.197Z","repository":{"id":352189310,"uuid":"1094017470","full_name":"bubustack/livekit-webhook-impulse","owner":"bubustack","description":"LiveKit webhook Impulse for bobrapet — triggers StoryRuns on room and participant lifecycle events.","archived":false,"fork":false,"pushed_at":"2026-04-18T10:04:52.000Z","size":57,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-18T11:28:50.267Z","etag":null,"topics":["bubustack","go","impulse","kubernetes","livekit","realtime","trigger","webhook"],"latest_commit_sha":null,"homepage":"https://bubustack.io/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bubustack.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":"SUPPORT.md","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},"funding":{"github":["bubustack"]}},"created_at":"2025-11-11T06:23:34.000Z","updated_at":"2026-04-18T10:04:43.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bubustack/livekit-webhook-impulse","commit_stats":null,"previous_names":["bubustack/livekit-webhook-impulse"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/bubustack/livekit-webhook-impulse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bubustack%2Flivekit-webhook-impulse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bubustack%2Flivekit-webhook-impulse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bubustack%2Flivekit-webhook-impulse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bubustack%2Flivekit-webhook-impulse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bubustack","download_url":"https://codeload.github.com/bubustack/livekit-webhook-impulse/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bubustack%2Flivekit-webhook-impulse/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32086815,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-21T06:27:27.065Z","status":"ssl_error","status_checked_at":"2026-04-21T06:27:21.250Z","response_time":128,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["bubustack","go","impulse","kubernetes","livekit","realtime","trigger","webhook"],"created_at":"2026-04-21T10:01:21.399Z","updated_at":"2026-04-21T10:01:25.179Z","avatar_url":"https://github.com/bubustack.png","language":"Go","readme":"# 🎙️ LiveKit Webhook Impulse\n\n[![LiveKit](https://img.shields.io/badge/LiveKit-Webhook-FF5A5A?style=for-the-badge\u0026logo=livekit)](https://livekit.io/)\n\nA stateless, production-ready **Impulse** for the [bobrapet](https://github.com/bubustack/bobrapet) workflow engine that acts as a secure bridge between your [LiveKit](https://livekit.io/) projects and your automated workflows.\n\nThis impulse listens for signed webhook events from a LiveKit server, validates their authenticity, and submits durable `StoryTrigger` requests in your bobrapet cluster. The controller then resolves those requests into `StoryRun`s. It is designed to be a simple, robust, and highly scalable entry point for any workflow that needs to react to real-time events in your LiveKit rooms.\n\n---\n\n## 🌟 Highlights\n\n- Verifies signed LiveKit webhooks before any workflow trigger is submitted.\n- Filters events, participants, and rooms before creating durable `StoryTrigger` requests.\n- Supports policy-based routing so the same impulse can start different realtime stories.\n- Stays stateless and easy to scale behind a normal Kubernetes Service/Ingress.\n\n## 🏗️ Architecture\n\nAs an **Impulse**, this component's role is to act as a *trigger*. It is the \"when\" of your system, connecting an external event source (LiveKit) to the \"what\" (a Story).\n\n```mermaid\ngraph TD\n    subgraph \"External World\"\n        A[LiveKit Server] -- Signed Webhook --\u003e B(LiveKit Impulse);\n    end\n\n    subgraph \"Kubernetes Cluster (bobrapet)\"\n        B -- Submits --\u003e C(StoryTrigger);\n        C -- Resolves to --\u003e D(StoryRun);\n        D -- Manages --\u003e E{StepRun 1\u003cbr\u003eEngram A};\n        D -- Manages --\u003e F{StepRun 2\u003cbr\u003eEngram B};\n    end\n```\n\nThe impulse is intentionally minimal and stateless, focusing on three core responsibilities:\n\n1.  **Webhook Verification**: Ensures incoming webhooks are authentic using JWT signature verification.\n2.  **Event Filtering**: Filters events based on a configurable allowlist and ignores events from self-identified agents to prevent infinite loops.\n3.  **Story Triggering**: Uses the Bubu SDK to submit the target `StoryTrigger`, passing a clean, structured representation of the LiveKit event as inputs. The controller then creates or reuses the target `StoryRun`.\n\nAll complex routing logic (e.g., \"run Story A for `participant_joined` and Story B for `room_finished`\") should be handled by a dedicated **Router Story** triggered by this impulse. This keeps the impulse lean and allows you to evolve your routing logic without redeploying this component.\n\n---\n\n## 🚀 Quick Start\n\n1. Create a Kubernetes secret containing your LiveKit API key and secret:\n   ```yaml\n   apiVersion: v1\n   kind: Secret\n   metadata:\n     name: my-livekit-keys\n   stringData:\n     API_KEY: \"LK...\"\n     API_SECRET: \"...\"\n   ```\n2. Apply the `Impulse.yaml` template and create an `Impulse` resource that points at your target Story.\n3. Expose the Service on port `8080` with a Kubernetes `Service`/`Ingress`.\n4. Register the same webhook URL and credentials in your LiveKit project.\n\n## ⚙️ Configuration (`Impulse.spec.with`)\n\nThe impulse is configured via its `Impulse.yaml` template.\n\nThe target Story is specified via the standard `Impulse.spec.storyRef` field.\n\n| Parameter                | Type       | Description                                                              | Default                                  |\n| ------------------------ | ---------- | ------------------------------------------------------------------------ | ---------------------------------------- |\n| `storyRef.name`          | `string`   | Target Story to trigger (required by Impulse CRD).                       | **Required**                             |\n| `storyRef.namespace`     | `string`   | Namespace of the target Story (optional, defaults to same namespace).    | -                                        |\n| `with.eventsAllowlist`   | `[]string` | List of LiveKit event names to accept (supports `*` and `foo*`).          | `[\"participant_joined\", \"room_started\"]` |\n| `with.selfIdentities`    | `[]string` | Participant identities to ignore (e.g., your bot/agent identities).      | `[\"bubu-agent\"]`                         |\n| `with.path`              | `string`   | The HTTP URL path for the webhook listener.                              | `/webhook`                               |\n| `with.policies`          | `[]Policy` | Optional routing policies (match on event/room/participant + override story \u0026 inputs). | `[]`                                      |\n| `secrets.livekitWebhook` | `secret`   | A Kubernetes secret containing `API_KEY` and `API_SECRET` for JWT validation. | **Required**                             |\n\nEach entry in `with.policies` can specify:\n\n- `name` / `description`: free-form identifiers for observability.\n- `storyName`: override the target story for matching events (defaults to `storyRef` if omitted).\n- `events`, `rooms`, `participants`: matchers that accept exact strings, `*`, or `prefix*`.\n- `storyInputs`: static JSON passed through to the Story as `policy.inputs` and `policyInputs`.\n- `metadata`: lightweight labels/annotations surfaced inside the Story for downstream routing.\n\n## 🔐 Secrets\n\nMap `secrets.livekitWebhook` to a Kubernetes secret containing:\n\n```yaml\nstringData:\n  API_KEY: \"LK...\"\n  API_SECRET: \"...\"\n```\n\n## 🌐 Environment Variables\n\nThis impulse has **no** required environment variables. All configuration is handled via the `Impulse` resource's `with` block and `secrets` mapping for maximum clarity and security.\n\n---\n\n## 📥 Story Inputs\n\nWhen this impulse resolves a Story trigger, it provides a structured JSON object as the resulting `StoryRun`'s inputs.\n\n```json\n{\n  \"event\": {\n    \"type\": \"participant_joined\",\n    \"id\": \"EV_Abc123\",\n    \"createdAt\": 1678886400,\n    \"raw\": {\n      \"participant\": {\n        \"identity\": \"user-alice\"\n      }\n    }\n  },\n  \"room\": {\n    \"name\": \"my-awesome-room\",\n    \"sid\": \"RM_Xyz789\"\n  },\n  \"participant\": {\n    \"identity\": \"user-alice\",\n    \"sid\": \"PA_Def456\"\n  },\n  \"policy\": {\n    \"name\": \"voice-on-join\",\n    \"inputs\": {\n      \"entryEngram\": \"livekit-agent\",\n      \"transport\": \"voice\"\n    },\n    \"metadata\": {\n      \"tier\": \"vip\"\n    }\n  },\n  \"policyInputs\": {\n    \"entryEngram\": \"livekit-agent\",\n    \"transport\": \"voice\"\n  }\n}\n```\n\n---\n\n## 🔄 Runtime Notes\n\nCreate an `Impulse` resource using the template. Point it to your secret and declare the policies that should spawn Stories (for example, kick off a realtime voice or translation Story when someone joins a room).\n\n```yaml\napiVersion: bubustack.io/v1alpha1\nkind: Impulse\nmetadata:\n  name: my-livekit-trigger-for-my-app\nspec:\n  templateRef:\n    name: livekit-webhook\n  # Target story is specified via storyRef (required by Impulse CRD)\n  storyRef:\n    name: livekit-router\n  with:\n    eventsAllowlist:\n      - \"participant_joined\"\n      - \"room_started\"\n    policies:\n      - name: voice-on-join\n        description: \"Start the real-time voice assistant when VIP users join\"\n        events:\n          - participant_joined\n        rooms:\n          - \"support-*\"\n        storyName: voice-assistant-story\n        storyInputs:\n          entryEngram: livekit-agent\n          transport: voice\n  secrets:\n    livekitWebhook: my-livekit-keys\n```\n\nExpose the impulse's HTTP service on port `8080` and configure the matching webhook URL in your LiveKit project.\n\n## 🧪 Local Development\n\n- `make lint` – Run the shared lint and static-analysis checks.\n- `go test ./...` – Run the impulse test suite locally.\n- `make docker-build` – Build the impulse image for local clusters.\n\n## 🤝 Community \u0026 Support\n\n- [Contributing](./CONTRIBUTING.md)\n- [Support](./SUPPORT.md)\n- [Security Policy](./SECURITY.md)\n- [Code of Conduct](./CODE_OF_CONDUCT.md)\n- [Discord](https://discord.gg/dysrB7D8H6)\n\n\n## 📄 License\n\nCopyright 2025 BubuStack.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","funding_links":["https://github.com/sponsors/bubustack"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbubustack%2Flivekit-webhook-impulse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbubustack%2Flivekit-webhook-impulse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbubustack%2Flivekit-webhook-impulse/lists"}