{"id":20947748,"url":"https://github.com/alphahydrae/monohook","last_synced_at":"2025-07-22T20:05:59.860Z","repository":{"id":66264919,"uuid":"186593201","full_name":"AlphaHydrae/monohook","owner":"AlphaHydrae","description":"Expose a single HTTP webhook endpoint that executes a command","archived":false,"fork":false,"pushed_at":"2019-06-05T07:54:49.000Z","size":43,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-03-13T04:42:45.539Z","etag":null,"topics":["command-line","golang","webhook","webhooks-catcher"],"latest_commit_sha":null,"homepage":"","language":"Go","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/AlphaHydrae.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","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}},"created_at":"2019-05-14T09:46:57.000Z","updated_at":"2020-10-01T14:19:39.000Z","dependencies_parsed_at":"2023-06-12T20:30:53.142Z","dependency_job_id":null,"html_url":"https://github.com/AlphaHydrae/monohook","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/AlphaHydrae/monohook","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlphaHydrae%2Fmonohook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlphaHydrae%2Fmonohook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlphaHydrae%2Fmonohook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlphaHydrae%2Fmonohook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AlphaHydrae","download_url":"https://codeload.github.com/AlphaHydrae/monohook/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlphaHydrae%2Fmonohook/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266563915,"owners_count":23948689,"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-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"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":["command-line","golang","webhook","webhooks-catcher"],"created_at":"2024-11-19T00:12:55.517Z","updated_at":"2025-07-22T20:05:59.842Z","avatar_url":"https://github.com/AlphaHydrae.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MonoHook\n\nExpose a single HTTP webhook endpoint that executes a command:\n\n```\n$\u003e monohook --authorization letmein --concurrency 1 --port 5000 -- deploy.sh\n```\n\nTrigger execution of the command by POSTing to the webhook:\n\n```\n$\u003e curl -v -X POST -H \"Authorization: Bearer letmein\" http://localhost:5000\n\u003e POST / HTTP/1.1\n\u003e Host: localhost:5000\n\u003e User-Agent: curl/7.54.0\n\u003e Accept: */*\n\u003e Authorization: Bearer letmein\n\u003e\n\u003c HTTP/1.1 202 Accepted\n\u003c Date: Wed, 15 May 2019 07:06:30 GMT\n\u003c Content-Length: 0\n```\n\nThe command is executed asynchronously.\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n\n- [Installation](#installation)\n  - [Docker](#docker)\n  - [Download binary](#download-binary)\n- [Usage](#usage)\n  - [Authentication](#authentication)\n  - [Concurrency](#concurrency)\n  - [Buffering](#buffering)\n  - [Command environment \u0026 working directory](#command-environment--working-directory)\n  - [Forwarding HTTP request data to the command](#forwarding-http-request-data-to-the-command)\n  - [Port number](#port-number)\n  - [TL;DR](#tldr)\n- [Exit codes](#exit-codes)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n* [Changelog](CHANGELOG.md)\n\n[![version](https://img.shields.io/badge/Version-v3.0.1-blue.svg)](https://github.com/AlphaHydrae/monohook/releases/tag/v3.0.1)\n[![build status](https://travis-ci.org/AlphaHydrae/monohook.svg?branch=master)](https://travis-ci.org/AlphaHydrae/monohook)\n[![license](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE.txt)\n\n\n\n## Installation\n\n### Docker\n\nMonoHook is [available on Docker Hub][hub]. Run it with Docker:\n\n```\n$\u003e docker run --rm -it -p 5000:5000 alphahydrae/monohook:2.0.0 -a letmein -- echo Hook triggered\n```\n\nTest it:\n\n```\n$\u003e curl -v -X POST -H \"Authorization: Bearer letmein\" http://localhost:5000\n```\n\nYou should see the command being executed in the container's logs:\n\n```\nExecuting /bin/echo\nHook triggered\nSuccessfully executed /bin/echo\n```\n\nTo use MonoHook in a `Dockerfile`, simply download the binary and make it executable:\n\n```\nRUN wget -O /usr/local/bin/monohook \\\n    https://github.com/AlphaHydrae/monohook/releases/download/v3.0.1/monohook_linux_amd64 \u0026\u0026 \\\n    chmod +x /usr/local/bin/monohook\n```\n\n### Download binary\n\n* **Linux**\n\n  ```\n  wget -O /usr/local/bin/monohook \\\n    https://github.com/AlphaHydrae/monohook/releases/download/v3.0.1/monohook_linux_amd64 \u0026\u0026 \\\n    chmod +x /usr/local/bin/monohook\n  ```\n* **Linux (arm64)**\n\n  ```\n  wget -O /usr/local/bin/monohook \\\n    https://github.com/AlphaHydrae/monohook/releases/download/v3.0.1/monohook_linux_arm64 \u0026\u0026 \\\n    chmod +x /usr/local/bin/monohook\n  ```\n* **macOS**\n\n  ```\n  wget -O /usr/local/bin/monohook \\\n    https://github.com/AlphaHydrae/monohook/releases/download/v3.0.1/monohook_darwin_amd64 \u0026\u0026 \\\n    chmod +x /usr/local/bin/monohook\n  ```\n* **Windows**\n\n  ```\n  wget -O /usr/local/bin/monohook \\\n    https://github.com/AlphaHydrae/monohook/releases/download/v3.0.1/monohook_windows_amd64 \u0026\u0026 \\\n    chmod +x /usr/local/bin/monohook\n  ```\n\n\n\n## Usage\n\nMonoHook listens on a given TCP port, 5000 by default, and executes a command\nwhen a request is received. The command must be specified after the argument\nterminator (`--`):\n\n```\n$\u003e monohook -- echo Hook triggered\n```\n\nTo trigger execution of the command, simply make a POST request to the hook:\n\n```\n$\u003e curl -v -X POST http://localhost:5000\n```\n\nIf you go back to the terminal where MonoHook is running, you will see that the\ncommand's output is piped to MonoHook's own standard output so you can see it:\n\n```\nExecuting /bin/echo\nHook triggered\nSuccessfully executed /bin/echo\n```\n\n### Authentication\n\nYou can secure the webhook by requiring that an authentication token be sent,\nusing the `-a, --authorization \u003cTOKEN\u003e` command-line flag or the\n`$MONOHOOK_AUTHORIZATION` environment variable:\n\n```\n$\u003e monohook -a letmein -- echo Hook triggered\n```\n\nMonoHook will respond to requests without authentication with HTTP 401\nUnauthorized, and the command will not be executed:\n\n```\n$\u003e curl -v -X POST http://localhost:5000\n\u003e POST / HTTP/1.1\n\u003e Host: localhost:5000\n\u003e User-Agent: curl/7.54.0\n\u003e Accept: */*\n\u003e\n\u003c HTTP/1.1 401 Unauthorized\n\u003c Date: Wed, 15 May 2019 07:22:05 GMT\n\u003c Content-Length: 0\n```\n\nYou may send the authentication token as a bearer token in the `Authorization`\nheader:\n\n```\n$\u003e curl -v -X POST -H \"Authorization: Bearer letmein\" http://localhost:5000\n\u003e POST / HTTP/1.1\n\u003e Host: localhost:5000\n\u003e User-Agent: curl/7.54.0\n\u003e Accept: */*\n\u003e Authorization: Bearer letmein\n\u003e\n\u003c HTTP/1.1 202 Accepted\n\u003c Date: Wed, 15 May 2019 07:24:11 GMT\n\u003c Content-Length: 0\n```\n\nOr in the `authorization` URL query parameter:\n\n```\n$\u003e curl -v -X POST \"http://localhost:5000?authorization=letmein\"\n\u003e POST /?authorization=letmein HTTP/1.1\n\u003e Host: localhost:5000\n\u003e User-Agent: curl/7.54.0\n\u003e Accept: */*\n\u003e\n\u003c HTTP/1.1 202 Accepted\n\u003c Date: Wed, 15 May 2019 07:24:59 GMT\n\u003c Content-Length: 0\n```\n\n\u003e MonoHook does not support HTTPS, therefore it is recommended that you put it\n\u003e behind a reverse proxy like Apache or nginx and use an SSL/TLS certificate.\n\u003e\n\u003e For example, you could use this location in an nginx server block:\n\u003e\n\u003e ```\n\u003e location /hooks/deploy {\n\u003e   proxy_set_header X-Real-IP $remote_addr;\n\u003e   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n\u003e   proxy_set_header X-Forwarded-Proto https;\n\u003e   proxy_set_header Host $http_host;\n\u003e   proxy_redirect off;\n\u003e   proxy_pass http://localhost:3000;\n\u003e }\n\u003e ```\n\n### Concurrency\n\nBy default, MonoHook will allow only one execution of the command at a time. If\nmultiple requests are received in quick succession, they will be buffered until\neach previou execution has completed.\n\nYou may test this by running MonoHook with no options:\n\n```\n$\u003e monohook -- echo Hook triggered\n```\n\nThen triggering 10 requests with cURL:\n\n```\n$\u003e for n in 0 1 2 3 4 5 6 7 8 9; do curl -v -X POST http://localhost:5000; done\n\u003c HTTP/1.1 202 Accepted\n\u003c HTTP/1.1 202 Accepted\n\u003c HTTP/1.1 202 Accepted\n...\n```\n\nYou can see in MonoHook's output that it is waiting for each command execution\nto complete before starting the next one:\n\n```\nExecuting /bin/echo\nHook triggered\nSuccessfully executed /bin/echo\n\nExecuting /bin/echo\nHook triggered\nSuccessfully executed /bin/echo\n\nExecuting /bin/echo\nHook triggered\nSuccessfully executed /bin/echo\n\n...\n```\n\nYou may specify a greater concurrency with the `-c, --concurrency \u003cLIMIT\u003e`\ncommand-line flag, or the `$MONOHOOK_CONCURRENCY` environment variable. This\nexample uses the `sleep` command to simulate a long-running command:\n\n```\n$\u003e monohook -c 3 -- sleep 2\n```\n\nTrigger 10 requests like before:\n\n```\n$\u003e for n in 0 1 2 3 4 5 6 7 8 9; do curl -v -X POST http://localhost:5000; done\n\u003c HTTP/1.1 202 Accepted\n\u003c HTTP/1.1 202 Accepted\n\u003c HTTP/1.1 202 Accepted\n...\n```\n\nThis time, you will see MonoHook executing the command in parallel in batches of\n3:\n\n```\nExecuting /bin/sleep\nExecuting /bin/sleep\nExecuting /bin/sleep\nSuccessfully executed /bin/sleep\nSuccessfully executed /bin/sleep\nSuccessfully executed /bin/sleep\n\nExecuting /bin/sleep\nExecuting /bin/sleep\nExecuting /bin/sleep\nSuccessfully executed /bin/sleep\nSuccessfully executed /bin/sleep\nSuccessfully executed /bin/sleep\n\n...\n```\n\n\u003e You may specify infinite concurrency by setting a concurrency of zero:\n\u003e `monohook -c 0 -- sleep 2`.\n\n### Buffering\n\nIf concurrency is limited (which is the default) and MonoHook is already busy\nexecuting a long-running command or commands, it will keep a buffer of pending\nrequests to execute. They will be executed as soon as the execution of previous\ncommands has completed and the configured concurrency limit permits it.\n\nThis buffer has a limited size (10 by default). If the buffer is full and\nMonoHook receives more requests, it will discard them and respond with HTTP 429\nToo Many Requests. Those requests will never be processed.\n\nYou may observe this behavior by running MonoHook with a small buffer and a\nlong-running command:\n\n```\n$\u003e monohook -b 3 -- sleep 2\n```\n\nTrigger 10 requests like before:\n\n```\n$\u003e for n in 0 1 2 3 4 5 6 7 8 9; do curl -v -X POST http://localhost:5000; done\n\u003c HTTP/1.1 202 Accepted\n\u003c HTTP/1.1 202 Accepted\n\u003c HTTP/1.1 202 Accepted\n\u003c HTTP/1.1 202 Accepted\n\u003c HTTP/1.1 429 Too Many Requests\n\u003c HTTP/1.1 429 Too Many Requests\n\u003c HTTP/1.1 429 Too Many Requests\n...\n```\n\nIn this example, MonoHook immediately started executing the first request, then\nput the next 3 in the buffer. Subsequent requests are discarded.\n\nThe size of the buffer can be configured with the `-b, --buffer \u003cSIZE\u003e`\ncommand-line flag or the `$MONOHOOK_BUFFER` environment variable.\n\n\u003e You may specify an infinite buffer by setting the buffer to zero: `monohook -b\n\u003e 0 -- sleep 2`\n\n### Command environment \u0026 working directory\n\nThe command will be executed with the same environment variables as those\navailable to MonoHook. By default, the working directory is the one where\nMonoHook is run.\n\nIt can be configured to execute the command in a specific working directory with\nthe `-C, --cwd \u003cDIR\u003e` command-line flag or the `$MONOHOOK_CWD` environment\nvariable:\n\n```\n$\u003e monohook -C /path/to/somewhere - pwd\n```\n\n### Forwarding HTTP request data to the command\n\nThe command does not have access to the HTTP request that triggered it by\ndefault. You may choose to forward part or all of the following data:\n\n* The HTTP request body with the `-B, --forward-request-body` command-line flag\n  or the `$MONOHOOK_FORWARD_REQUEST_BODY` environment variable.\n\n  If enabled, the request body will be piped to the command's standard input.\n\n  If the `-Y, --cache-request-body` command-line flag or the\n  `$MONOHOOK_CACHE_REQUEST_BODY` environment variable is also enabled, the\n  request body will be read immediately and cached for when the hook executes\n  (which might take a while depending on concurrency settings). Otherwise, the\n  request will wait to complete until the hook can actually execute.\n* HTTP request headers with the `-H, --forward-request-headers` command-line\n  flag or the `$MONOHOOK_FORWARD_REQUEST_HEADERS` environment variable.\n\n  If enabled, request headers will be provided to the command as environment\n  variables, with the following naming convention:\n\n      Accept -\u003e $MONOHOOK_REQUEST_HEADER_ACCEPT\n      Content-Type -\u003e $MONOHOOK_REQUEST_HEADER_CONTENT_TYPE\n* The HTTP request URL with the `-U, --forward-request-url` command-line flag or\n  the `$MONOHOOK_FORWARD_REQUEST_URL` environment variable.\n\n  If enabled, the URL parsed from the HTTP request line is provided to the\n  command as the `$MONOHOOK_REQUEST_URL` environment variable.\n\nThe following example shows how to save the body of each HTTP request to a file:\n\n```\n$\u003e monohook -B -- tee -a body.txt\n```\n\nTrigger 10 cURL requests with a body:\n\n```\n$\u003e for n in 0 1 2 3 4 5 6 7 8 9; do curl -v -d $n http://localhost:5000; done\n```\n\nCheck the file's contents:\n\n```\n$\u003e cat body.txt\n0123456789\n```\n\n\u003e Note that forwarding the request body without caching it (using the `-Y,\n\u003e --cache-request-body` command-line flag or the `$MONOHOOK_CACHE_REQUEST_BODY`\n\u003e environment variable) means that the execution of the command will not end\n\u003e before the HTTP request body has been completely consumed. This may have a\n\u003e performance impact. For example, with a concurrency of 1, each request will\n\u003e have to wait for the previous one to upload its data.\n\n### Port number\n\nMonoHook listens on port 5000 by default. You may use a different port with the\n`-p, --port \u003cNUMBER\u003e` command-line flag or the `$MONOHOOK_PORT` environment\nvariable.\n\n### TL;DR\n\n```\nmonohook exposes a single HTTP webhook endpoint that executes a command.\n\nUsage:\n  monohook [OPTION...] [--] [EXEC...]\n\nOptions:\n  -a, --authorization string      Authentication token that must be sent as a Bearer token in the 'Authorization' header or as the 'authorization' URL query parameter\n  -b, --buffer uint               Maximum number of requests to queue before refusing subsequent ones until the queue is freed (zero for infinite) (default 10)\n  -Y, --cache-request-body        Whether to cache the HTTP request's body so the hook can return right away\n  -c, --concurrency uint          Maximum number of times the command should be executed in parallel (zero for infinite concurrency) (default 1)\n  -C, --cwd string                Working directory in which to run the command\n  -B, --forward-request-body      Whether to forward each HTTP request's body to the the command's standard input\n  -H, --forward-request-headers   Whether to forward each HTTP request's headers to the the command as environment variables (e.g. Content-Type becomes $MONOHOOK_REQUEST_HEADER_CONTENT_TYPE)\n  -U, --forward-request-url       Whether to forward each HTTP request's URL to the the command as the $MONOHOOK_REQUEST_URL environment variable\n  -p, --port uint                 Port on which to listen to (default 5000)\n  -q, --quiet                     Do not print anything except the command's standard output and error streams (default false)\n\nExamples:\n  Update a file when the hook is triggered:\n    monohook -- touch hooked.txt\n  Deploy an application when the hook is triggered:\n    monohook -a letmein -- deploy-stuff.sh\n```\n\n\n\n## Exit codes\n\n**MonoHook** may exit with the following status codes to indicate a problem:\n\nCode | Description\n:--- | :---\n`1`  | Invalid arguments were given.\n`2`  | The command to execute (provided after `--`) could not be found in the `$PATH`.\n\n\n\n[hub]: https://hub.docker.com/r/alphahydrae/monohook\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falphahydrae%2Fmonohook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falphahydrae%2Fmonohook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falphahydrae%2Fmonohook/lists"}