{"id":49832069,"url":"https://github.com/appatalks/runnerlink","last_synced_at":"2026-05-13T21:04:32.086Z","repository":{"id":327529326,"uuid":"1109645661","full_name":"appatalks/RunnerLink","owner":"appatalks","description":"GitHub Hosted Runner through an SSH Gateway","archived":false,"fork":false,"pushed_at":"2026-05-11T03:57:23.000Z","size":1675,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-11T04:16:22.143Z","etag":null,"topics":["actions","copilot-cli","debug","github-hosted-runners","ssh"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/appatalks.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-04T04:58:01.000Z","updated_at":"2026-05-11T04:13:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/appatalks/RunnerLink","commit_stats":null,"previous_names":["appatalks/debug-hosted-runner"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/appatalks/RunnerLink","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appatalks%2FRunnerLink","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appatalks%2FRunnerLink/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appatalks%2FRunnerLink/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appatalks%2FRunnerLink/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/appatalks","download_url":"https://codeload.github.com/appatalks/RunnerLink/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appatalks%2FRunnerLink/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32999522,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T13:14:54.681Z","status":"ssl_error","status_checked_at":"2026-05-13T13:14:51.610Z","response_time":115,"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":["actions","copilot-cli","debug","github-hosted-runners","ssh"],"created_at":"2026-05-13T21:04:21.761Z","updated_at":"2026-05-13T21:04:32.077Z","avatar_url":"https://github.com/appatalks.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/RunnerLink.png\" alt=\"RunnerLink\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eRunnerLink\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\u003cstrong\u003eYour only Actions gateway.\u003c/strong\u003e\u003c/p\u003e\n\u003cp align=\"center\"\u003e\nDirect SSH access to GitHub-hosted runners through a reverse-tunnel gateway — from a single desktop app.\u003cbr\u003e\nLaunch runners, dock interactive terminals, browse remote files, and manage tool authentication, all without leaving your local machine.\n\u003c/p\u003e\n\n---\n\n## Quick Start\n\n```bash\n# 1. Generate SSH keys and build the gateway\nbash test-assets/generate-keys.sh\ndocker compose up --build -d\n\n# 2. Launch RunnerLink\n./runnerlink\n```\n\n\u003e **Requirements:** Python 3.10+, Tk, Docker, and the [GitHub CLI](https://cli.github.com/) (`gh`).\n\n---\n\n## How It Works\n\n```\n┌──────────────┐       reverse tunnel        ┌───────────────────┐\n│  RunnerLink  │ ──ssh→ gateway:50556 ←────── │  GitHub Runner    │\n│  (your PC)   │ ──ssh→ gateway:2222  ─────→  │  (Actions VM)     │\n└──────────────┘                              └───────────────────┘\n```\n\n1. **Gateway** — A lightweight Docker container runs OpenSSH with `GatewayPorts yes`, accepting reverse tunnels on ports 2222–2231.\n2. **GitHub Workflow** — A dispatched Actions job installs SSH on the runner, injects your public key, and opens a persistent reverse tunnel back to the gateway.\n3. **RunnerLink** — Connects through the tunnel, giving you a full interactive shell, file browser, and tool management on the live runner.\n\n---\n\n## Setup\n\n### 1 — Start the Gateway\n\n```bash\nbash test-assets/generate-keys.sh   # generate keys + authorized_keys\ndocker compose up --build -d         # build \u0026 start the gateway\ndocker compose logs -f gateway       # verify it's listening\n```\n\n### 2 — Port Forwarding\n\nForward these ports on your router to the machine running the gateway:\n\n| Port(s) | Purpose |\n|---------|---------|\n| **50556** | Gateway SSH — runners connect here |\n| **2222–2231** | Tunnel endpoints — you connect here |\n\n### 3 — GitHub Secrets\n\nGo to **Settings → Secrets → Actions** in your repository:\n\n| Secret | Value |\n|--------|-------|\n| `GATEWAY_PRIVATE_KEY` | Contents of `test-assets/id_rsa_test` (full file) |\n| `GATEWAY_HOST` | Your public IP (`curl -s icanhazip.com`) |\n| `GATEWAY_PORT` | `50556` |\n| `GATEWAY_USER` | `gateway` |\n\n### 4 — Launch RunnerLink\n\n```bash\n./runnerlink\n```\n\nClick **Probe** on the default runner to start a workflow, or use **Add Runner** to create additional named workstreams on separate tunnel ports.\n\n### 5 — Connect Manually (optional)\n\nYou don't need the app — any SSH client works:\n\n```bash\nssh -i test-assets/id_rsa_test -p 2222 -o StrictHostKeyChecking=no runner@\u003cYOUR_PUBLIC_IP\u003e\n```\n\n---\n\n## Features\n\n| Feature | Description |\n|---------|-------------|\n| **Docked Terminal** | Embed a full xterm session inside the app — one per runner, preserved across tab switches |\n| **Multiple Runners** | Run up to 10 concurrent workstreams on ports 2222–2231 |\n| **File Explorer** | Browse, navigate, and download files from the remote runner |\n| **Tool Auth** | One-click setup and authentication for GitHub CLI, Copilot, Azure CLI, and Azure Data Explorer |\n| **Countdown Timer** | Per-runner lifetime countdown with amber (\u003c 1 hr) and red (\u003c 15 min) warnings |\n| **Session Management** | Auto-link workflow runs, probe tunnels, and cancel stale sessions |\n| **Copy / Paste** | Toolbar buttons, right-click paste, Ctrl+Shift+C/V, and middle-click in the docked terminal |\n\n---\n\n## Project Structure\n\n```\n.\n├── runnerlink                      # Launch script\n├── helper-widget/\n│   ├── runner_widget.py            # RunnerLink application\n│   ├── config.example.json         # Default configuration (tracked)\n│   └── config.json                 # Local overrides (gitignored)\n├── .github/workflows/\n│   └── runnerLink.yml              # GitHub Actions reverse-tunnel workflow\n├── Dockerfile                      # SSH gateway (Ubuntu 22.04 + OpenSSH)\n├── docker-compose.yml              # Gateway service definition\n└── test-assets/\n    └── generate-keys.sh            # SSH key + authorized_keys generator\n```\n\n---\n\n## Configuration\n\nCopy and edit the example config for local customization:\n\n```bash\ncp helper-widget/config.example.json helper-widget/config.json\n```\n\n**Workstreams** — Each runner has a name, branch, tunnel port, and max lifetime (default 6 hours). Add runners from the app UI or edit the JSON directly.\n\n**Tools** — Modular check / install / auth commands for GitHub CLI, Copilot, Azure CLI, and Azure Data Explorer. The app runs these on the runner over SSH.\n\n\u003e Never store tokens or passwords in config files. All authentication flows happen interactively in a trusted terminal session.\n\n---\n\n## Troubleshooting\n\n| Symptom | Check |\n|---------|-------|\n| Gateway SSH refused on 50556 | `docker compose ps` — is the container running? |\n| Runner can't reach gateway | Verify router port forwarding and public IP |\n| Tunnel port (2222) refuses | Is the workflow still running? Check `gh run list` |\n| Permission denied | Confirm you're using the matching private key |\n| `az login` crashes | RunnerLink auto-patches the profile for free/no-subscription accounts |\n| Terminal clips after resize | Click **Redock** or switch tabs and back |\n\nCheck gateway logs anytime:\n\n```bash\ndocker compose logs -f gateway\n```\n\n---\n\n## License\n\nMIT\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eRunnerLink\u003c/strong\u003e — Your only Actions gateway.\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fappatalks%2Frunnerlink","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fappatalks%2Frunnerlink","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fappatalks%2Frunnerlink/lists"}