{"id":44255580,"url":"https://github.com/benwilber/tinysse","last_synced_at":"2026-02-10T16:25:57.454Z","repository":{"id":279132193,"uuid":"897546386","full_name":"benwilber/tinysse","owner":"benwilber","description":"A programmable server for Server-Sent Events (SSE).","archived":false,"fork":false,"pushed_at":"2025-04-26T20:19:38.000Z","size":152,"stargazers_count":71,"open_issues_count":5,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-10-18T20:13:26.548Z","etag":null,"topics":["axum","lua","rust","server","server-sent-events","sse","tokio"],"latest_commit_sha":null,"homepage":"https://tinysse.com","language":"Rust","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/benwilber.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"AUTHORS","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-02T20:19:25.000Z","updated_at":"2025-10-14T17:21:02.000Z","dependencies_parsed_at":"2025-03-28T14:23:28.924Z","dependency_job_id":"3e33ca64-8c50-40cd-844a-5cbe3aff2419","html_url":"https://github.com/benwilber/tinysse","commit_stats":null,"previous_names":["benwilber/tinysse"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/benwilber/tinysse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benwilber%2Ftinysse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benwilber%2Ftinysse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benwilber%2Ftinysse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benwilber%2Ftinysse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benwilber","download_url":"https://codeload.github.com/benwilber/tinysse/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benwilber%2Ftinysse/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29307900,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-10T16:09:25.305Z","status":"ssl_error","status_checked_at":"2026-02-10T16:08:52.170Z","response_time":65,"last_error":"SSL_read: 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":["axum","lua","rust","server","server-sent-events","sse","tokio"],"created_at":"2026-02-10T16:25:55.958Z","updated_at":"2026-02-10T16:25:57.448Z","avatar_url":"https://github.com/benwilber.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tiny SSE\n\nA programmable server for Server-Sent Events (SSE).\n\n## Features\n\n\n- **Flexible Message Handling** – Filter, modify, redirect, and replay messages dynamically.  \n- **Reliable Connections** – Track subscribers, support reconnections, and maintain session state.  \n- **Secure Access Control** – Enforce authentication, authorization, and event-based restrictions.  \n- **Customizable Behavior** – Use hooks to modify messages and manage subscriptions programmatically.  \n\n\n## Table of Contents\n\n- [Features](#features)\n- [Installation](#installation)\n  - [Homebrew](#homebrew-macos)\n  - [Building](#building)\n- [Examples](#examples)\n  - [Basic Pub/Sub server](#basic-pubsub-server)\n  - [A Whirlwind Tour](#a-whirlwind-tour)\n- [HTTP API](#http-api)\n  - [Publishing messages](#publishing-messages)\n    - [SSE message fields](#sse-message-fields)\n  - [Subscribing to messages](#subscribing-to-messages)\n- [Lua API](#lua-api)\n  - [`startup(cli)`](#startupcli)\n  - [`tick(count)`](#tickcount)\n  - [`publish(pub)`](#publishpub)\n  - [`subscribe(sub)`](#subscribesub)\n  - [`catchup(sub, last_event_id)`](#catchupsub-last_event_id)\n  - [`message(pub, sub)`](#messagepub-sub)\n  - [`unsubscribe(sub)`](#unsubscribesub)\n  - [`timeout(sub, elapsed)`](#timeoutsub-elapsed)\n- [Lua API Built-ins](BUILTINS.md)\n- [Usage](#usage)\n- [Contributing to Tiny SSE](#contributing-to-tiny-sse)\n  - [Getting Started](#getting-started)\n  - [Reporting Issues](#reporting-issues)\n  - [Feature Requests](#feature-requests)\n  - [Code Contributions](#code-contributions)\n\n## Installation\n\n[Archives of binary releases](https://github.com/benwilber/tinysse/releases) are available for Linux, macOS, and Windows.\n\n### Homebrew (macOS)\n\n```sh\nbrew tap benwilber/tinysse\nbrew install benwilber/tinysse/tinysse-bin\ntinysse --help\n```\n\n### Building\n\nThe project can be built with the standard Rust/Cargo toolchain:\n\n```sh\ngit clone https://github.com/benwilber/tinysse.git\ncd tinysse\ncargo build --release\n./target/release/tinysse --help\n```\n\n## Examples\n\n### Basic Pub/Sub server\nStart the server\n\n```sh\n$ tinysse\n```\n\n```\nINFO tinysse: Listening on 127.0.0.1:1983\n```\n\n1) Start a subscriber\n\n```sh\ncurl http://127.0.0.1:1983/sse\n```\n```\n: ok\n...\n```\n\n2) Publish a message\n\n```sh\ncurl -X POST -d data=\"Hello, World\" http://127.0.0.1:1983/sse\n```\n```\n{\"queued\":1,\"subscribers\":1}\n```\n\n3) Observe the message received by the subscriber\n\n```\n: ok\n\ndata: Hello, World\n...\n```\n\n## A Whirlwind Tour\n\nMake a Lua script `script.lua`\n\nRun the server with the script\n\n```sh\n$ tinysse --script script.lua\n```\n\n```lua\n-- The `uuid` package is built-in to the Tiny SSE server\nlocal uuid = require \"uuid\"\n\n-- A message is published\nfunction publish(pub)\n  -- Set a unique ID on the publish request.\n  -- This can later be referenced in the `message(pub, sub)`\n  -- function to correlate the publish request with message\n  -- delivery to subscribers\n  pub.id = uuid()\n\n  -- We can override the data in the SSE message\n  pub.msg.data = \"Hello, Universe!\"\n\n  -- If the publisher did not set a message ID, then we can set one here.\n  -- This will be the `id: \u003cid\u003e` line in the SSE message.\n  if not pub.msg.id then\n    pub.msg.id = uuid()\n  end\n\n  -- We can set a custom event\n  pub.msg.event = \"custom-event\"\n\n  -- Comments too\n  pub.msg.comment = {\"This is a comment\", \"Another comment!\"}\n\n  -- Return the pub request to the server or it\n  -- will be rejected and not delivered to any subscribers\n  return pub\nend\n\n-- A new subscriber connects\nfunction subscribe(sub)\n  -- Set a unique ID on the subscriber.\n  sub.id = uuid()\n\n  -- Return the sub request to the server or it\n  -- will be rejected and the client will be disconnected immediately\n  return sub\nend\n\n-- A message is delivered to a subscriber\nfunction message(pub, sub)\n  print(\"Publish ID:\", pub.id)\n  print(\"Message ID:\", pub.msg.id)\n  print(\"Subscriber ID:\", sub.id)\n\n  -- Return the pub request to the server or\n  -- the subscriber will not receive the message\n  -- (but will still remain connected for subsequent messages)\n  return pub\nend\n\n-- A subscriber disconnects\nfunction unsubscribe(sub)\n  print(\"Unsubscribed:\", sub.id)\nend\n```\n\n## HTTP API\n### Publishing messages\nThe server supports publishing SSE messages via HTTP `POST` to the URL path configured by the `--pub-path=\u003cpath\u003e` option (defaults to `/sse`).\n\nIt accepts data encoded as both `application/x-www-form-urlencoded` and `application/json`.  The specific content type must always be indicated in the request or it will be rejected.\n\n```curl\ncurl -i -X POST \\\n  --header \"content-type: application/json\" \\\n  --data-raw '{\"data\": \"Hello World\"}' \\\n  http://127.0.0.1:1983/sse\n```\n\nA successful publish will respond with a `202 Accepted` status code and an `application/json` body with the current number of subscribers and the number of messages in the queue that have not been delivered to all subscribers (yet).\n\n```\nHTTP/1.1 202 Accepted\ncontent-type: application/json\ncontent-length: 31\n\n{\"queued\": 1, \"subscribers\": 1}\n```\n\nThe size of the internal message queue can be configured with the `--capacity=\u003csize\u003e` option.  It defaults to 256.\n\n#### SSE message fields\n\nAll fields are optional but at least one must be provided or the message will be rejected with a `400 Bad Request` error.\n\n\n```json\n{\n  \"id\": \"some-id\",\n  \"event\": \"custom-event\",\n  \"data\": \"Some data\",\n  \"comment\": [\"First comment\", \"Second comment\"]\n}\n```\n\nEquivalent message as `application/x-www-form-urlencoded`:\n\n```form\nid=some-message-id\n\u0026event=custom-event\n\u0026data=Some%20data\n\u0026comment=First%20comment\n\u0026comment=Second%20comment\n```\n\n`data` containing newlines is automatically split across multiple `data:` lines in the SSE message.\n\n### Subscribing to messages\n\nThe server supports subscribing to SSE messages via HTTP `GET` to the URL path configured by the `--sub-path=\u003cpath\u003e` option (defaults to `/sse`).\n\n```curl\ncurl -i http://127.0.0.1:1983/sse\n\nHTTP/1.1 200 OK\ncontent-type: text/event-stream\ncache-control: no-cache\n\n: ok\n\nid: some-id\nevent: custom-event\ndata: Some data\n: First comment\n: Second comment\n\n: keep-alive\n```\n\nUpon successful subscription, the server will immediately respond with the SSE comment `ok` indicating that the connection is established and waiting for new messages.\n\nKeep-alive messages (SSE comments) are sent periodically to ensure the connection stays open and is not closed by intermediate proxies due to socket inactivity.  These messages are configurable with the `--keep-alive` and `--keep-alive-text` options.\n\n## Lua API\n\nThe server can function as just a simple SSE pub/sub server without using the Lua API.  However, much of the advanced functionality (authorization, message routing, etc.) requires writing Lua code to implement custom behaviors.  The server is asynchronous and invokes global Lua functions defined in the script given by the `--script=\u003cpath\u003e` option when various events occur.  The server will provide arguments to the functions with context of the event.\n\nThe program runs in a single Lua context for the lifetime of the server so that a global state is shared across the various function calls.\n\n### `startup(cli)`\n\nThis is the first function called by the server immediately after it\nbegins listening on the configured address and port (default: `127.0.0.1:1983`) and\nbefore the socket accepts any client connections.  It will be called only\nonce during the server lifetime, and will provide the CLI options to the program\nas a Lua table `cli`.\nThe server will not accept a return value from this function.  However, it\nwill abort if the function raises a Lua error.\n\n```lua\nfunction startup(cli)\n  -- The `cli` table looks like:\n  {\n    keep_alive_text = \"keep-alive\",\n    script = \"script.lua\",\n    script_tick = 500,\n    log_level = \"INFO\",\n    pub_path = \"/sse\",\n    sub_path = \"/sse\",\n    keep_alive = 60000,\n    timeout_retry = 0,\n    timeout = 300000,\n    serve_static_path = \"/\",\n    capacity = 256,\n    listen = \"127.0.0.1:1983\",\n    unsafe_script = false\n  }\nend\n```\n\n### `tick(count)`\n\nA periodic event that allows the Lua script to \"wake up\" and perform background tasks at regular intervals (default `500ms`).  It provides a single argument `count` which is the number of times the tick function has been invoked (including the current) since the server started.\n\n```lua\nfunction tick(count)\n  -- Do background work here\nend\n```\n\n### `publish(pub)`\n\nCalled when a client wants to publish a message.  It provides a single argument `pub` which is a Lua table containing context of the publish request.  The function is free to modify the request however it needs, but it must return it (modified or not) to the server or the publish request will be rejected with a `403 Forbidden` error and the message will not be delivered to any subscribers.\n\n**NOTE**: Changes to the inner `req` table will not be preserved.\n\n```lua\nfunction publish(pub)\n  -- The `pub` table looks like\n  {\n    req = {\n      headers = {\n        [\"content-type\"] = \"application/json\",\n        [\"content-length\"] = \"24\",\n        accept = \"*/*\",\n        host = \"127.0.0.1:1983\",\n        [\"user-agent\"] = \"curl/8.7.1\"\n      },\n      query = \"\",\n      path = \"/sse\",\n      addr = {\n        ip = \"127.0.0.1\",\n        port = 59615\n      },\n      method = \"POST\"\n    },\n    msg = {\n      data = \"Hello, World\"\n    }\n  }\n  \n  -- The function is free to modify this table however it needs, but it\n  -- must return it to the server or the message will be rejected.\n  return pub\nend\n```\n\n### `subscribe(sub)`\n\nCalled when a new subscriber connects.  It provides a single argument `sub` which is a Lua table containing context of the subscribe request.  The function is free to modify the request however it needs, but it must return it (modified or not) to the server or the connection will be rejected with a `403 Forbidden` error and the client will be disconnected immediately.\n\n**NOTE**: Changes to the inner `req` table will not be preserved.\n\n```lua\nfunction subscribe(sub)\n  -- The `sub` table looks like:\n  {\n    req = {\n      query = \"\",\n      headers = {\n        [\"user-agent\"] = \"curl/8.7.1\",\n        accept = \"*/*\",\n        host = \"127.0.0.1:1983\"\n      },\n      path = \"/sse\",\n      addr = {\n        ip = \"127.0.0.1\",\n        port = 59632\n      },\n      method = \"GET\"\n    }\n  }\n  \n  -- The function is free to modify this table however it needs, but it\n  -- must return it to the server or the subscribe request will be rejected.\n  return sub\nend\n```\n\n### `catchup(sub, last_event_id)`\n\nCalled immediately after a client subscribes. If the client provides a Last-Event-ID, either through the `Last-Event-ID:` request header or as a query parameter (`?last_event_id=`), this function should attempt to retrieve missed messages. If both are provided, the header takes precedence. The function may return `nil` or an array of SSE messages to \"catch up\" the subscriber with any messages they may have missed due to reconnection or to provide recent message history. If `last_event_id` is `nil` or no messages are available, the function may return `nil` instead of an empty array.\n\n**NOTE:** The `message(pub, sub)` function **will not** be called for messages delivered from the `catchup(sub, last_event_id)` function.\n\n```lua\nfunction catchup(sub, last_event_id)\n  -- last_event_id might be nil if the client did not provide it\n  local msgs = {}\n\n  -- For instance, \"catch-up\" subscriber with the 10 most recent messages\n  for i=1,10 do\n    table.insert(msgs, {\n      id = \"some-id-\" .. i,\n      event = \"some-event\",\n      data = \"some data\"\n    })\n  end\n\n  return msgs\nend\n```\n\n### `message(pub, sub)`\n\nCalled before delivering a message to a subscriber. Receives `pub` and `sub`, the tables returned from the `publish` and `subscribe` functions. Modifications to these tables affect only this subscriber, not others receiving the same message. Typically used for routing and subscriber-specific adjustments.\n\n**NOTE**: Changes to the inner `req` tables will not be preserved.\n\n```lua\nfunction message(pub, sub)\n  -- Subscriber-specific logic such as routing, message modifications, etc.\n  \n  -- Returning `nil` (or just nothing at all) will prevent this subscriber from receiving\n  -- the message.  Returning the `pub` table to the server will continue with\n  -- delivery of the (possibly modified) SSE message to the subscriber.\n  return pub\nend\n```\n\n### `unsubscribe(sub)`\n\nCalled when a subscriber disconnects.  The server provides a single argument `sub` which is the Lua table returned from the `subscribe` function.  It does not accept any return value.\n\n```lua\nfunction unsubscribe(sub)\n  -- Client has unsubscribed (disconnected) from the SSE server.\nend\n```\n\n### `timeout(sub, elapsed)`\n\nCalled when a subscriber disconnects as result of an SSE timeout.  The server provides two arguments, `sub` and `elapsed`.  `sub` is the table returned from the `subscribe` function, and `elapsed` is the total milliseconds that the subscriber was connected.  The server accepts an optional return value which is the number of milliseconds that the client should wait before reconnecting.  If not given, it will default to the value given by the `--timeout-retry` option.\n\n**NOTE:** The `unsubscribe(sub)` function will be called immediately after this.\n\n```lua\nfunction timeout(sub, elapsed)\n  -- Subscriber timed-out and was disconnected.\nend\n```\n\nFor advanced usage, see the [Lua API Built-ins](BUILTINS.md#built-in-lua-packages) and the [Lua examples](examples/lua)\n\n\n## Usage\n\n```text\n$ tinysse --help\nTiny SSE\n\nA programmable server for Server-Sent Events (SSE).\n\nUsage: tinysse [OPTIONS]\n\nOptions:\n  -l, --listen \u003cADDR:PORT\u003e\n          The address and port for the HTTP server to listen\n          \n          [env: TINYSSE_LISTEN=]\n          [default: 127.0.0.1:1983]\n\n  -L, --log-level \u003cLEVEL\u003e\n          The logging level for the server. Possible values: ERROR, WARN, INFO, DEBUG, TRACE\n          \n          [env: TINYSSE_LOG_LEVEL=]\n          [default: INFO]\n\n  -k, --keep-alive \u003cINTERVAL\u003e\n          The interval between keep-alive messages sent to clients (e.g., 60s, 2m).\n          Keep-alive messages are sent periodically to ensure that clients remain connected\n          \n          [env: TINYSSE_KEEP_ALIVE=]\n          [default: 60s]\n\n  -K, --keep-alive-text \u003cTEXT\u003e\n          The text of the keep-alive comment sent to clients.\n          \n          [env: TINYSSE_KEEP_ALIVE_TEXT=]\n          [default: keep-alive]\n\n  -t, --timeout \u003cTIMEOUT\u003e\n          The timeout duration for subscriber connections (e.g., 5m, 300s, 10m).\n          Connections open for longer than this duration will be closed\n          \n          [env: TINYSSE_TIMEOUT=]\n          [default: 5m]\n\n  -r, --timeout-retry \u003cRETRY\u003e\n          The retry delay sent to clients after a connection timeout (e.g., 0s, 2s).\n          This delay instructs clients how long to wait before attempting to reconnect.\n          Setting this to 0s instructs the client to reconnect immediately\n          \n          [env: TINYSSE_TIMEOUT_RETRY=]\n          [default: 0s]\n\n  -c, --capacity \u003cCAPACITY\u003e\n          The capacity of the server's internal message queue\n          \n          [env: TINYSSE_CAPACITY=]\n          [default: 256]\n\n  -s, --script \u003cFILE_PATH\u003e\n          The path to a Lua script for server customization\n          \n          [env: TINYSSE_SCRIPT=]\n\n      --script-data \u003cDATA\u003e\n          Optional data to pass to the Lua script as the `opts.script_data` value in the `startup(opts)` function\n          \n          [env: TINYSSE_SCRIPT_DATA=]\n\n      --script-tick \u003cINTERVAL\u003e\n          The interval between Lua script ticks (e.g., 1s, 500ms). The script tick is a periodic event that allows the Lua script to perform\n          background tasks in the `tick(count)` function\n          \n          [env: TINYSSE_SCRIPT_TICK=]\n          [default: 500ms]\n\n      --unsafe-script\n          Allow the Lua script to load (require) native code, such as shared (.so) libraries. Enabling this can pose security risks, as\n          native code can execute arbitrary operations. Use this option only if you trust the Lua script and need it to load native modules\n          \n          [env: TINYSSE_UNSAFE_SCRIPT=]\n\n  -m, --max-body-size \u003cBYTES\u003e\n          The maximum size of the publish request body that the server will accept (e.g., 32KB, 1MB)\n          \n          [env: TINYSSE_MAX_BODY_SIZE=]\n          [default: 64KB]\n\n  -P, --pub-path \u003cURL_PATH\u003e\n          The URL path for publishing messages via POST\n          \n          [env: TINYSSE_PUB_PATH=]\n          [default: /sse]\n\n  -S, --sub-path \u003cURL_PATH\u003e\n          The URL path for subscribing to messages via GET\n          \n          [env: TINYSSE_SUB_PATH=]\n          [default: /sse]\n\n  -D, --serve-static-dir \u003cDIR_PATH\u003e\n          Serve static files from the specified directory under the path specified by `--serve-static-path`\n          \n          [env: TINYSSE_SERVE_STATIC_DIR=]\n\n  -U, --serve-static-path \u003cURL_PATH\u003e\n          The URL path under which to serve static files from the directory specified by `--serve-static-dir`\n          \n          [env: TINYSSE_SERVE_STATIC_PATH=]\n          [default: /]\n\n      --cors-allow-origin \u003cORIGINS\u003e\n          Set Access-Control-Allow-Origin header to the specified origin(s)\n          \n          [env: TINYSSE_CORS_ALLOW_ORIGIN=]\n          [default: *]\n\n      --cors-allow-methods \u003cMETHODS\u003e\n          Set Access-Control-Allow-Methods header to the specified method(s)\n          \n          [env: TINYSSE_CORS_ALLOW_METHODS=]\n          [default: \"GET, HEAD, POST\"]\n\n      --cors-allow-headers \u003cHEADERS\u003e\n          Set Access-Control-Allow-Headers header to the specified header(s). (e.g., Cookie,Authorization)\n          \n          [env: TINYSSE_CORS_ALLOW_HEADERS=]\n          [default: *]\n\n      --cors-allow-credentials\n          Set Access-Control-Allow-Credentials header to true. Cannot be set if Access-Control-Allow-Origin or Access-Control-Allow-Headers\n          is set to '*' (any)\n          \n          [env: TINYSSE_CORS_ALLOW_CREDENTIALS=]\n\n      --cors-max-age \u003cDURATION\u003e\n          Set Access-Control-Max-Age header to the specified duration (e.g., 1h, 60s). Set to 0s to disable browsers from caching preflight OPTIONS requests\n          \n          [env: TINYSSE_CORS_MAX_AGE=]\n          [default: 0s]\n\n  -h, --help\n          Print help (see a summary with '-h')\n```\n\n## Contributing to Tiny SSE\n\nThank you for your interest in contributing to Tiny SSE! We welcome all contributions, including bug reports, feature requests, documentation improvements, and code contributions.\n\n### Getting Started\n\n1. Fork the repository and create a new branch for your changes.\n2. Make your modifications and ensure they follow Rust (and Lua) best practices.\n3. Run tests to verify your changes with `cargo test`.\n4. Format your code using `cargo fmt` and check for issues with `cargo check`.\n5. Submit a pull request with a clear description of your changes.\n\n### Reporting Issues\n\nIf you encounter a bug, please open an issue and include:\n\n- A clear description of the problem.\n- Steps to reproduce the issue.\n- Expected vs. actual behavior.\n- Any relevant logs or error messages.\n\n### Feature Requests\n\nWe welcome feature suggestions! Before submitting a request, check if an issue already exists. Provide a detailed explanation of how the feature benefits the project.\n\n### Code Contributions\n\n- Follow Rust (and Lua) best practices and maintain code clarity.\n- Use descriptive commit messages summarizing your changes.\n- Write tests for new features or bug fixes.\n- Keep discussions respectful and relevant.\n\nBy contributing to Tiny SSE, you agree that your contributions will be licensed under the **[Apache-2.0 license](LICENSE)**.\n\nThank you for helping improve Tiny SSE!","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenwilber%2Ftinysse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenwilber%2Ftinysse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenwilber%2Ftinysse/lists"}