{"id":30205381,"url":"https://github.com/hcengineering/hulypulse","last_synced_at":"2025-08-13T13:14:56.424Z","repository":{"id":309009877,"uuid":"1033907591","full_name":"hcengineering/hulypulse","owner":"hcengineering","description":"Hulypulse is a service that enables clients to share information on a “whiteboard”.","archived":false,"fork":false,"pushed_at":"2025-08-08T15:39:56.000Z","size":47,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-09T08:54:53.131Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hcengineering.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}},"created_at":"2025-08-07T14:30:21.000Z","updated_at":"2025-08-08T16:15:19.000Z","dependencies_parsed_at":"2025-08-09T08:54:56.909Z","dependency_job_id":"331b3581-57c7-4331-8ee3-8ea22026246b","html_url":"https://github.com/hcengineering/hulypulse","commit_stats":null,"previous_names":["hcengineering/hulypulse"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/hcengineering/hulypulse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hcengineering%2Fhulypulse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hcengineering%2Fhulypulse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hcengineering%2Fhulypulse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hcengineering%2Fhulypulse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hcengineering","download_url":"https://codeload.github.com/hcengineering/hulypulse/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hcengineering%2Fhulypulse/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270246665,"owners_count":24551754,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-13T02:00:09.904Z","response_time":66,"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":[],"created_at":"2025-08-13T13:14:53.179Z","updated_at":"2025-08-13T13:14:56.410Z","avatar_url":"https://github.com/hcengineering.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Hulypulse\n\nHulypulse is a service that enables clients to share information on a “whiteboard”. Clients connected to the same “whiteboard” see data provided by other clients to the whiteboard.\n\nThe service is exposed as REST and WebSocket API.\n\n**Usage scenarios:**\n\n- user presence in a document\n- user is “typing” event\n- user cursor position in editor or drawing board\n- service posts a process status\n\n## Key\n\nKey is a string that consists of one or multiple segments separated by ‘/’. Example: foo/bar/baz.\nKey may not end with ‘/’\nSegment may not contain special characters (‘*’, ‘?’, ‘[’, ‘]’,‘\\’,‘\\x00..\\xF1’,‘\\x7F’,‘\"’,‘'’)\nSegment may not be empty\nKey segment may be private (prefixed with ‘$’)\n\n    Query\n\nMay not contain special characters (‘*’, ‘?’, ‘[’, ‘]’,‘\\’,‘\\x00..\\xF1’,‘\\x7F’,‘\"’,‘'’)\nIt is possible to use prefix, for listings / subscriptions  (prefix ends with segment separator ‘/’)\n\nGET/SUBSCRIBE/..   a/b → single key\nGET/SUBSCRIBE/..   a/b/c/ → multiple\n\n    If multiple\n\nselect all keys starting with prefix\nskip keys, containing private segments to the right from the prefix\n\n    example\n1. /a/b/$c/$d,  2. /a/b/c,  3. /a/b/$c, 4. /a/b/$c/$d/e\n/ → [2]\n/a/b/ → [2]\n/a/b/$c/ → [3]\n/a/b/$c/$d/ → [4]\n/a/b/$c/$d → [1]\n\n\n## Data\n“Data” is an arbitrary JSON document.\nSize of data is limited to some reasonable size\n\n## API\nMethods\n\nGET - returns values of one key\n\nLIST - returns values with given prefix until the “sentinel”\n\nPUT - put value to the key\n- Support CAS\n- Support If-* headers\n\nDELETE - delete value of the key\n\nSUB - subscribe to key data + get initial state\nBehavior identical to LIST\n\nUNSUB - unsubscribe to key data\n\n\n## HTTP API\n\n```PUT /{workspace}/{key}```\n- Input\n    Body - data\n    Content-Type: application/json (do we need something else?)\n    Content-Length: optional\n    Headers: TTL or absolute expiration time\n        HULY-TTL\n        HULY-EXPIRE-AT\n    ** Conditional Headers If-*: **\n\t- `If-Match: *` — update only if the key exists\n\t- `If-Match: \u003cmd5\u003e` — update only if current value's MD5 matches\n\t- `If-None-Match: *` — insert only if the key does not exist\n- Output\n    - Status:\n\t- `201` if inserted with `If-None-Match: *`\n\t- `204` on successful insert or update\n\t- `412` if the condition is not met\n\t- `400` if headers are invalid\n    - No body\n\n```PATCH /{workspace}/{key}```\n- TODO (not in v1)\n\n```DELETE /{workspace}/{key}```\n- Output\n    Status: 204\n\n```GET /{workspace}/{key}```\n- Output\n    - Status 200\n    - Content-type: application/json\n    - Body:\n        - workspace\n\t- key\n        - data\n        - expiresAt ?\n\n```GET /{workspace}?prefix={key}```\n- Output\n    - Status 200\n    - Content-type: application/json\n    - Body (array):\n        - workspace\n        - key\n        - data\n        - expiresAt ?\n\n## WebSocket API\n\n**Client to Server**\n\n```PUT```\n    - correlation id (optional)\n    - type: \"put\"\n    - key: “foo/bar“\n    - data\n    - TTL / expiresAt\n\n```DELETE```\n    - correlation id (optional)\n    - type: \"delete\"\n    - key: “foo/bar“\n\n```SUB```\n    type: \"sub\"\n    key: “foo/bar“\n\n```UNSUB```\n    - type: \"unsub\"\n    - key: “foo/bar“\n\n**Server to Client**\n\n```PUT```\n    - correlation id (optional)\n    - type: \"put\"\n    - ?? TODO: user? workspace: \"11111111-2222-3333-4444-555555555555\"\n    - key: “foo/bar“\n    - data\n    - expiresAt\n\n```DELETE```\n    - correlation id (optional)\n    - type: \"delete\"\n    - key: “foo/bar“\n\n\n## Running\n\nPre-build docker images is available at: hardcoreeng/service_hulypulse:{tag}.\n\nYou can use the following command to run the image locally:\n```bash\ndocker run -p 8095:8095 -it --rm hardcoreeng/service_hulypulse:{tag}\"\n```\n\nIf you want to run the service as a part of local huly development environment use the following command:\n```bash\n export HULY_REDIS_URLS=\"redis://huly.local:6379\"\n docker run --rm -it --network dev_default -p 8095:8095 hardcoreeng/service_hulypulse:{tag}\n```\nThis will run Hulypulse in the same network as the rest of huly services, and set the redis connection string to the one matching the local dev redis instance.\n\nYou can then access hulypulse at http://localhost:8095.\n\n\n## Authetication\nHulypulse uses bearer JWT token authetication. At the moment, it will accept any token signed by the hulypulse secret. The secret is set in the environment variable HULY_TOKEN_SECRET variable. \n\n## Configuration\nThe following environment variables are used to configure hulypulse:\n   - ```HULY_BIND_HOST```: host to bind the server to (default: 0.0.0.0)\n   - ```HULY_BIND_PORT```: port to bind the server to (default: 8094)\n   - ```HULY_PAYLOAD_SIZE_LIMIT```: maximum size of the payload (default: 2Mb)\n   - ```HULY_TOKEN_SECRET```: secret used to sign JWT tokens (default: secret)\n   - ```HULY_REDIS_URLS```: redis connection string (default: redis://huly.local:6379)\n   - ```HULY_REDIS_PASSWORD```: redis password (default: \"\u0026lt;invalid\u0026gt;\")\n   - ```HULY_REDIS_MODE```: redis mode \"direct\" or \"sentinel\" (default: \"direct\")\n   - ```HULY_REDIS_SERVICE```: redis service (default: \"mymaster\")\n   - ```HULY_MAX_TTL```: maximum storage time (default: 3600)\n\n## Todo (in no particular order)\n- [ ] Optional value encryption\n- [ ] HEAD request\n- [ ] Conditional update (optimistic locking)\n- [ ] Support for open telemetry\n- [ ] Concurrency control for database migration (several instances of hulypulse are updated at the same time)\n- [ ] TLS support\n- [ ] Namespacee based access control\n- [ ] Liveness/readiness probe endpoint\n\n## Contributing\nContributions are welcome! Please open an issue or a pull request if you have any suggestions or improvements.\n\n## License\nThis project is licensed under EPL-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhcengineering%2Fhulypulse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhcengineering%2Fhulypulse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhcengineering%2Fhulypulse/lists"}