wildtunnel 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. wildtunnel-0.1.0/.github/workflows/publish.yml +18 -0
  2. wildtunnel-0.1.0/.gitignore +256 -0
  3. wildtunnel-0.1.0/.pre-commit-config.yaml +12 -0
  4. wildtunnel-0.1.0/.python-version +1 -0
  5. wildtunnel-0.1.0/LICENSE +21 -0
  6. wildtunnel-0.1.0/PKG-INFO +199 -0
  7. wildtunnel-0.1.0/README.md +183 -0
  8. wildtunnel-0.1.0/pyproject.toml +64 -0
  9. wildtunnel-0.1.0/scripts/__init__.py +0 -0
  10. wildtunnel-0.1.0/scripts/new_provider.py +202 -0
  11. wildtunnel-0.1.0/src/wildtunnel/__init__.py +3 -0
  12. wildtunnel-0.1.0/src/wildtunnel/cli.py +37 -0
  13. wildtunnel-0.1.0/src/wildtunnel/frpc_cli.py +303 -0
  14. wildtunnel-0.1.0/src/wildtunnel/frpc_config.py +31 -0
  15. wildtunnel-0.1.0/src/wildtunnel/frpc_toml.py +39 -0
  16. wildtunnel-0.1.0/src/wildtunnel/paths.py +53 -0
  17. wildtunnel-0.1.0/src/wildtunnel/prompts.py +14 -0
  18. wildtunnel-0.1.0/src/wildtunnel/providers/__init__.py +0 -0
  19. wildtunnel-0.1.0/src/wildtunnel/providers/aws_ec2/__init__.py +0 -0
  20. wildtunnel-0.1.0/src/wildtunnel/providers/aws_ec2/app.py +90 -0
  21. wildtunnel-0.1.0/src/wildtunnel/providers/aws_ec2/assets/user_data.sh +41 -0
  22. wildtunnel-0.1.0/src/wildtunnel/providers/aws_ec2/cli.py +263 -0
  23. wildtunnel-0.1.0/src/wildtunnel/providers/aws_ec2/config.py +60 -0
  24. wildtunnel-0.1.0/src/wildtunnel/providers/aws_ec2/tunnel_stack.py +191 -0
  25. wildtunnel-0.1.0/src/wildtunnel/providers/aws_lightsail/__init__.py +0 -0
  26. wildtunnel-0.1.0/src/wildtunnel/providers/aws_lightsail/assets/user_data.sh +23 -0
  27. wildtunnel-0.1.0/src/wildtunnel/providers/aws_lightsail/cli.py +252 -0
  28. wildtunnel-0.1.0/src/wildtunnel/providers/aws_lightsail/config.py +268 -0
  29. wildtunnel-0.1.0/src/wildtunnel/server/assets/server-setup.sh +132 -0
  30. wildtunnel-0.1.0/src/wildtunnel/services.py +43 -0
  31. wildtunnel-0.1.0/src/wildtunnel/systemd_unit.py +19 -0
  32. wildtunnel-0.1.0/src/wildtunnel/token.py +12 -0
  33. wildtunnel-0.1.0/tests/__init__.py +0 -0
  34. wildtunnel-0.1.0/tests/providers/__init__.py +0 -0
  35. wildtunnel-0.1.0/tests/providers/aws_ec2/__init__.py +0 -0
  36. wildtunnel-0.1.0/tests/providers/aws_ec2/test_cli.py +569 -0
  37. wildtunnel-0.1.0/tests/providers/aws_ec2/test_config.py +64 -0
  38. wildtunnel-0.1.0/tests/providers/aws_ec2/test_tunnel_stack.py +272 -0
  39. wildtunnel-0.1.0/tests/providers/aws_lightsail/__init__.py +0 -0
  40. wildtunnel-0.1.0/tests/providers/aws_lightsail/test_cli.py +434 -0
  41. wildtunnel-0.1.0/tests/providers/aws_lightsail/test_config.py +151 -0
  42. wildtunnel-0.1.0/tests/scripts/__init__.py +0 -0
  43. wildtunnel-0.1.0/tests/scripts/test_new_provider.py +147 -0
  44. wildtunnel-0.1.0/tests/test_cli.py +22 -0
  45. wildtunnel-0.1.0/tests/test_frpc_cli.py +658 -0
  46. wildtunnel-0.1.0/tests/test_frpc_config.py +66 -0
  47. wildtunnel-0.1.0/tests/test_frpc_toml.py +88 -0
  48. wildtunnel-0.1.0/tests/test_paths.py +34 -0
  49. wildtunnel-0.1.0/tests/test_prompts.py +39 -0
  50. wildtunnel-0.1.0/tests/test_server_setup.py +48 -0
  51. wildtunnel-0.1.0/tests/test_services.py +60 -0
  52. wildtunnel-0.1.0/tests/test_systemd_unit.py +16 -0
  53. wildtunnel-0.1.0/tests/test_token.py +18 -0
  54. wildtunnel-0.1.0/uv.lock +1195 -0
@@ -0,0 +1,18 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ environment: pypi
12
+ permissions:
13
+ id-token: write
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: astral-sh/setup-uv@v3
17
+ - run: uv build
18
+ - run: uv publish
@@ -0,0 +1,256 @@
1
+ # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig
2
+ # Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,linux,dotenv,macos,python
3
+ # Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,linux,dotenv,macos,python
4
+
5
+ ### dotenv ###
6
+ .env
7
+
8
+ ### Linux ###
9
+ *~
10
+
11
+ # temporary files which can be created if a process still has a handle open of a deleted file
12
+ .fuse_hidden*
13
+
14
+ # KDE directory preferences
15
+ .directory
16
+
17
+ # Linux trash folder which might appear on any partition or disk
18
+ .Trash-*
19
+
20
+ # .nfs files are created when an open file is removed but is still being accessed
21
+ .nfs*
22
+
23
+ ### macOS ###
24
+ # General
25
+ .DS_Store
26
+ .AppleDouble
27
+ .LSOverride
28
+
29
+ # Icon must end with two \r
30
+ Icon
31
+
32
+ # Thumbnails
33
+ ._*
34
+
35
+ # Files that might appear in the root of a volume
36
+ .DocumentRevisions-V100
37
+ .fseventsd
38
+ .Spotlight-V100
39
+ .TemporaryItems
40
+ .Trashes
41
+ .VolumeIcon.icns
42
+ .com.apple.timemachine.donotpresent
43
+
44
+ # Directories potentially created on remote AFP share
45
+ .AppleDB
46
+ .AppleDesktop
47
+ Network Trash Folder
48
+ Temporary Items
49
+ .apdisk
50
+
51
+ ### macOS Patch ###
52
+ # iCloud generated files
53
+ *.icloud
54
+
55
+ ### Python ###
56
+ # Byte-compiled / optimized / DLL files
57
+ __pycache__/
58
+ *.py[cod]
59
+ *$py.class
60
+
61
+ # C extensions
62
+ *.so
63
+
64
+ # Distribution / packaging
65
+ .Python
66
+ build/
67
+ develop-eggs/
68
+ dist/
69
+ downloads/
70
+ eggs/
71
+ .eggs/
72
+ lib/
73
+ lib64/
74
+ parts/
75
+ sdist/
76
+ var/
77
+ wheels/
78
+ share/python-wheels/
79
+ *.egg-info/
80
+ .installed.cfg
81
+ *.egg
82
+ MANIFEST
83
+
84
+ # PyInstaller
85
+ # Usually these files are written by a python script from a template
86
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
87
+ *.manifest
88
+ *.spec
89
+
90
+ # Installer logs
91
+ pip-log.txt
92
+ pip-delete-this-directory.txt
93
+
94
+ # Unit test / coverage reports
95
+ htmlcov/
96
+ .tox/
97
+ .nox/
98
+ .coverage
99
+ .coverage.*
100
+ .cache
101
+ nosetests.xml
102
+ coverage.xml
103
+ *.cover
104
+ *.py,cover
105
+ .hypothesis/
106
+ .pytest_cache/
107
+ cover/
108
+
109
+ # Translations
110
+ *.mo
111
+ *.pot
112
+
113
+ # Django stuff:
114
+ *.log
115
+ local_settings.py
116
+ db.sqlite3
117
+ db.sqlite3-journal
118
+
119
+ # Flask stuff:
120
+ instance/
121
+ .webassets-cache
122
+
123
+ # Scrapy stuff:
124
+ .scrapy
125
+
126
+ # Sphinx documentation
127
+ docs/_build/
128
+
129
+ # PyBuilder
130
+ .pybuilder/
131
+ target/
132
+
133
+ # Jupyter Notebook
134
+ .ipynb_checkpoints
135
+
136
+ # IPython
137
+ profile_default/
138
+ ipython_config.py
139
+
140
+ # pyenv
141
+ # For a library or package, you might want to ignore these files since the code is
142
+ # intended to run in multiple environments; otherwise, check them in:
143
+ # .python-version
144
+
145
+ # pipenv
146
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
147
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
148
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
149
+ # install all needed dependencies.
150
+ #Pipfile.lock
151
+
152
+ # poetry
153
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
154
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
155
+ # commonly ignored for libraries.
156
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
157
+ #poetry.lock
158
+
159
+ # pdm
160
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
161
+ #pdm.lock
162
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
163
+ # in version control.
164
+ # https://pdm.fming.dev/#use-with-ide
165
+ .pdm.toml
166
+
167
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
168
+ __pypackages__/
169
+
170
+ # Celery stuff
171
+ celerybeat-schedule
172
+ celerybeat.pid
173
+
174
+ # SageMath parsed files
175
+ *.sage.py
176
+
177
+ # Environments
178
+ .venv
179
+ env/
180
+ venv/
181
+ ENV/
182
+ env.bak/
183
+ venv.bak/
184
+
185
+ # Spyder project settings
186
+ .spyderproject
187
+ .spyproject
188
+
189
+ # Rope project settings
190
+ .ropeproject
191
+
192
+ # mkdocs documentation
193
+ /site
194
+
195
+ # mypy
196
+ .mypy_cache/
197
+ .dmypy.json
198
+ dmypy.json
199
+
200
+ # Pyre type checker
201
+ .pyre/
202
+
203
+ # pytype static type analyzer
204
+ .pytype/
205
+
206
+ # Cython debug symbols
207
+ cython_debug/
208
+
209
+ # PyCharm
210
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
211
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
212
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
213
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
214
+ #.idea/
215
+
216
+ ### Python Patch ###
217
+ # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
218
+ poetry.toml
219
+
220
+ # ruff
221
+ .ruff_cache/
222
+
223
+ # LSP config files
224
+ pyrightconfig.json
225
+
226
+ ### VisualStudioCode ###
227
+ .vscode/*
228
+ !.vscode/settings.json
229
+ !.vscode/tasks.json
230
+ !.vscode/launch.json
231
+ !.vscode/extensions.json
232
+ !.vscode/*.code-snippets
233
+
234
+ # Local History for Visual Studio Code
235
+ .history/
236
+
237
+ # Built Visual Studio Code Extensions
238
+ *.vsix
239
+
240
+ ### VisualStudioCode Patch ###
241
+ # Ignore all local history of files
242
+ .history
243
+ .ionide
244
+
245
+ # End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,linux,dotenv,macos,python
246
+
247
+ # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
248
+
249
+ # Claude Code runtime state (not project content)
250
+ .claude/scheduled_tasks.lock
251
+
252
+ # CDK (real cache lives under $XDG_CACHE_HOME/wildtunnel/aws_ec2/,
253
+ # outside the repo; these just catch stray local `cdk` invocations from here)
254
+ cdk.out/
255
+ cdk.context.json
256
+
@@ -0,0 +1,12 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.16.8
4
+ hooks:
5
+ - id: ruff-check
6
+ args: [--output-format, github, --fix]
7
+ - id: ruff-format
8
+ - repo: https://github.com/igorshubovych/markdownlint-cli
9
+ rev: v0.49.1
10
+ hooks:
11
+ - id: markdownlint
12
+ stages: [manual] # Only run in CI
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dasbd72
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,199 @@
1
+ Metadata-Version: 2.5
2
+ Name: wildtunnel
3
+ Version: 0.1.0
4
+ Summary: Expose a local service to the internet at a wildcard subdomain, tunneled through a VPS running frps. Ships with AWS EC2 and AWS Lightsail providers.
5
+ Author-email: dasbd72 <twbd723@gmail.com>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.12
9
+ Requires-Dist: boto3>=1.34
10
+ Requires-Dist: click>=8.1
11
+ Provides-Extra: aws-ec2
12
+ Requires-Dist: aws-cdk-cli>=2.1143.0; extra == 'aws-ec2'
13
+ Requires-Dist: aws-cdk-lib>=2.160; extra == 'aws-ec2'
14
+ Requires-Dist: constructs>=10.3; extra == 'aws-ec2'
15
+ Description-Content-Type: text/markdown
16
+
17
+ # Wildtunnel
18
+
19
+ This tool makes a home-network service available at `<name>.<domain>`. It does not open an inbound port at home.
20
+
21
+ ```
22
+ Browser -> DNS (Cloudflare) -> VPS (Caddy + frps) -> tunnel -> home PC (frpc) -> your app
23
+ ```
24
+
25
+ Wildtunnel puts the tunnel server on a VPS. It supports two providers: AWS EC2 (deployed via CDK) and AWS Lightsail (deployed via direct API calls, no CDK toolchain needed). The code separates the generic server setup (frps + Caddy) from each provider's deploy logic, so adding another provider needs no rewrite.
26
+
27
+ ## Setup and usage
28
+
29
+ ### Step 1: Install the CLI
30
+
31
+ Install frpc on a different machine first: [github.com/fatedier/frp/releases](https://github.com/fatedier/frp/releases)
32
+
33
+ ```sh
34
+ # For the AWS EC2 provider:
35
+ uv tool install 'wildtunnel[aws-ec2]'
36
+
37
+ # For the AWS Lightsail provider (no extra needed, no CDK toolchain):
38
+ uv tool install wildtunnel
39
+ ```
40
+
41
+ Tab completion for subcommands, flags, and tunnel names (`frpc del <tab>`) is available for bash, zsh, and fish. Add the line for your shell to its rc file, then restart your shell:
42
+
43
+ ```sh
44
+ source <(wildtunnel completion bash) # ~/.bashrc
45
+ source <(wildtunnel completion zsh) # ~/.zshrc
46
+ wildtunnel completion fish | source # ~/.config/fish/config.fish
47
+ ```
48
+
49
+ ### Step 2: Deploy the server
50
+
51
+ Pick one provider.
52
+
53
+ #### Step 2a: Deploy to AWS EC2
54
+
55
+ Creates an EC2 host, frps, Caddy, an Elastic IP, and an IAM role, via CDK.
56
+
57
+ ```sh
58
+ wildtunnel aws-ec2 config --domain dasbd72.com # reads the region from your AWS CLI config
59
+ wildtunnel aws-ec2 deploy
60
+ ```
61
+
62
+ Options for `aws-ec2 config`:
63
+
64
+ - `--domain`: base domain for the tunnel subdomains, e.g. `dasbd72.com`.
65
+ - `--region`: AWS region for the stack. Defaults to your AWS CLI region.
66
+ - `--extra-port-range START-END`: opens a TCP+UDP port range on the EC2 host and in frps, for `frpc add --proto tcp|udp --remote-port`. Not opened unless given. Run `aws-ec2 deploy` again to apply a change.
67
+ - `--yes`: skips confirmation prompts.
68
+
69
+ Record the `ElasticIp` output. Check status anytime with `wildtunnel aws-ec2 status`.
70
+
71
+ Get the frpc client's data (server address, base domain, token, and a ready `frpc config` command):
72
+
73
+ ```sh
74
+ wildtunnel aws-ec2 info
75
+ ```
76
+
77
+ #### Step 2b: Deploy to AWS Lightsail
78
+
79
+ Creates a `nano_3_0` Lightsail instance, frps, Caddy, and a static IP, via direct Lightsail API calls. No CDK, no CloudFormation.
80
+
81
+ ```sh
82
+ wildtunnel aws-lightsail config --domain dasbd72.com # reads the region from your AWS CLI config
83
+ wildtunnel aws-lightsail deploy
84
+ ```
85
+
86
+ `aws-lightsail config` takes the same options as `aws-ec2 config`: `--domain`, `--region`, `--extra-port-range`, `--yes`. Check status anytime with `wildtunnel aws-lightsail status`.
87
+
88
+ Lightsail instances cannot have an IAM role, so wildtunnel writes the frp token directly into the instance's user data at deploy time, instead of fetching it from SSM at boot. One effect: `wildtunnel aws-lightsail info` only works on the machine that ran `deploy`, since the token is never stored remotely.
89
+
90
+ ```sh
91
+ wildtunnel aws-lightsail info
92
+ ```
93
+
94
+ Re-running `deploy` on a live instance only syncs the static IP and firewall rules (for example after changing `--extra-port-range`) — Lightsail cannot re-run cloud-init, so it never reinstalls frps/Caddy. Use `wildtunnel aws-lightsail diff` to check for firewall drift, or `destroy` + `deploy` to force a clean reinstall.
95
+
96
+ ### Step 3: Point your DNS at the server address
97
+
98
+ Get the server address: the `ElasticIp` from `aws-ec2 info`, or the static IP from `aws-lightsail info`.
99
+
100
+ At your DNS provider (for example Cloudflare), add a wildcard `A` record, `*.dasbd72.com`, pointed at that address. Keep it DNS-only — do not enable the Cloudflare proxy (grey cloud, not orange). Make sure port 80 is open: Caddy needs it to get a TLS certificate per host on demand, via Let's Encrypt HTTP-01.
101
+
102
+ ### Step 4: Configure the frpc client
103
+
104
+ #### Step 4.1: Add a service
105
+
106
+ frpc does not read the deployed server automatically. Copy the values, or the ready command, from `aws-ec2 info` / `aws-lightsail info`.
107
+
108
+ ```sh
109
+ wildtunnel frpc config \
110
+ --server-addr 1.2.3.4 \
111
+ --base-domain dasbd72.com \
112
+ --token <token>
113
+ wildtunnel frpc add openwebui 192.168.50.10:8080
114
+ # -> https://openwebui.dasbd72.com
115
+ ```
116
+
117
+ `frpc config` options `--server-addr`, `--base-domain`, and `--token` are all required.
118
+
119
+ `frpc add <name> <local-ip>:<local-port>` adds a tunnel for a local service — HTTP/HTTPS at `<name>.<base-domain>` by default. For a non-HTTP service, for example WireGuard, add `--proto tcp` or `--proto udp` with `--remote-port <port>` (must be in the range you set with `--extra-port-range`):
120
+
121
+ ```sh
122
+ frpc add wireguard 127.0.0.1:51820 --proto udp --remote-port 51820
123
+ ```
124
+
125
+ `frpc list` shows configured tunnels. `frpc del <name>` removes one.
126
+
127
+ #### Step 4.2: Run frpc on a different machine
128
+
129
+ Use the same data as step 4.1. The `render` command writes an `frpc.toml` to stdout, without saving a file or starting a service:
130
+
131
+ ```sh
132
+ wildtunnel frpc render \
133
+ --server-addr 1.2.3.4 \
134
+ --base-domain dasbd72.com \
135
+ --token <token>
136
+ ```
137
+
138
+ ## Upgrade from aws-reverse-tunnel
139
+
140
+ This project was renamed from `aws-reverse-tunnel` to `wildtunnel`. There is no automatic migration. To keep your existing configuration, move the config directory by hand:
141
+
142
+ ```sh
143
+ mv ~/.config/aws-reverse-tunnel ~/.config/wildtunnel
144
+ ```
145
+
146
+ ## Cost
147
+
148
+ ### AWS EC2
149
+
150
+ Runs one `t4g.nano` EC2 host, an 8 GiB gp3 EBS volume, and one Elastic IP. On-demand list prices below for a 730-hour month; treat as estimates, since AWS prices change.
151
+
152
+ | Resource | ap-northeast-1 (Tokyo) | us-east-1 |
153
+ | --- | --- | --- |
154
+ | t4g.nano (730 h) | ~$3.87 | ~$3.07 |
155
+ | EBS gp3, 8 GiB | ~$0.77 | ~$0.64 |
156
+ | Public IPv4 (Elastic IP) | ~$3.65 | ~$3.65 |
157
+ | SSM parameter and KMS | free | free |
158
+ | **Total (idle)** | **~$8.30** | **~$7.35** |
159
+
160
+ - AWS charges $0.005/hour per public IPv4 address since February 2024 — this applies to the Elastic IP even when idle, making it the largest cost after the host.
161
+ - The table excludes data transfer: egress scales with traffic. 100 GB/month is free, then AWS charges about $0.114/GB in Tokyo and about $0.09/GB in us-east-1.
162
+ - The SSM parameter uses the AWS-managed KMS key, which has no monthly fee.
163
+ - A one-year Compute Savings Plan or Reserved Instance lowers the host cost by about 30–40%.
164
+
165
+ ### AWS Lightsail
166
+
167
+ The `nano_3_0` bundle is a flat $5/month (check [aws.amazon.com/lightsail/pricing](https://aws.amazon.com/lightsail/pricing/) for current prices) — includes the instance, SSD storage, and a monthly data-transfer allowance. No separate EBS or per-GB egress charges like EC2, and no $0.005/hour IP surcharge (a static IP is free while attached to a running instance). Simpler pricing, at the cost of a fixed 0.5 GiB RAM (same as `t4g.nano`) and no Savings Plan or Reserved Instance discount.
168
+
169
+ ## Development
170
+
171
+ ```sh
172
+ uv sync --all-extras
173
+ uv run pytest
174
+ uv run mypy src tests
175
+ uv run pre-commit run --all-files
176
+ ```
177
+
178
+ ### Code layout
179
+
180
+ - `src/wildtunnel/`: the CLI and the generic server installer (`server/assets/server-setup.sh`), which reads its input from environment variables and makes no AWS calls — so it also runs by hand on any Linux VPS.
181
+ - `src/wildtunnel/providers/aws_ec2/`: all AWS EC2 code — the CDK stack, app entry point, SSM token helpers, and the `aws-ec2` CLI group. Includes the stack itself, so `aws-ec2 deploy` needs no clone of this repo.
182
+ - `src/wildtunnel/providers/aws_lightsail/`: all AWS Lightsail code — the `aws-lightsail` CLI group and direct boto3 calls. No CDK: Lightsail has no useful CDK L2 constructs, and the point of Lightsail is to be simpler than EC2+CloudFormation.
183
+
184
+ ### Add a new provider
185
+
186
+ `scripts/new_provider.py` is a maintainer-only scaffold, not shipped in the package and not wired into the CLI. It generates `providers/<name>/` and `tests/providers/<name>/`, mirroring `providers/aws_ec2/`'s shape (a Click group, a config dataclass, matching test stubs), then prints the remaining manual steps. Refuses to run if `providers/<name>/` already exists.
187
+
188
+ ```sh
189
+ uv run python scripts/new_provider.py hetzner
190
+ ```
191
+
192
+ ## Known limitations
193
+
194
+ - The CDK app creates the frp token, so the plaintext token ends up in the generated CloudFormation template. AWS stores it as a SecureString, but a person with read access to the stack can still see it. Acceptable for this personal, single-account setup.
195
+ - Lightsail instances cannot have an IAM role, so wildtunnel writes the frp token directly into the instance's user data instead of fetching it from SSM. A person who can call `lightsail:GetInstance` can see it there. Also, `wildtunnel aws-lightsail info` only works on the machine that ran `deploy`, since the token is never stored remotely.
196
+
197
+ ## License
198
+
199
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,183 @@
1
+ # Wildtunnel
2
+
3
+ This tool makes a home-network service available at `<name>.<domain>`. It does not open an inbound port at home.
4
+
5
+ ```
6
+ Browser -> DNS (Cloudflare) -> VPS (Caddy + frps) -> tunnel -> home PC (frpc) -> your app
7
+ ```
8
+
9
+ Wildtunnel puts the tunnel server on a VPS. It supports two providers: AWS EC2 (deployed via CDK) and AWS Lightsail (deployed via direct API calls, no CDK toolchain needed). The code separates the generic server setup (frps + Caddy) from each provider's deploy logic, so adding another provider needs no rewrite.
10
+
11
+ ## Setup and usage
12
+
13
+ ### Step 1: Install the CLI
14
+
15
+ Install frpc on a different machine first: [github.com/fatedier/frp/releases](https://github.com/fatedier/frp/releases)
16
+
17
+ ```sh
18
+ # For the AWS EC2 provider:
19
+ uv tool install 'wildtunnel[aws-ec2]'
20
+
21
+ # For the AWS Lightsail provider (no extra needed, no CDK toolchain):
22
+ uv tool install wildtunnel
23
+ ```
24
+
25
+ Tab completion for subcommands, flags, and tunnel names (`frpc del <tab>`) is available for bash, zsh, and fish. Add the line for your shell to its rc file, then restart your shell:
26
+
27
+ ```sh
28
+ source <(wildtunnel completion bash) # ~/.bashrc
29
+ source <(wildtunnel completion zsh) # ~/.zshrc
30
+ wildtunnel completion fish | source # ~/.config/fish/config.fish
31
+ ```
32
+
33
+ ### Step 2: Deploy the server
34
+
35
+ Pick one provider.
36
+
37
+ #### Step 2a: Deploy to AWS EC2
38
+
39
+ Creates an EC2 host, frps, Caddy, an Elastic IP, and an IAM role, via CDK.
40
+
41
+ ```sh
42
+ wildtunnel aws-ec2 config --domain dasbd72.com # reads the region from your AWS CLI config
43
+ wildtunnel aws-ec2 deploy
44
+ ```
45
+
46
+ Options for `aws-ec2 config`:
47
+
48
+ - `--domain`: base domain for the tunnel subdomains, e.g. `dasbd72.com`.
49
+ - `--region`: AWS region for the stack. Defaults to your AWS CLI region.
50
+ - `--extra-port-range START-END`: opens a TCP+UDP port range on the EC2 host and in frps, for `frpc add --proto tcp|udp --remote-port`. Not opened unless given. Run `aws-ec2 deploy` again to apply a change.
51
+ - `--yes`: skips confirmation prompts.
52
+
53
+ Record the `ElasticIp` output. Check status anytime with `wildtunnel aws-ec2 status`.
54
+
55
+ Get the frpc client's data (server address, base domain, token, and a ready `frpc config` command):
56
+
57
+ ```sh
58
+ wildtunnel aws-ec2 info
59
+ ```
60
+
61
+ #### Step 2b: Deploy to AWS Lightsail
62
+
63
+ Creates a `nano_3_0` Lightsail instance, frps, Caddy, and a static IP, via direct Lightsail API calls. No CDK, no CloudFormation.
64
+
65
+ ```sh
66
+ wildtunnel aws-lightsail config --domain dasbd72.com # reads the region from your AWS CLI config
67
+ wildtunnel aws-lightsail deploy
68
+ ```
69
+
70
+ `aws-lightsail config` takes the same options as `aws-ec2 config`: `--domain`, `--region`, `--extra-port-range`, `--yes`. Check status anytime with `wildtunnel aws-lightsail status`.
71
+
72
+ Lightsail instances cannot have an IAM role, so wildtunnel writes the frp token directly into the instance's user data at deploy time, instead of fetching it from SSM at boot. One effect: `wildtunnel aws-lightsail info` only works on the machine that ran `deploy`, since the token is never stored remotely.
73
+
74
+ ```sh
75
+ wildtunnel aws-lightsail info
76
+ ```
77
+
78
+ Re-running `deploy` on a live instance only syncs the static IP and firewall rules (for example after changing `--extra-port-range`) — Lightsail cannot re-run cloud-init, so it never reinstalls frps/Caddy. Use `wildtunnel aws-lightsail diff` to check for firewall drift, or `destroy` + `deploy` to force a clean reinstall.
79
+
80
+ ### Step 3: Point your DNS at the server address
81
+
82
+ Get the server address: the `ElasticIp` from `aws-ec2 info`, or the static IP from `aws-lightsail info`.
83
+
84
+ At your DNS provider (for example Cloudflare), add a wildcard `A` record, `*.dasbd72.com`, pointed at that address. Keep it DNS-only — do not enable the Cloudflare proxy (grey cloud, not orange). Make sure port 80 is open: Caddy needs it to get a TLS certificate per host on demand, via Let's Encrypt HTTP-01.
85
+
86
+ ### Step 4: Configure the frpc client
87
+
88
+ #### Step 4.1: Add a service
89
+
90
+ frpc does not read the deployed server automatically. Copy the values, or the ready command, from `aws-ec2 info` / `aws-lightsail info`.
91
+
92
+ ```sh
93
+ wildtunnel frpc config \
94
+ --server-addr 1.2.3.4 \
95
+ --base-domain dasbd72.com \
96
+ --token <token>
97
+ wildtunnel frpc add openwebui 192.168.50.10:8080
98
+ # -> https://openwebui.dasbd72.com
99
+ ```
100
+
101
+ `frpc config` options `--server-addr`, `--base-domain`, and `--token` are all required.
102
+
103
+ `frpc add <name> <local-ip>:<local-port>` adds a tunnel for a local service — HTTP/HTTPS at `<name>.<base-domain>` by default. For a non-HTTP service, for example WireGuard, add `--proto tcp` or `--proto udp` with `--remote-port <port>` (must be in the range you set with `--extra-port-range`):
104
+
105
+ ```sh
106
+ frpc add wireguard 127.0.0.1:51820 --proto udp --remote-port 51820
107
+ ```
108
+
109
+ `frpc list` shows configured tunnels. `frpc del <name>` removes one.
110
+
111
+ #### Step 4.2: Run frpc on a different machine
112
+
113
+ Use the same data as step 4.1. The `render` command writes an `frpc.toml` to stdout, without saving a file or starting a service:
114
+
115
+ ```sh
116
+ wildtunnel frpc render \
117
+ --server-addr 1.2.3.4 \
118
+ --base-domain dasbd72.com \
119
+ --token <token>
120
+ ```
121
+
122
+ ## Upgrade from aws-reverse-tunnel
123
+
124
+ This project was renamed from `aws-reverse-tunnel` to `wildtunnel`. There is no automatic migration. To keep your existing configuration, move the config directory by hand:
125
+
126
+ ```sh
127
+ mv ~/.config/aws-reverse-tunnel ~/.config/wildtunnel
128
+ ```
129
+
130
+ ## Cost
131
+
132
+ ### AWS EC2
133
+
134
+ Runs one `t4g.nano` EC2 host, an 8 GiB gp3 EBS volume, and one Elastic IP. On-demand list prices below for a 730-hour month; treat as estimates, since AWS prices change.
135
+
136
+ | Resource | ap-northeast-1 (Tokyo) | us-east-1 |
137
+ | --- | --- | --- |
138
+ | t4g.nano (730 h) | ~$3.87 | ~$3.07 |
139
+ | EBS gp3, 8 GiB | ~$0.77 | ~$0.64 |
140
+ | Public IPv4 (Elastic IP) | ~$3.65 | ~$3.65 |
141
+ | SSM parameter and KMS | free | free |
142
+ | **Total (idle)** | **~$8.30** | **~$7.35** |
143
+
144
+ - AWS charges $0.005/hour per public IPv4 address since February 2024 — this applies to the Elastic IP even when idle, making it the largest cost after the host.
145
+ - The table excludes data transfer: egress scales with traffic. 100 GB/month is free, then AWS charges about $0.114/GB in Tokyo and about $0.09/GB in us-east-1.
146
+ - The SSM parameter uses the AWS-managed KMS key, which has no monthly fee.
147
+ - A one-year Compute Savings Plan or Reserved Instance lowers the host cost by about 30–40%.
148
+
149
+ ### AWS Lightsail
150
+
151
+ The `nano_3_0` bundle is a flat $5/month (check [aws.amazon.com/lightsail/pricing](https://aws.amazon.com/lightsail/pricing/) for current prices) — includes the instance, SSD storage, and a monthly data-transfer allowance. No separate EBS or per-GB egress charges like EC2, and no $0.005/hour IP surcharge (a static IP is free while attached to a running instance). Simpler pricing, at the cost of a fixed 0.5 GiB RAM (same as `t4g.nano`) and no Savings Plan or Reserved Instance discount.
152
+
153
+ ## Development
154
+
155
+ ```sh
156
+ uv sync --all-extras
157
+ uv run pytest
158
+ uv run mypy src tests
159
+ uv run pre-commit run --all-files
160
+ ```
161
+
162
+ ### Code layout
163
+
164
+ - `src/wildtunnel/`: the CLI and the generic server installer (`server/assets/server-setup.sh`), which reads its input from environment variables and makes no AWS calls — so it also runs by hand on any Linux VPS.
165
+ - `src/wildtunnel/providers/aws_ec2/`: all AWS EC2 code — the CDK stack, app entry point, SSM token helpers, and the `aws-ec2` CLI group. Includes the stack itself, so `aws-ec2 deploy` needs no clone of this repo.
166
+ - `src/wildtunnel/providers/aws_lightsail/`: all AWS Lightsail code — the `aws-lightsail` CLI group and direct boto3 calls. No CDK: Lightsail has no useful CDK L2 constructs, and the point of Lightsail is to be simpler than EC2+CloudFormation.
167
+
168
+ ### Add a new provider
169
+
170
+ `scripts/new_provider.py` is a maintainer-only scaffold, not shipped in the package and not wired into the CLI. It generates `providers/<name>/` and `tests/providers/<name>/`, mirroring `providers/aws_ec2/`'s shape (a Click group, a config dataclass, matching test stubs), then prints the remaining manual steps. Refuses to run if `providers/<name>/` already exists.
171
+
172
+ ```sh
173
+ uv run python scripts/new_provider.py hetzner
174
+ ```
175
+
176
+ ## Known limitations
177
+
178
+ - The CDK app creates the frp token, so the plaintext token ends up in the generated CloudFormation template. AWS stores it as a SecureString, but a person with read access to the stack can still see it. Acceptable for this personal, single-account setup.
179
+ - Lightsail instances cannot have an IAM role, so wildtunnel writes the frp token directly into the instance's user data instead of fetching it from SSM. A person who can call `lightsail:GetInstance` can see it there. Also, `wildtunnel aws-lightsail info` only works on the machine that ran `deploy`, since the token is never stored remotely.
180
+
181
+ ## License
182
+
183
+ MIT. See [LICENSE](LICENSE).