{"id":28411709,"url":"https://github.com/agentjido/jido_signal","last_synced_at":"2026-04-02T17:11:28.138Z","repository":{"id":296780588,"uuid":"994446978","full_name":"agentjido/jido_signal","owner":"agentjido","description":"Agent Communication Envelope and Utilities","archived":false,"fork":false,"pushed_at":"2026-03-30T23:08:55.000Z","size":8301,"stargazers_count":27,"open_issues_count":0,"forks_count":9,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-03-31T01:28:55.470Z","etag":null,"topics":["cloudevents","events","message-bus","messaging","routing"],"latest_commit_sha":null,"homepage":"https://agentjido.xyz","language":"Elixir","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/agentjido.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","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":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-06-02T00:09:24.000Z","updated_at":"2026-03-30T23:08:28.000Z","dependencies_parsed_at":"2025-12-09T16:00:40.458Z","dependency_job_id":null,"html_url":"https://github.com/agentjido/jido_signal","commit_stats":null,"previous_names":["agentjido/jido_signal"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/agentjido/jido_signal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agentjido%2Fjido_signal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agentjido%2Fjido_signal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agentjido%2Fjido_signal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agentjido%2Fjido_signal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/agentjido","download_url":"https://codeload.github.com/agentjido/jido_signal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agentjido%2Fjido_signal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31311266,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"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":["cloudevents","events","message-bus","messaging","routing"],"created_at":"2025-06-02T18:44:25.163Z","updated_at":"2026-04-02T17:11:28.129Z","avatar_url":"https://github.com/agentjido.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Jido.Signal\n\n[![Hex.pm](https://img.shields.io/hexpm/v/jido_signal.svg)](https://hex.pm/packages/jido_signal)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/jido_signal/)\n[![CI](https://github.com/agentjido/jido_signal/actions/workflows/ci.yml/badge.svg)](https://github.com/agentjido/jido_signal/actions/workflows/ci.yml)\n[![License](https://img.shields.io/hexpm/l/jido_signal.svg)](https://github.com/agentjido/jido_signal/blob/main/LICENSE)\n[![Coverage Status](https://coveralls.io/repos/github/agentjido/jido_signal/badge.svg?branch=main)](https://coveralls.io/github/agentjido/jido_signal?branch=main)\n\n\u003e **Agent Communication Envelope and Utilities**\n\n_`Jido.Signal` is part of the [Jido](https://github.com/agentjido/jido) project. Learn more about Jido at [jido.run](https://jido.run)._\n\n## Overview\n\n`Jido.Signal` is a sophisticated toolkit for building event-driven and agent-based systems in Elixir. It provides a complete ecosystem for defining, routing, dispatching, and tracking signals throughout your application, built on the CloudEvents v1.0.2 specification with powerful Jido-specific extensions.\n\nWhether you're building microservices that need reliable event communication, implementing complex agent-based systems, or creating observable distributed applications, Jido.Signal provides the foundation for robust, traceable, and scalable event-driven architecture.\n\n## Why Do I Need Signals?\n\n**Agent Communication in Elixir's Process-Driven World**\n\nElixir's strength lies in lightweight processes that communicate via message passing, but raw message passing has limitations when building complex systems:\n\n- **Phoenix Channels** need structured event broadcasting across connections\n- **GenServers** require reliable inter-process communication with context\n- **Agent Systems** demand traceable conversations between autonomous processes\n- **Distributed Services** need standardized message formats across nodes\n\nTraditional Elixir messaging (`send`, `GenServer.cast/call`) works great for simple scenarios, but falls short when you need:\n\n- **Standardized Message Format**: Raw tuples and maps lack structure and metadata\n- **Event Routing**: Broadcasting to multiple interested processes based on patterns\n- **Conversation Tracking**: Understanding which message caused which response\n- **Reliable Delivery**: Ensuring critical messages aren't lost if a process crashes\n- **Cross-System Integration**: Communicating with external services via webhooks/HTTP\n\n```elixir\n# Traditional Elixir messaging\nGenServer.cast(my_server, {:user_created, user_id, email})  # Unstructured\nsend(pid, {:event, data})  # No routing or reliability\n\n# With Jido.Signal\n{:ok, signal} = UserCreated.new(%{user_id: user_id, email: email})\nBus.publish(:app_bus, [signal])  # Structured, routed, traceable, reliable\n```\n\nJido.Signal transforms Elixir's message passing into a sophisticated communication system that scales from simple GenServer interactions to complex multi-agent orchestration across distributed systems.\n\n## Key Features\n\n### **Standardized Signal Structure**\n- CloudEvents v1.0.2 compliant message format\n- Custom signal types with data validation\n- Rich metadata and context tracking\n- Flexible serialization (JSON, MessagePack, Erlang Term Format)\n\n### **High-Performance Signal Bus**\n- In-memory GenServer-based pub/sub system\n- Persistent subscriptions with checkpointing, retry, and Dead Letter Queue (DLQ)\n- Middleware pipeline for cross-cutting concerns with timeout protection\n- Complete signal history with replay capabilities\n- Partitioned dispatch with rate limiting for horizontal scaling\n- Instance isolation for multi-tenant deployments\n\n### **Advanced Routing Engine**\n- Trie-based pattern matching for optimal performance\n- Wildcard support (`*` single-level, `**` multi-level)\n- Priority-based execution ordering\n- Custom pattern matching functions\n\n### **Pluggable Dispatch System**\n- Multiple delivery adapters (PID, PubSub, HTTP, Logger, Console)\n- Synchronous and asynchronous delivery modes\n- Batch processing for high-throughput scenarios\n- Configurable timeout and retry mechanisms\n- Circuit breaker wrapper for fault isolation (using `:fuse`)\n\n### **Causality \u0026 Conversation Tracking**\n- Complete signal relationship graphs\n- Cause-effect chain analysis\n- Conversation grouping and temporal ordering\n- Comprehensive system traceability for debugging and auditing\n\n## Installation\n\n### Igniter Installation\nIf your project has [Igniter](https://hexdocs.pm/igniter/readme.html) available,\nyou can install Jido Signal using the command \n\n```bash\nmix igniter.install jido_signal\n```\n\n### Manual Installation\nAdd `jido_signal` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:jido_signal, \"~\u003e 2.0\"}\n  ]\nend\n```\n\nThen run:\n\n```bash\nmix deps.get\n```\n\n## Quick Start\n\n### 1. Start a Signal Bus\n\nAdd to your application's supervision tree:\n\n```elixir\n# In your application.ex\nchildren = [\n  {Jido.Signal.Bus, name: :my_app_bus}\n]\n\nSupervisor.start_link(children, strategy: :one_for_one)\n```\n\n### 2. Create a Subscriber\n\n```elixir\ndefmodule MySubscriber do\n  use GenServer\n\n  def start_link(_opts), do: GenServer.start_link(__MODULE__, %{})\n  def init(state), do: {:ok, state}\n\n  # Handle incoming signals\n  def handle_info({:signal, signal}, state) do\n    IO.puts(\"Received: #{signal.type}\")\n    {:noreply, state}\n  end\nend\n```\n\n### 3. Subscribe and Publish\n\n```elixir\nalias Jido.Signal.Bus\nalias Jido.Signal\n\n# Start subscriber and subscribe to user events\n{:ok, sub_pid} = MySubscriber.start_link([])\n{:ok, _sub_id} = Bus.subscribe(:my_app_bus, \"user.*\", dispatch: {:pid, target: sub_pid})\n\n# Create and publish a signal\n# Preferred: positional constructor (type, data, attrs)\n{:ok, signal} = Signal.new(\"user.created\", %{user_id: \"123\", email: \"user@example.com\"},\n  source: \"/auth/registration\"\n)\n\n# Also available: Map/keyword constructor (backwards compatible)\n{:ok, signal} = Signal.new(%{\n  type: \"user.created\",\n  source: \"/auth/registration\",\n  data: %{user_id: \"123\", email: \"user@example.com\"}\n})\n\nBus.publish(:my_app_bus, [signal])\n# Output: \"Received: user.created\"\n```\n\n## Core Concepts\n\n### The Signal\n\nSignals are CloudEvents-compliant message envelopes that carry your application's events:\n\n```elixir\n# Basic signal with positional constructor (preferred)\n{:ok, signal} = Signal.new(\"order.created\", %{order_id: \"ord_123\", amount: 99.99},\n  source: \"/ecommerce/orders\"\n)\n\n# Map constructor (also available)\n{:ok, signal} = Signal.new(%{\n  type: \"order.created\",\n  source: \"/ecommerce/orders\",\n  data: %{order_id: \"ord_123\", amount: 99.99}\n})\n\n# Dispatch is configured when subscribing or dispatching, not on the signal\n:ok = Dispatch.dispatch(signal, [\n  {:pubsub, target: MyApp.PubSub, topic: \"payments\"},\n  {:webhook, url: \"https://api.partner.com/webhook\", secret: \"secret123\"}\n])\n```\n\n### Custom Signal Types\n\nDefine strongly-typed signals with validation:\n\n```elixir\ndefmodule UserCreated do\n  use Jido.Signal,\n    type: \"user.created.v1\",\n    default_source: \"/users\",\n    schema: [\n      user_id: [type: :string, required: true],\n      email: [type: :string, required: true],\n      name: [type: :string, required: true]\n    ]\nend\n\n# Usage\n{:ok, signal} = UserCreated.new(%{\n  user_id: \"u_123\",\n  email: \"john@example.com\",\n  name: \"John Doe\"\n})\n\n# Validation errors\n{:error, reason} = UserCreated.new(%{user_id: \"u_123\"})\n# =\u003e {:error, \"Invalid data for Signal: Required key :email not found\"}\n```\n\nTyped signals can also declare extension policy when you want constructor-time guarantees\nfor known extensions without changing generic deserialization behavior:\n\n```elixir\ndefmodule UserCreated do\n  use Jido.Signal,\n    type: \"user.created.v1\",\n    schema: [\n      user_id: [type: :string, required: true]\n    ],\n    extension_policy: [\n      {MyApp.Signal.Ext.Trace, :required},\n      {MyApp.Signal.Ext.Dispatch, :forbidden}\n    ]\nend\n\n{:ok, signal} =\n  UserCreated.new(%{user_id: \"u_123\"},\n    trace: %{trace_id: \"trace-123\", span_id: \"span-456\"}\n  )\n```\n\n### The Router\n\nPowerful pattern matching for signal routing:\n\n```elixir\nalias Jido.Signal.Router\n\nroutes = [\n  # Exact matches have highest priority\n  {\"user.created\", :handle_user_creation},\n  \n  # Single-level wildcards\n  {\"user.*.updated\", :handle_user_updates},\n  \n  # Multi-level wildcards\n  {\"audit.**\", :audit_logger, 100},  # High priority\n  \n  # Pattern matching functions\n  {\"**\", fn signal -\u003e String.contains?(signal.type, \"error\") end, :error_handler}\n]\n\n{:ok, router} = Router.new(routes)\n\n# Route signals to handlers\n{:ok, targets} = Router.route(router, Jido.Signal.new!(\"user.profile.updated\", %{}))\n# =\u003e {:ok, [:handle_user_updates]}\n```\n\n### Dispatch System\n\nFlexible delivery to multiple destinations:\n\n```elixir\nalias Jido.Signal.Dispatch\n\ndispatch_configs = [\n  # Send to process\n  {:pid, target: my_process_pid},\n  \n  # Publish via Phoenix.PubSub\n  {:pubsub, target: MyApp.PubSub, topic: \"events\"},\n  \n  # HTTP webhook with signature\n  {:webhook, url: \"https://api.example.com/webhook\", secret: \"secret123\"},\n  \n  # Log structured data\n  {:logger, level: :info, structured: true},\n  \n  # Console output\n  {:console, format: :pretty}\n]\n\n# Synchronous dispatch\n:ok = Dispatch.dispatch(signal, dispatch_configs)\n\n# Asynchronous dispatch\n{:ok, task} = Dispatch.dispatch_async(signal, dispatch_configs)\n```\n\n## Advanced Features\n\n### Persistent Subscriptions\n\nTrack signal acknowledgments for reliable processing:\n\n```elixir\n# Create persistent subscription with full options\n{:ok, sub_id} = Bus.subscribe(:my_app_bus, \"payment.*\",\n  persistent?: true, # `persistent: true` is also supported (backward compatible)\n  dispatch: {:pid, target: self()},\n  max_in_flight: 100,      # Max unacknowledged signals\n  max_pending: 5_000,      # Max queued signals before backpressure\n  max_attempts: 5,         # Retry attempts before DLQ\n  retry_interval: 500      # Milliseconds between retries\n)\n\n# Receive and acknowledge signals\nreceive do\n  {:signal, signal} -\u003e\n    # Process the signal\n    process_payment(signal)\n\n    # Acknowledge successful processing\n    Bus.ack(:my_app_bus, sub_id, signal.id)\nend\n\n# After max_attempts failures, signals move to Dead Letter Queue\n# See Event Bus guide for DLQ management\n```\n\n### Middleware Pipeline\n\nAdd cross-cutting concerns with middleware:\n\n```elixir\nmiddleware = [\n  # Built-in logging middleware\n  {Jido.Signal.Bus.Middleware.Logger, [\n    level: :info,\n    include_signal_data: true\n  ]},\n  \n  # Custom middleware\n  {MyApp.AuthMiddleware, []},\n  {MyApp.MetricsMiddleware, []}\n]\n\n{:ok, _pid} = Jido.Signal.Bus.start_link(\n  name: :my_bus, \n  middleware: middleware\n)\n```\n\nMiddleware callbacks (`before_publish`, `after_publish`, `before_dispatch`, `after_dispatch`) are executed with timeout protection (default 100ms, configurable via `middleware_timeout_ms`). Slow middleware is terminated and the operation continues. See `Jido.Signal.Bus.Middleware.Logger` for a complete implementation example.\n\n### Causality Tracking\n\nTrack signal relationships for complete system observability:\n\n```elixir\nalias Jido.Signal.Journal\n\n# Create journal\njournal = Journal.new()\n\n# Record causal relationships\nJournal.record(journal, initial_signal, nil)  # Root cause\nJournal.record(journal, response_signal, initial_signal.id)  # Caused by initial_signal\nJournal.record(journal, side_effect, initial_signal.id)     # Also caused by initial_signal\n\n# Analyze relationships\neffects = Journal.get_effects(journal, initial_signal.id)\n# =\u003e [response_signal, side_effect]\n\ncause = Journal.get_cause(journal, response_signal.id)\n# =\u003e initial_signal\n```\n\n### Signal History \u0026 Replay\n\nAccess complete signal history:\n\n```elixir\n# Get recent signals matching pattern\n{:ok, signals} = Bus.replay(:my_app_bus, \"user.*\", \n  since: DateTime.utc_now() |\u003e DateTime.add(-3600, :second),\n  limit: 100\n)\n\n# Replay to new subscriber\n{:ok, new_sub} = Bus.subscribe(:my_app_bus, \"user.*\", \n  dispatch: {:pid, target: new_process_pid},\n  replay_since: DateTime.utc_now() |\u003e DateTime.add(-1800, :second)\n)\n```\n\n### Snapshots\n\nCreate point-in-time views of your signal log:\n\n```elixir\n# Create filtered snapshot\n{:ok, snapshot_id} = Bus.snapshot_create(:my_app_bus, %{\n  path_pattern: \"order.**\",\n  since: ~U[2024-01-01 00:00:00Z],\n  until: ~U[2024-01-31 23:59:59Z]\n})\n\n# Read snapshot data\n{:ok, signals} = Bus.snapshot_read(:my_app_bus, snapshot_id)\n\n# Export or analyze the signals\nEnum.each(signals, \u0026analyze_order_signal/1)\n```\n\n### Instance Isolation\n\nFor multi-tenant applications or testing, create isolated signal infrastructure:\n\n```elixir\n# Start an isolated instance with its own Registry, TaskSupervisor, etc.\n{:ok, _} = Jido.Signal.Instance.start_link(name: MyApp.Jido)\n\n# Start buses scoped to the instance\n{:ok, _} = Jido.Signal.Bus.start_link(name: :tenant_bus, jido: MyApp.Jido)\n\n# Lookup uses the correct instance registry\n{:ok, bus_pid} = Jido.Signal.Bus.whereis(:tenant_bus, jido: MyApp.Jido)\n\n# Multiple instances are completely isolated\n{:ok, _} = Jido.Signal.Instance.start_link(name: TenantA.Jido)\n{:ok, _} = Jido.Signal.Instance.start_link(name: TenantB.Jido)\n\n# Same bus name, different instances = different processes\n{:ok, _} = Jido.Signal.Bus.start_link(name: :events, jido: TenantA.Jido)\n{:ok, _} = Jido.Signal.Bus.start_link(name: :events, jido: TenantB.Jido)\n```\n\n## Use Cases\n\n### Microservices Communication\n```elixir\n# Service A publishes order events\n{:ok, signal} = OrderCreated.new(%{order_id: \"123\", customer_id: \"456\"})\nBus.publish(:event_bus, [signal])\n\n# Service B processes inventory\n# Service C sends notifications  \n# Service D updates analytics\n```\n\n### Agent-Based Systems\n```elixir\n# Agents communicate via signals\n{:ok, signal} = AgentMessage.new(%{\n  from_agent: \"agent_1\",\n  to_agent: \"agent_2\", \n  action: \"negotiate_price\",\n  data: %{product_id: \"prod_123\", offered_price: 99.99}\n})\n```\n\n### Event Sourcing\n```elixir\n# Commands become events\n{:ok, command_signal} = CreateUser.new(user_data)\n{:ok, event_signal} = UserCreated.new(user_data, cause: command_signal.id)\n\n# Store in journal for complete audit trail\nJournal.record(journal, event_signal, command_signal.id)\n```\n\n### Distributed Workflows\n```elixir\n# Coordinate multi-step processes\nworkflow_signals = [\n  %Signal{type: \"workflow.started\", data: %{workflow_id: \"wf_123\"}},\n  %Signal{type: \"step.completed\", data: %{step: 1, workflow_id: \"wf_123\"}},\n  %Signal{type: \"step.completed\", data: %{step: 2, workflow_id: \"wf_123\"}},\n  %Signal{type: \"workflow.completed\", data: %{workflow_id: \"wf_123\"}}\n]\n```\n\n## Documentation\n\n- **[Getting Started Guide](guides/getting-started.md)** - Quick setup and first signal\n- **[Signals \u0026 Dispatch](guides/signals-and-dispatch.md)** - Signal structure and dispatch adapters\n- **[Event Bus](guides/event-bus.md)** - Pub/sub messaging with middleware\n- **[Signal Router](guides/signal-router.md)** - Pattern matching and routing\n- **[Signal Extensions](guides/signal-extensions.md)** - Custom Signal metadata extensions\n- **[Signal Journal](guides/signal-journal.md)** - Causality tracking and persistence\n- **[Serialization](guides/serialization.md)** - JSON, MessagePack, and Erlang Term formats\n- **[Advanced Topics](guides/advanced.md)** - Custom adapters, performance, and testing\n- **[API Reference](https://hexdocs.pm/jido_signal)** - Complete function documentation\n\n## Development\n\n### Prerequisites\n\n- Elixir 1.18+\n- Erlang/OTP 27+\n\n### Setup\n\n```bash\ngit clone https://github.com/agentjido/jido_signal.git\ncd jido_signal\nmix deps.get\n```\n\n### Running Tests\n\n```bash\nmix test\n```\n\n### Quality Checks\n\n```bash\nmix quality  # Runs formatter, dialyzer, and credo\n```\n\n### Generate Documentation\n\n```bash\nmix docs\n```\n\n## Contributing\n\nWe welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details on:\n\n- Setting up your development environment\n- Running tests and quality checks\n- Submitting pull requests\n- Code style guidelines\n\n## License\n\nThis project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.\n\n## Related Projects\n\n- **[Jido](https://github.com/agentjido/jido)** - The main Jido agent framework\n- **[Jido Workbench](https://github.com/agentjido/jido_workbench)** - Development tools and utilities\n\n## Links\n\n- [Hex Package](https://hex.pm/packages/jido_signal)\n- [Documentation](https://hexdocs.pm/jido_signal)\n- [GitHub Repository](https://github.com/agentjido/jido_signal)\n- [Jido Website](https://jido.run)\n- [CloudEvents Specification](https://cloudevents.io/)\n\n---\n\n**Built with ❤️ by the Jido team**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagentjido%2Fjido_signal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fagentjido%2Fjido_signal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagentjido%2Fjido_signal/lists"}