{"id":16684536,"url":"https://github.com/saoudrizwan/kamisama","last_synced_at":"2026-05-09T05:31:09.130Z","repository":{"id":57288226,"uuid":"193365135","full_name":"saoudrizwan/kamisama","owner":"saoudrizwan","description":"Cluster with automatic respawn and graceful shutdown","archived":false,"fork":false,"pushed_at":"2019-10-20T17:31:14.000Z","size":37,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-26T14:43:50.212Z","etag":null,"topics":["javascript","nodejs","typescript"],"latest_commit_sha":null,"homepage":"https://gitroyalty.com/saoudrizwan/kamisama","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/saoudrizwan.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}},"created_at":"2019-06-23T15:32:55.000Z","updated_at":"2019-10-20T17:31:16.000Z","dependencies_parsed_at":"2022-08-27T23:52:40.856Z","dependency_job_id":null,"html_url":"https://github.com/saoudrizwan/kamisama","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/saoudrizwan/kamisama","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saoudrizwan%2Fkamisama","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saoudrizwan%2Fkamisama/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saoudrizwan%2Fkamisama/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saoudrizwan%2Fkamisama/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saoudrizwan","download_url":"https://codeload.github.com/saoudrizwan/kamisama/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saoudrizwan%2Fkamisama/sbom","scorecard":{"id":800213,"data":{"date":"2025-08-11","repo":{"name":"github.com/saoudrizwan/kamisama","commit":"d8e849a3ad435f39fd982d5b224633ed30971c26"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-23T10:14:03.467Z","repository_id":57288226,"created_at":"2025-08-23T10:14:03.468Z","updated_at":"2025-08-23T10:14:03.468Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32808383,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"online","status_checked_at":"2026-05-09T02:00:06.633Z","response_time":123,"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":["javascript","nodejs","typescript"],"created_at":"2024-10-12T14:44:12.428Z","updated_at":"2026-05-09T05:31:09.059Z","avatar_url":"https://github.com/saoudrizwan.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\t\u003cimg src=\"https://user-images.githubusercontent.com/7799382/59981707-a0cef080-95d5-11e9-863a-c48dbfdd4d84.png\" \u003e\n\t\u003ch1\u003ekamisama\u003c/h1\u003e\n\t\u003cp align=\"center\"\u003e\n    \u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e\n  • \u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e\n  • \u003ca href=\"#license\"\u003eLicense\u003c/a\u003e\n\u003c/p\u003e\n\u003c/div\u003e\n\n[![GitRoyalty](https://img.shields.io/endpoint?url=https://gitroyalty.com/saoudrizwan/kamisama/badge.json)](https://gitroyalty.com/saoudrizwan/kamisama)\n\n\nSince Node is single threaded, it doesn't automatically take advantage of a multi-core CPU. Clustering allows your app to spawn worker processes each running their own thread on a core, while all sharing the same server port. Node intelligently distributes new connections across the workers in a round-robin fashion, ensuring work load is balanced. kamisama abstracts this boilerplate implementation, and automatically respawns workers if they crash. kamisama also lets you implement a promise based shutdown function to ensure each worker shuts down gracefully (i.e. [finish http requests](https://nodejs.org/api/net.html#net_server_close_callback), [close database connections](https://redis.io/commands/quit), etc.)\n\n**index.js/ts**\n\n```javascript\nkamisama({\n    workers: 3,\n    run: async id =\u003e {\n        console.log(`running worker ${id}`)\n        // connect to database, start server, etc.\n    },\n    shutdown: async (id, signal) =\u003e {\n        console.log(`worker ${id} shutting down from ${signal}`)\n        // finish http requests, close database connection, etc.\n    },\n    timeout: 5000\n})\n```\n\n**console**\n\n```bash\n$ node index.js\nrunning worker 1\nrunning worker 2\nrunning worker 3\n```\n\n```bash\n^C\nworker 1 shutting down from SIGINT\nworker 2 shutting down from SIGINT\nworker 3 shutting down from SIGINT\n```\n\n## Installation\n\nSupport kamisama's contributors with a monthly subscription on https://gitroyalty.com/saoudrizwan/kamisama to install this package.\n\n[![Subscribe on GitRoyalty](https://gitroyalty.com/saoudrizwan/kamisama/subscribe.svg)](https://gitroyalty.com/saoudrizwan/kamisama)\n\u003cbr/\u003e\n\u003csub\u003e* comes with a 2 week free trial and can be cancelled anytime\u003c/sub\u003e\n\n## Usage\n\nYou can pass kamisama a run function if you want to create as many workers as there are CPU cores on your machine.\n\n```javascript\nkamisama(id =\u003e {\n    console.log(`running worker ${id}`)\n})\n```\n\nKeep in mind that running a process per core takes maximum advantage of the machine, so although this is good for production, you should stick with one or two workers during development.\n\nUsually you'd want to use `KamisamaOptions` though, like in the example.\n\n```javascript\nkamisama({\n    workers?: number\n    run: (id: number): void\n    shutdown?: (id: number, signal: \"SIGINT\" | \"SIGTERM\" | \"SIGHUP\" | \"SIGBREAK\" | \"SIGUSR2\"): any\n    timeout?: number\n})\n```\n\n`workers?: number`\n\n-   The number of worker processes to fork for the cluster.\n-   [Heroku provides an appropriate value](https://devcenter.heroku.com/articles/node-concurrency#enabling-concurrency-in-your-app) for this based on your dyno type: `process.env.WEB_CONCURRENCY`\n-   Default value: `os.cpus().length` (# of cores in machine's CPU)\n\n`run: (id: number): void`\n\n-   Function called for each worker process. `id` is the worker ID given by the master process.\n-   This is where you connect to databases, start your http server, etc.\n-   Under the hood kamisama calls `Promise.resolve(run(id))`, and if an error is thrown then the process exits immediately. However you may instead want to log this first before letting kamisama exit the process.\n    ```javascript\n    run: async (id) =\u003e {\n        try {\n            await database.connect()\n        } catch (error) {\n            await logger.error(error)\n            process.exit(1) // this way you're responsible for exiting\n        }\n    }\n    ```\n\n`shutdown?: (id: number, signal: \"SIGINT\" | \"SIGTERM\" | \"SIGHUP\" | \"SIGBREAK\" | \"SIGUSR2\"): any`\n\n-   Function called when the master process receives one of the common [shutdown signals](#what-signals-does-kamisama-listen-to).\n-   You must not attach listeners to these shutdown signals in your worker process if you want kamisama's listeners to call your shutdown function. (However you *should* attach listeners to [process-specific signals](#what-signals-should-you-listen-to) that can help you debug errors in your app.)\n-   Similar to the run function, kamisama calls `Promise.resolve(shutdown(id, signal))`, so you can use `async`/`await` to shutdown asynchronously.\n-   This is where you would [gracefully shutdown](https://hackernoon.com/graceful-shutdown-in-nodejs-2f8f59d1c357) your worker process. This is good practice for web servers, otherwise users' requests would get dropped, database updates would be aborted, and the list goes on.\n-   While it's normal practice to use a listener for shutdown signals (i.e. `process.on(\"SIGINT\", shutdown)`) clustering can send duplicate signals, or even more when npm or nodemon are used to run the process. kamisama takes care of this issue and ensures your shutdown function gets called for each worker only once.\n-   kamisama also lets you gracefully shutdown workers when nodemon restarts your app on a file change. Although this can be useful to ensure development and production environments behave the same, it may speed up your workflow to disable this. Simply add a conditional for the `SIGUSR2` signal like so:\n\n    ```javascript\n    shutdown: (id, signal) =\u003e {\n        if (signal === \"SIGUSR2\") return\n        // ...\n    }\n    ```\n\n`timeout?: number`\n\n-   How long to wait (milliseconds) until kamisama should forcefully shutdown worker processes after the shutdown function is called.\n-   Default value: `10_000` (10 seconds)\n\n### Master process\n\nkamisama only exposes functions to run in worker processes for the sake of simplicity. If you want to run code in the master process, use the `cluster` module in the same file you use kamisama. (Remember kamisama is just a function that runs in the master process.)\n\n```javascript\nif (cluster.isMaster) {\n    console.log(`Master process id: ${process.pid}`)\n}\n```\n\n### What signals does kamisama listen to?\n\n**`SIGINT`**\n\n-   Triggered by `CTRL`+`C` in Terminal\n\n**`SIGTERM`**\n\n-   Generic shutdown signal, usually sent by hosting service (i.e. Heroku)\n-   Not supported on Windows\n\n**`SIGHUP`**\n\n-   Usually generated when the console window is closed\n-   On Windows Node will be unconditionally terminated about 10 seconds later\n\n**`SIGBREAK`**\n\n-   Delivered on Windows when `Ctrl`+`Break` is pressed\n\n**`SIGUSR2`**\n\n-   [Sent by nodemon](https://github.com/remy/nodemon#controlling-shutdown-of-your-script) when a file has been updated\n\n### What signals should you listen to?\n\n**`uncaughtException`**\n\n-   Emitted when your app throws an unhandled error, inherently meaning your application is in an undefined state.\n-   You *could* ignore this message and resume function, but it is recommended to call your graceful shutdown function and let kamisama respawn the worker with a fresh slate.\n-   For example, you can log the error from the event before shutting down (you must exit the process yourself, so kamisama can respawn it).\n    ```javascript\n    process.on(\"uncaughtException\", async error =\u003e {\n        // log error information...\n        await shutdown(id, \"uncaughtException\")\n        process.exit()\n    })\n    ```\n-   If you're using an error tracking service, you may be able to override its shutdown function with your own graceful shutdown (i.e. Sentry's [`OnUncaughtException` integration](https://github.com/getsentry/sentry-docs/blob/master/src/collections/_documentation/platforms/node/default-integrations.md#onuncaughtexception))\n\n**`unhandledRejection`**\n\n-   Emitted when a promise throws an error that is not caught, possibly hanging an http request or halting expected execution, putting your application in an unexpected state.\n-   Normally this doesn't crash the process like `uncaughtException`, but Node promises that in future versions this will change. It is best to treat this event like an `uncaughtException`, restarting your server with kamisama and logging the error to to prevent this from happening again.\n-   For example, you can inspect the unhandled rejection's reason and promise and send it off to a service like Sentry before shutting down gracefully.\n    ```javascript\n    process.on(\"unhandledRejection\", async (reason, promise) =\u003e {\n        // send information to logger or error tracking service like Sentry...\n        await shutdown(id, \"unhandledRejection\")\n        process.exit()\n    })\n    ```\n\n## License\n\nkamisama uses the [MIT license](https://github.com/saoudrizwan/kamisama/blob/master/LICENSE). Please file an issue if you have any questions or if you'd like to share how you're using kamisama.\n\n## Questions?\n\nContact me by email \u003ca href=\"mailto:hello@saoudmr.com\"\u003ehello@saoudmr.com\u003c/a\u003e, or by Twitter \u003ca href=\"https://twitter.com/sdrzn\" target=\"_blank\"\u003e@sdrzn\u003c/a\u003e. Please create an \u003ca href=\"https://github.com/saoudrizwan/kamisama/issues\"\u003eissue\u003c/a\u003e if you come across a bug or would like a feature to be added.\n\n## Notable Mentions\n\n-   [throng](https://github.com/hunterloftis/throng) was a wonderful starting point and reference\n-   [Heroku's](https://devcenter.heroku.com/articles/node-concurrency) [wonderful](https://help.heroku.com/ROG3H81R/why-does-sigterm-handling-not-work-correctly-in-nodejs-with-npm) [docs](https://devcenter.heroku.com/articles/node-redis-workers#worker-process)\n-   [Maryna Sokolyan](https://dribbble.com/msokolyan) for the beautiful plum branch\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaoudrizwan%2Fkamisama","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaoudrizwan%2Fkamisama","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaoudrizwan%2Fkamisama/lists"}