{"id":28316736,"url":"https://github.com/erlef/localhost-run","last_synced_at":"2025-06-24T06:30:42.262Z","repository":{"id":291145859,"uuid":"976749170","full_name":"erlef/localhost-run","owner":"erlef","description":"Expose local ports to the internet via SSH tunnels using Elixir and localhost.run.","archived":false,"fork":false,"pushed_at":"2025-06-11T08:02:50.000Z","size":52,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-11T09:21:30.214Z","etag":null,"topics":["elixir","localhost","tunnel","tunnel-client"],"latest_commit_sha":null,"homepage":"https://hex.pm/packages/localhost_run","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/erlef.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/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},"funding":{"custom":["https://members.erlef.org/join-us","https://erlef.org/sponsors#become-a-sponsor"]}},"created_at":"2025-05-02T17:17:17.000Z","updated_at":"2025-06-11T08:02:53.000Z","dependencies_parsed_at":null,"dependency_job_id":"7e76e5cb-9159-452b-acb0-2573c13efe66","html_url":"https://github.com/erlef/localhost-run","commit_stats":null,"previous_names":["erlef/localhost-run"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/erlef/localhost-run","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erlef%2Flocalhost-run","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erlef%2Flocalhost-run/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erlef%2Flocalhost-run/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erlef%2Flocalhost-run/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/erlef","download_url":"https://codeload.github.com/erlef/localhost-run/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erlef%2Flocalhost-run/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261620042,"owners_count":23185448,"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","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":["elixir","localhost","tunnel","tunnel-client"],"created_at":"2025-05-25T03:08:02.649Z","updated_at":"2025-06-24T06:30:42.250Z","avatar_url":"https://github.com/erlef.png","language":"Elixir","readme":"# LocalhostRun\n\n[![Main Branch](https://github.com/erlef/localhost-run/actions/workflows/branch_main.yml/badge.svg?branch=main)](https://github.com/erlef/localhost-run/actions/workflows/branch_main.yml)\n[![Module Version](https://img.shields.io/hexpm/v/localhost_run.svg)](https://hex.pm/packages/localhost_run)\n[![Total Download](https://img.shields.io/hexpm/dt/localhost_run.svg)](https://hex.pm/packages/localhost_run)\n[![License](https://img.shields.io/hexpm/l/localhost_run.svg)](https://github.com/erlef/localhost-run/blob/main/LICENSE)\n[![Last Updated](https://img.shields.io/github/last-commit/erlef/localhost-run.svg)](https://github.com/erlef/localhost-run/commits/master)\n[![Coverage Status](https://coveralls.io/repos/github/erlef/localhost-run/badge.svg?branch=main)](https://coveralls.io/github/erlef/localhost-run?branch=main)\n\nA small Elixir client for [localhost.run](https://localhost.run), which lets you\nexpose local ports to the internet via SSH tunnels.\n\nThis can be useful for development, testing webhooks, or sharing a local server\nwith someone. It supports both unauthenticated and authenticated connections\nusing SSH keys or your local SSH agent.\n\n## Installation\n\nAdd to your `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:localhost_run, \"~\u003e 0.1.0\"}\n  ]\nend\n```\n\n## Basic Usage\n\nYou can use this module in different ways depending on your needs.\n\n### Start with `GenServer`\n\n```elixir\n{:ok, pid} = LocalhostRun.start_link(internal_port: 4000)\n```\n\n### As a supervised process\n\n```elixir\nchildren = [\n  {LocalhostRun, [internal_port: 4000]}\n]\n\nopts = [strategy: :one_for_one, name: MyApp.Supervisor]\nSupervisor.start_link(children, opts)\n```\n\n### Manual connection\n\n```elixir\n{:ok, {conn_ref, channel_id}} = LocalhostRun.connect(internal_port: 4000)\n\nreceive do\n  {:ssh_cm, ^conn_ref, {:data, ^channel_id, 0, message}} -\u003e\n    case JSON.decode!(message) do\n      %{\"event\" =\u003e \"tcpip-forward\", \"address\" =\u003e host} -\u003e\n        Logger.info(\"Tunnel established to #{host}\")\n\n      %{\"event\" =\u003e \"authn\", \"message\" =\u003e msg} -\u003e\n        Logger.info(\"Authentication successful: #{msg}\")\n\n      %{\"message\" =\u003e msg} -\u003e\n        Logger.debug(\"Received: #{inspect(msg)}\")\n    end\nend\n```\n\n## Authentication\n\nTo use features like custom domains or longer tunnel timeouts, you’ll need to\nauthenticate with localhost.run.\n\nSee their [docs](https://localhost.run/docs/custom-domains) for details.\n\nYou have two options:\n\n### SSH agent\n\nMake sure your SSH agent is running and your key is added:\n\n```bash\neval \"$(ssh-agent -s)\"\nssh-add ~/.ssh/id_rsa\n```\n\nThen pass the agent to `LocalhostRun`:\n\n```elixir\n{:ok, pid} = LocalhostRun.start_link(internal_port: 4000, ssh_options: [\n  user: \"your_username\",\n  key_cb: {:ssh_agent, []}\n])\n```\n\n### SSH key file\n\nAlternatively, you can specify a key file:\n\n```elixir\n{:ok, pid} = LocalhostRun.start_link(internal_port: 4000, ssh_options: [\n  user: \"your_username\",\n  key_cb: {:ssh_file, []}\n])\n```\n\n## Getting the tunnel address\n\nIf you need to get the public address of the tunnel:\n\n```elixir\n{:ok, host} = LocalhostRun.get_exposed_host()\nIO.puts(\"Tunnel is available at #{host}\")\n```\n\n## Logging\n\nThe module sets some helpful metadata in your logs:\n\n* `:ssh_host`\n* `:ssh_port`\n* `:external_host`\n* `:internal_port`\n\nThis can help with debugging or tracing tunnel usage.\n\n## License\n\n    Copyright 2025 Jonatan Männchen\n    Copyright 2025 Erlang Ecosystem Foundation\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n","funding_links":["https://members.erlef.org/join-us","https://erlef.org/sponsors#become-a-sponsor"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferlef%2Flocalhost-run","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ferlef%2Flocalhost-run","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferlef%2Flocalhost-run/lists"}