@hamedb89/localghost 0.1.9 → 0.1.12
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.
- package/README.md +340 -254
- package/dist/cli.js +993 -89
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +342 -92
- package/dist/index.js +2048 -925
- package/dist/index.js.map +1 -1
- package/dist/{tunnel-DzfLXZ8O.d.ts → tunnel-BA52DD9e.d.ts} +53 -1
- package/dist/vite.d.ts +2 -1
- package/dist/vite.js +131 -17
- package/dist/vite.js.map +1 -1
- package/docs/flows.md +3 -3
- package/docs/ghost-tunnel.md +248 -18
- package/docs/localghost.1.md +43 -7
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -9,46 +9,20 @@ Buh. Friendly local hostnames for app repos.
|
|
|
9
9
|
[](https://github.com/hamedb89/localghost/actions/workflows/ci.yml)
|
|
10
10
|
[](https://github.com/hamedb89/localghost/actions/workflows/pages.yml)
|
|
11
11
|
[](https://github.com/hamedb89/localghost/actions/workflows/publish-npm.yml)
|
|
12
|
-
[](https://www.npmjs.com/package/@hamedb89/localghost)
|
|
13
13
|
|
|
14
|
-
Localghost is a tiny Node.js CLI for
|
|
14
|
+
Localghost is a tiny Node.js CLI for clean local app domains. Add it as a dev dependency, keep running the command your team already knows, and use `http://app.localhost/` instead of remembering which port belongs to which process.
|
|
15
15
|
|
|
16
|
-
[Website](https://hamedb89.github.io/localghost/) · [npm](https://www.npmjs.com/package/@hamedb89/localghost) · [GitHub](https://github.com/hamedb89/localghost)
|
|
16
|
+
[Website](https://hamedb89.github.io/localghost/) · [Docs](https://hamedb89.github.io/localghost/docs/) · [npm](https://www.npmjs.com/package/@hamedb89/localghost) · [GitHub](https://github.com/hamedb89/localghost)
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## Quick Start
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
- Lets repos choose explicit config files or filename patterns when `.localghost` is not enough.
|
|
22
|
-
- Updates only a managed Localghost block in `/etc/hosts` during explicit setup.
|
|
23
|
-
- Generates `ops/local/Caddyfile` for local reverse proxying. HTTP is the default; HTTPS is explicit with `--https` or `--ssl`.
|
|
24
|
-
- Checks whether Caddy is installed, but does not run Homebrew for you.
|
|
25
|
-
- Provides a Vite plugin that sets explicit `server.allowedHosts` entries.
|
|
26
|
-
- Defaults Vite dev to the configured Localghost domain and no-ops during production/build.
|
|
27
|
-
- Prints parsed config and project-local state as JSON for scripts, Codex, agents, and future MCP tools.
|
|
28
|
-
- Checks npm for newer Localghost releases at most once per day, with an explicit opt-out.
|
|
29
|
-
|
|
30
|
-
## Trust
|
|
31
|
-
|
|
32
|
-
- CI runs typecheck, build, site build, and npm package dry-run on Node 20 and 22.
|
|
33
|
-
- GitHub Pages is deployed by Actions from the checked-in `site/` and `assets/` sources.
|
|
34
|
-
- Preview the exact Pages artifact locally with `npm run site:serve`, then open `http://127.0.0.1:4173/`.
|
|
35
|
-
- npm publish is guarded by `prepublishOnly` and the release workflow publishes with npm provenance.
|
|
36
|
-
- Runtime dependencies are intentionally small: `commander` for the CLI and `execa` for process execution. Vite is an optional peer dependency for the Vite plugin.
|
|
37
|
-
- No postinstall scripts, hidden Homebrew installs, or broad hosts-file rewrites.
|
|
38
|
-
- Update checks are best-effort, cached for 24 hours, and can be disabled with `LOCALGHOST_NO_UPDATE_CHECK=1` or `--no-update-check`.
|
|
39
|
-
|
|
40
|
-
<p align="center">
|
|
41
|
-
<img src="./assets/localghost-app-icon.png" alt="Localghost app icon" width="180">
|
|
42
|
-
</p>
|
|
43
|
-
|
|
44
|
-
## Start Here
|
|
20
|
+
Install it as a dev dependency:
|
|
45
21
|
|
|
46
22
|
```sh
|
|
47
23
|
yarn add -D @hamedb89/localghost
|
|
48
24
|
```
|
|
49
25
|
|
|
50
|
-
That is the entrypoint you are optimizing for: install the dev dependency, keep using the dev command your team already knows, and let Localghost handle the local-domain setup around it.
|
|
51
|
-
|
|
52
26
|
For Vite apps, add the plugin once:
|
|
53
27
|
|
|
54
28
|
```ts
|
|
@@ -60,15 +34,13 @@ export default defineConfig({
|
|
|
60
34
|
});
|
|
61
35
|
```
|
|
62
36
|
|
|
63
|
-
|
|
37
|
+
Then keep using the command your repo already expects:
|
|
64
38
|
|
|
65
39
|
```sh
|
|
66
40
|
yarn dev
|
|
67
41
|
```
|
|
68
42
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
On the first interactive `yarn dev`, Localghost can create `.localghost`, explain the `/etc/hosts` change, write the local Caddyfile, and then print the browser-facing URL:
|
|
43
|
+
On the first interactive run, Localghost can create `.localghost`, explain the `/etc/hosts` change, write `ops/local/Caddyfile`, and print the browser-facing URL:
|
|
72
44
|
|
|
73
45
|
```txt
|
|
74
46
|
localghost
|
|
@@ -77,7 +49,7 @@ also: http://www.app.localhost/
|
|
|
77
49
|
target: http://127.0.0.1:5173/
|
|
78
50
|
```
|
|
79
51
|
|
|
80
|
-
|
|
52
|
+
For non-Vite apps, wrap your raw dev command:
|
|
81
53
|
|
|
82
54
|
```json
|
|
83
55
|
{
|
|
@@ -88,109 +60,139 @@ If your project does not use Vite, or you want one command that starts Caddy and
|
|
|
88
60
|
}
|
|
89
61
|
```
|
|
90
62
|
|
|
91
|
-
|
|
63
|
+
## The Simple Stuff
|
|
64
|
+
|
|
65
|
+
Start the detected development server with Localghost:
|
|
92
66
|
|
|
93
67
|
```sh
|
|
94
|
-
|
|
68
|
+
npm exec localghost
|
|
95
69
|
```
|
|
96
70
|
|
|
97
|
-
|
|
71
|
+
Localghost detects the package manager, prefers a non-recursive `dev:raw` script, falls back to `dev`, repairs stale setup when needed, then starts Caddy and the server. Preview the decision without starting anything:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
npm exec localghost -- --dry-run
|
|
75
|
+
```
|
|
98
76
|
|
|
99
|
-
|
|
77
|
+
Create the repo-local hostname contract:
|
|
100
78
|
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
# Format: <host> <port>
|
|
104
|
-
app.localhost 5173
|
|
105
|
-
www.app.localhost 5173
|
|
106
|
-
api.app.localhost 8787
|
|
79
|
+
```sh
|
|
80
|
+
localghost init --write-scripts
|
|
107
81
|
```
|
|
108
82
|
|
|
109
|
-
|
|
83
|
+
Check whether the machine is ready:
|
|
110
84
|
|
|
111
85
|
```sh
|
|
112
|
-
|
|
86
|
+
localghost doctor
|
|
113
87
|
```
|
|
114
88
|
|
|
115
|
-
|
|
89
|
+
Prepare `/etc/hosts` and the local Caddyfile:
|
|
116
90
|
|
|
117
91
|
```sh
|
|
118
|
-
|
|
92
|
+
localghost setup
|
|
119
93
|
```
|
|
120
94
|
|
|
121
|
-
|
|
95
|
+
Check setup readiness:
|
|
122
96
|
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
Run: brew install caddy
|
|
126
|
-
Localghost will not install it for you. No surprise spells.
|
|
97
|
+
```sh
|
|
98
|
+
localghost status --ready
|
|
127
99
|
```
|
|
128
100
|
|
|
129
|
-
|
|
101
|
+
Repair stale hosts, Caddy configuration, or setup state:
|
|
130
102
|
|
|
131
103
|
```sh
|
|
132
|
-
|
|
104
|
+
localghost repair
|
|
133
105
|
```
|
|
134
106
|
|
|
135
|
-
|
|
107
|
+
Run only the local proxy:
|
|
136
108
|
|
|
137
109
|
```sh
|
|
138
|
-
|
|
110
|
+
localghost dev
|
|
139
111
|
```
|
|
140
112
|
|
|
141
|
-
|
|
113
|
+
Wrap an app server:
|
|
142
114
|
|
|
143
115
|
```sh
|
|
144
|
-
|
|
116
|
+
localghost run -- vite
|
|
145
117
|
```
|
|
146
118
|
|
|
147
|
-
|
|
119
|
+
See the domain layer:
|
|
148
120
|
|
|
149
121
|
```sh
|
|
150
|
-
|
|
122
|
+
localghost routes
|
|
151
123
|
```
|
|
152
124
|
|
|
153
|
-
|
|
125
|
+
```txt
|
|
126
|
+
localghost routes
|
|
127
|
+
http://app.localhost/ -> http://127.0.0.1:5173
|
|
128
|
+
http://api.app.localhost/ -> http://127.0.0.1:8787
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
See active Localghost sessions:
|
|
154
132
|
|
|
155
133
|
```sh
|
|
156
|
-
|
|
134
|
+
localghost ps
|
|
135
|
+
localghost ps --json
|
|
157
136
|
```
|
|
158
137
|
|
|
159
|
-
|
|
138
|
+
Check for updates:
|
|
160
139
|
|
|
161
140
|
```sh
|
|
162
|
-
|
|
163
|
-
yarn localghost setup
|
|
141
|
+
localghost update
|
|
164
142
|
```
|
|
165
143
|
|
|
166
144
|
Prefer `.localhost` names. `.local` is supported, but Localghost warns because `.local` can collide with mDNS/Bonjour.
|
|
167
145
|
|
|
168
|
-
##
|
|
146
|
+
## What It Changes
|
|
169
147
|
|
|
170
|
-
|
|
148
|
+
Localghost is intentionally small and explicit:
|
|
171
149
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
150
|
+
- Reads `.localghost` from your app repo and turns hostnames into local routes.
|
|
151
|
+
- Updates only a managed Localghost block in `/etc/hosts` during explicit setup.
|
|
152
|
+
- Generates `ops/local/Caddyfile` for local reverse proxying.
|
|
153
|
+
- Records setup state in `ops/local/localghost-state.json`.
|
|
154
|
+
- Keeps HTTP as the default; HTTPS is explicit with `--https`, `--ssl`, or config.
|
|
155
|
+
- Checks whether Caddy is installed, but never installs Homebrew packages for you.
|
|
156
|
+
- Gives Vite explicit `server.allowedHosts` entries without using `allowedHosts: true`.
|
|
157
|
+
- Never opens browser tabs by default.
|
|
158
|
+
|
|
159
|
+
`setup`, `dev`, and `teardown` refuse to run in production-like environments such as `NODE_ENV=production`, `VERCEL_ENV=production`, or `LOCALGHOST_ENV=production`.
|
|
160
|
+
|
|
161
|
+
## Configuration By Use Case
|
|
162
|
+
|
|
163
|
+
### One App Domain
|
|
164
|
+
|
|
165
|
+
Use a `.localghost` file when you want one stable local domain:
|
|
166
|
+
|
|
167
|
+
```txt
|
|
168
|
+
# .localghost
|
|
169
|
+
app.localhost 5173
|
|
175
170
|
```
|
|
176
171
|
|
|
177
|
-
|
|
172
|
+
Then run:
|
|
178
173
|
|
|
179
174
|
```sh
|
|
180
|
-
localghost
|
|
175
|
+
localghost setup
|
|
176
|
+
localghost run -- vite
|
|
181
177
|
```
|
|
182
178
|
|
|
183
|
-
|
|
179
|
+
### Multiple Local Services
|
|
184
180
|
|
|
185
|
-
|
|
186
|
-
|
|
181
|
+
Map each browser-facing host to the upstream port:
|
|
182
|
+
|
|
183
|
+
```txt
|
|
184
|
+
# .localghost
|
|
185
|
+
app.localhost 5173
|
|
186
|
+
www.app.localhost 5173
|
|
187
|
+
api.app.localhost 8787
|
|
188
|
+
admin.app.localhost 5174
|
|
187
189
|
```
|
|
188
190
|
|
|
189
|
-
|
|
191
|
+
`localghost routes` prints the same `domain -> upstream` map that `setup` and `dev` use.
|
|
190
192
|
|
|
191
|
-
|
|
193
|
+
### Add Package Scripts
|
|
192
194
|
|
|
193
|
-
`localghost init --write-scripts` adds
|
|
195
|
+
`localghost init --write-scripts` adds missing scripts without replacing your existing ones:
|
|
194
196
|
|
|
195
197
|
```json
|
|
196
198
|
{
|
|
@@ -200,6 +202,7 @@ The Vite plugin accepts the same shape through `fileName`, `configFiles`, or `co
|
|
|
200
202
|
"localghost:proxy:https": "localghost dev --https",
|
|
201
203
|
"localghost:run": "localghost run --",
|
|
202
204
|
"localghost:ready": "localghost status --ready",
|
|
205
|
+
"localghost:repair": "localghost repair",
|
|
203
206
|
"localghost:trust": "localghost trust",
|
|
204
207
|
"localghost:ps": "localghost ps",
|
|
205
208
|
"localghost:print": "localghost print",
|
|
@@ -215,7 +218,9 @@ The Vite plugin accepts the same shape through `fileName`, `configFiles`, or `co
|
|
|
215
218
|
}
|
|
216
219
|
```
|
|
217
220
|
|
|
218
|
-
|
|
221
|
+
### Keep `yarn dev` As The Daily Command
|
|
222
|
+
|
|
223
|
+
Wrap the raw app server so teammates keep typing the normal command:
|
|
219
224
|
|
|
220
225
|
```json
|
|
221
226
|
{
|
|
@@ -226,7 +231,7 @@ A full app might compose them with its own servers:
|
|
|
226
231
|
}
|
|
227
232
|
```
|
|
228
233
|
|
|
229
|
-
|
|
234
|
+
For Turborepo, wrap the dev runner and keep dev uncached:
|
|
230
235
|
|
|
231
236
|
```json
|
|
232
237
|
{
|
|
@@ -237,8 +242,6 @@ In Turborepo, let Localghost wrap the dev runner and keep dev uncached:
|
|
|
237
242
|
}
|
|
238
243
|
```
|
|
239
244
|
|
|
240
|
-
Then keep persistent dev tasks uncached:
|
|
241
|
-
|
|
242
245
|
```json
|
|
243
246
|
{
|
|
244
247
|
"tasks": {
|
|
@@ -247,263 +250,350 @@ Then keep persistent dev tasks uncached:
|
|
|
247
250
|
}
|
|
248
251
|
```
|
|
249
252
|
|
|
250
|
-
`localghost run`
|
|
253
|
+
`localghost run` starts Caddy, handles optional HTTPS trust, starts the child command, passes `LOCALGHOST_PORT` and `VITE_PORT`, and stops Caddy when the child exits.
|
|
251
254
|
|
|
252
|
-
|
|
255
|
+
### Vite Plugin
|
|
253
256
|
|
|
254
|
-
|
|
255
|
-
import { defineLocalghostConfig } from "@hamedb89/localghost";
|
|
257
|
+
Use the plugin when you want Vite to bind to `127.0.0.1`, use the selected Localghost port, set strict `allowedHosts`, and print the browser-facing domain:
|
|
256
258
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
+
```ts
|
|
260
|
+
import { defineConfig } from "vite";
|
|
261
|
+
import { localGhostPlugin } from "@hamedb89/localghost/vite";
|
|
262
|
+
|
|
263
|
+
export default defineConfig({
|
|
264
|
+
plugins: [
|
|
265
|
+
localGhostPlugin({
|
|
266
|
+
port: 5173,
|
|
267
|
+
configFiles: [".localghost.private", ".localghost"]
|
|
268
|
+
})
|
|
269
|
+
]
|
|
259
270
|
});
|
|
260
271
|
```
|
|
261
272
|
|
|
262
|
-
|
|
273
|
+
The plugin runs only during local `vite serve`; production/build mode does not configure Vite dev-server hosting. If `ghostTunnel` is configured, the build hook can still print the production URL shape for visibility.
|
|
263
274
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
275
|
+
### Custom Config Files
|
|
276
|
+
|
|
277
|
+
By default, Localghost reads `.localghost`. Repos that need another file name can be explicit:
|
|
278
|
+
|
|
279
|
+
```sh
|
|
280
|
+
localghost print --config .localghost.preview
|
|
281
|
+
localghost setup --config .localghost.preview
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
You can pass `--config` more than once. Localghost uses the first file that exists:
|
|
285
|
+
|
|
286
|
+
```sh
|
|
287
|
+
localghost print --config .localghost.private --config .localghost
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
You can also search project-root filenames with a regular expression:
|
|
291
|
+
|
|
292
|
+
```sh
|
|
293
|
+
localghost print --config-pattern '^\.localghost\.(private|preview)$'
|
|
271
294
|
```
|
|
272
295
|
|
|
273
|
-
|
|
296
|
+
The Vite plugin accepts the same idea through `fileName`, `configFiles`, or `configPattern`.
|
|
297
|
+
|
|
298
|
+
### Shared Project Defaults
|
|
274
299
|
|
|
275
|
-
|
|
300
|
+
Most repos do not need `localghost.config.mjs`. Add it when you want shared defaults for CLI and Vite:
|
|
276
301
|
|
|
277
302
|
```js
|
|
278
303
|
import { defineLocalghostConfig } from "@hamedb89/localghost";
|
|
279
304
|
|
|
280
305
|
export default defineLocalghostConfig({
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
306
|
+
project: "app",
|
|
307
|
+
port: 5173,
|
|
308
|
+
dynamicPort: true,
|
|
309
|
+
autoRepair: true,
|
|
310
|
+
command: ["pnpm", "dev"],
|
|
311
|
+
wwwAlias: true
|
|
285
312
|
});
|
|
286
313
|
```
|
|
287
314
|
|
|
288
|
-
|
|
315
|
+
Localghost derives `project` from `package.json`, defaults to port `5173`, keeps HTTP as the default, enables dynamic ports and setup repair by default, and adds `www.` aliases by default. `run`, `dev`, and the Vite plugin perform a read-only readiness check first and repair only when the managed hosts block or setup state is stale.
|
|
289
316
|
|
|
290
|
-
|
|
291
|
-
localghost ghost tunnel
|
|
292
|
-
mode: manual
|
|
293
|
-
expected: https://app-decision-layer-hamed.ghost.socialworkouts.app/
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
Without `domains`, the expected URL stays wildcarded:
|
|
317
|
+
With no subcommand, `command` takes precedence. Otherwise Localghost detects npm, pnpm, Yarn, or Bun and runs `dev:raw` or `dev`. Scripts that invoke Localghost are skipped to prevent recursion.
|
|
297
318
|
|
|
298
|
-
|
|
299
|
-
localghost ghost tunnel
|
|
300
|
-
mode: manual
|
|
301
|
-
expected: https://app-decision-layer-hamed.ghost.*/
|
|
302
|
-
```
|
|
319
|
+
### Multiple Apps
|
|
303
320
|
|
|
304
|
-
|
|
321
|
+
For a monorepo where one root command already starts every app, keep using `command` and list the routes in `.localghost`.
|
|
305
322
|
|
|
306
|
-
|
|
323
|
+
When Localghost should own each process, configure explicit services:
|
|
307
324
|
|
|
308
325
|
```js
|
|
309
326
|
export default defineLocalghostConfig({
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
327
|
+
services: [
|
|
328
|
+
{
|
|
329
|
+
name: "web",
|
|
330
|
+
cwd: "apps/web",
|
|
331
|
+
host: "xyz.localhost",
|
|
332
|
+
port: 5173,
|
|
333
|
+
command: ["pnpm", "dev"]
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
name: "api",
|
|
337
|
+
cwd: "apps/api",
|
|
338
|
+
host: "api.xyz.localhost",
|
|
339
|
+
port: 8787,
|
|
340
|
+
command: ["pnpm", "dev"]
|
|
316
341
|
}
|
|
317
|
-
|
|
342
|
+
]
|
|
318
343
|
});
|
|
319
344
|
```
|
|
320
345
|
|
|
321
|
-
|
|
346
|
+
Then bare `localghost` starts one Caddy instance and both services. Each command runs in its own `cwd` and receives its own `LOCALGHOST_PORT`, `VITE_PORT`, and `LOCALGHOST_SERVICE`. Dynamic-port selection and setup repair apply to every service. When Caddy or any service exits, Localghost stops the remaining processes.
|
|
322
347
|
|
|
323
|
-
|
|
324
|
-
import {
|
|
325
|
-
assertSecureGhostTunnelRequest,
|
|
326
|
-
constructGhostTunnelUrl,
|
|
327
|
-
readLocalghostProjectConfig
|
|
328
|
-
} from "@hamedb89/localghost";
|
|
348
|
+
Omit a service `command` to detect `dev:raw` or `dev` from that service directory. Service directories must stay inside the project root, and names and hosts must be unique.
|
|
329
349
|
|
|
330
|
-
|
|
331
|
-
const url = constructGhostTunnelUrl({
|
|
332
|
-
domain: "socialworkouts.app",
|
|
333
|
-
route: "plan",
|
|
334
|
-
project: "summer-base",
|
|
335
|
-
owner: "hamed",
|
|
336
|
-
ghostTunnel: config.ghostTunnel
|
|
337
|
-
});
|
|
350
|
+
Caddy startup and validation logs are quiet after success; configuration errors remain visible. Set `LOCALGHOST_CADDY_VERBOSE=1` when debugging Caddy itself. Once all service ports are listening, Localghost prints the final hostname-to-upstream map beneath the service startup logs.
|
|
338
351
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
});
|
|
352
|
+
Try the runnable Node example in [`examples/multi-service`](./examples/multi-service).
|
|
353
|
+
|
|
354
|
+
Disable automatic repair when you want strict failure behavior:
|
|
355
|
+
|
|
356
|
+
```sh
|
|
357
|
+
localghost run --auto-repair=no -- vite
|
|
346
358
|
```
|
|
347
359
|
|
|
348
|
-
|
|
360
|
+
Or set `autoRepair: false` in `localghost.config.mjs`. HTTPS certificate trust remains explicit.
|
|
349
361
|
|
|
350
|
-
|
|
362
|
+
### Fixed Ports
|
|
351
363
|
|
|
352
|
-
|
|
364
|
+
Dynamic ports are on by default. Localghost starts at the configured port, checks `127.0.0.1:<port>`, and walks upward until it finds a free port.
|
|
353
365
|
|
|
354
|
-
|
|
366
|
+
Use strict fixed-port behavior when you want startup to fail instead:
|
|
355
367
|
|
|
356
368
|
```sh
|
|
357
|
-
|
|
358
|
-
npm run test:cli
|
|
359
|
-
npm run test:coverage
|
|
369
|
+
localghost run --dynamic-port=no -- vite
|
|
360
370
|
```
|
|
361
371
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
```txt
|
|
365
|
-
localghost ps
|
|
372
|
+
Or in config:
|
|
366
373
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
app.localhost -> 127.0.0.1:5173 (listening)
|
|
374
|
+
```js
|
|
375
|
+
export default defineLocalghostConfig({
|
|
376
|
+
dynamicPort: false
|
|
377
|
+
});
|
|
372
378
|
```
|
|
373
379
|
|
|
374
|
-
|
|
380
|
+
### Local HTTPS
|
|
375
381
|
|
|
376
|
-
|
|
382
|
+
HTTP is the default. Use HTTPS only when you explicitly want Caddy local certificates:
|
|
377
383
|
|
|
378
|
-
|
|
384
|
+
```sh
|
|
385
|
+
localghost setup --https
|
|
386
|
+
localghost dev --https
|
|
387
|
+
```
|
|
379
388
|
|
|
380
|
-
|
|
389
|
+
Trust Caddy's local HTTPS CA when you want browsers to stop showing local certificate warnings:
|
|
381
390
|
|
|
382
391
|
```sh
|
|
383
|
-
|
|
384
|
-
|
|
392
|
+
localghost trust
|
|
393
|
+
localghost run --trust -- vite
|
|
385
394
|
```
|
|
386
395
|
|
|
387
|
-
|
|
396
|
+
macOS may ask for your password so Caddy can add its local CA to Keychain. Localghost records the trust result in `ops/local/localghost-state.json`.
|
|
388
397
|
|
|
389
|
-
|
|
398
|
+
You can also make HTTPS the repo default:
|
|
390
399
|
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
|
|
400
|
+
```js
|
|
401
|
+
export default defineLocalghostConfig({
|
|
402
|
+
https: true
|
|
403
|
+
});
|
|
404
|
+
```
|
|
394
405
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
406
|
+
### Disable `www.` Aliases
|
|
407
|
+
|
|
408
|
+
By default, `app.localhost 5173` also creates `www.app.localhost`.
|
|
409
|
+
|
|
410
|
+
Disable that when the repo wants only exact hosts:
|
|
411
|
+
|
|
412
|
+
```js
|
|
413
|
+
export default defineLocalghostConfig({
|
|
414
|
+
wwwAlias: false
|
|
402
415
|
});
|
|
403
416
|
```
|
|
404
417
|
|
|
405
|
-
|
|
418
|
+
### Public Ghost Tunnel
|
|
419
|
+
|
|
420
|
+
`ghostTunnel` is an opt-in production URL shape for deployed wildcard endpoints. It does not change local Caddy or `/etc/hosts` setup.
|
|
421
|
+
|
|
422
|
+
Use public mode when the deployed app should react to whatever route arrives:
|
|
406
423
|
|
|
407
|
-
|
|
424
|
+
```js
|
|
425
|
+
import { defineLocalghostConfig } from "@hamedb89/localghost";
|
|
426
|
+
|
|
427
|
+
export default defineLocalghostConfig({
|
|
428
|
+
ghostTunnel: {
|
|
429
|
+
mode: "public",
|
|
430
|
+
domains: "copper-comet.example"
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
```
|
|
408
434
|
|
|
409
|
-
|
|
435
|
+
Build output stays flexible instead of filling slots from the build machine:
|
|
410
436
|
|
|
411
437
|
```txt
|
|
412
|
-
localghost
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
target: http://127.0.0.1:5173/
|
|
438
|
+
localghost ghost tunnel
|
|
439
|
+
mode: public
|
|
440
|
+
configured: https://<route>-<project>-<owner>.ghost.copper-comet.example/
|
|
416
441
|
```
|
|
417
442
|
|
|
418
|
-
|
|
443
|
+
Production code can parse and validate the incoming wildcard host:
|
|
419
444
|
|
|
420
|
-
|
|
445
|
+
```ts
|
|
446
|
+
import {
|
|
447
|
+
assertSecureGhostTunnelRequest,
|
|
448
|
+
readLocalghostProjectConfig
|
|
449
|
+
} from "@hamedb89/localghost";
|
|
421
450
|
|
|
422
|
-
|
|
451
|
+
const { config } = await readLocalghostProjectConfig();
|
|
423
452
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
localghost setup --https
|
|
432
|
-
localghost trust
|
|
433
|
-
localghost status
|
|
434
|
-
localghost status --ready
|
|
435
|
-
localghost ps
|
|
436
|
-
localghost ps --json
|
|
437
|
-
localghost reset
|
|
438
|
-
localghost teardown
|
|
439
|
-
localghost teardown --remove-caddyfile
|
|
440
|
-
localghost update
|
|
441
|
-
localghost --no-update-check doctor
|
|
442
|
-
localghost run -- vite
|
|
443
|
-
localghost run --trust -- vite
|
|
444
|
-
localghost run --dynamic-port=no -- vite
|
|
445
|
-
localghost dev --config-pattern '^\.localghost\.'
|
|
446
|
-
localghost dev --https
|
|
447
|
-
localghost print
|
|
453
|
+
const route = assertSecureGhostTunnelRequest({
|
|
454
|
+
host: request.headers.get("host") ?? "",
|
|
455
|
+
domain: "copper-comet.example",
|
|
456
|
+
protocol: "https",
|
|
457
|
+
authenticated: Boolean(session),
|
|
458
|
+
ghostTunnel: config.ghostTunnel
|
|
459
|
+
});
|
|
448
460
|
```
|
|
449
461
|
|
|
450
|
-
|
|
462
|
+
By default, secure requests require HTTPS and app-authenticated access.
|
|
451
463
|
|
|
452
|
-
|
|
464
|
+
### Concrete Ghost Tunnel Preview
|
|
453
465
|
|
|
454
|
-
|
|
466
|
+
Use `preview` only when you want one concrete URL in logs or menus:
|
|
467
|
+
|
|
468
|
+
```js
|
|
469
|
+
export default defineLocalghostConfig({
|
|
470
|
+
ghostTunnel: {
|
|
471
|
+
mode: "public",
|
|
472
|
+
domains: "copper-comet.example",
|
|
473
|
+
preview: {
|
|
474
|
+
route: "decisionlayer",
|
|
475
|
+
project: "decision-layer",
|
|
476
|
+
owner: "hamedbahrami"
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
```
|
|
455
481
|
|
|
456
|
-
|
|
482
|
+
That prints:
|
|
457
483
|
|
|
458
484
|
```txt
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
485
|
+
localghost ghost tunnel
|
|
486
|
+
mode: public
|
|
487
|
+
configured: https://decisionlayer-decision-layer-hamedbahrami.ghost.copper-comet.example/
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### Manual Ghost Tunnel
|
|
491
|
+
|
|
492
|
+
Manual mode is the default. It is useful for private or operator-mediated sharing flows:
|
|
493
|
+
|
|
494
|
+
```js
|
|
495
|
+
export default defineLocalghostConfig({
|
|
496
|
+
ghostTunnel: {
|
|
497
|
+
mode: "manual",
|
|
498
|
+
domains: "moonlit-otter.example"
|
|
499
|
+
}
|
|
500
|
+
});
|
|
462
501
|
```
|
|
463
502
|
|
|
464
|
-
|
|
503
|
+
Manual relay helpers are private by default: registration requires a local-agent bearer token plus an exact signed route claim, targets must be explicit local host/port objects, dangerous ports are blocked, sensitive headers/logs are redacted, and offline agents get a safe 503 page.
|
|
465
504
|
|
|
466
|
-
|
|
505
|
+
### Script And Agent Output
|
|
467
506
|
|
|
468
|
-
|
|
507
|
+
Use JSON when scripts, menu bar tools, or agents need stable output:
|
|
469
508
|
|
|
470
509
|
```sh
|
|
471
|
-
localghost
|
|
472
|
-
localghost status --ready
|
|
510
|
+
localghost print
|
|
473
511
|
localghost status --json
|
|
512
|
+
localghost ps --json
|
|
513
|
+
localghost update --json
|
|
474
514
|
```
|
|
475
515
|
|
|
476
|
-
`
|
|
516
|
+
`print` parses the selected config file. `status` reports the project-local setup state. `ps` reports active sessions across projects and whether each upstream port is listening.
|
|
477
517
|
|
|
478
|
-
|
|
518
|
+
### Reset Or Remove Localghost
|
|
519
|
+
|
|
520
|
+
Reconcile the managed hosts block, regenerate and validate the Caddyfile, and refresh setup state:
|
|
479
521
|
|
|
480
522
|
```sh
|
|
481
|
-
localghost
|
|
523
|
+
localghost repair
|
|
482
524
|
```
|
|
483
525
|
|
|
484
|
-
|
|
526
|
+
For HTTPS certificate trust problems, explicitly re-run Caddy's trust step:
|
|
527
|
+
|
|
528
|
+
```sh
|
|
529
|
+
localghost repair --https --trust
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
If a running Caddy process exits, `localghost run` exits with it; starting the normal development command again launches a fresh Caddy process.
|
|
533
|
+
|
|
534
|
+
Retest setup without deleting `.localghost`:
|
|
485
535
|
|
|
486
536
|
```sh
|
|
487
537
|
localghost reset
|
|
488
538
|
localghost setup
|
|
489
539
|
```
|
|
490
540
|
|
|
491
|
-
|
|
541
|
+
Remove only the managed hosts block for this project:
|
|
542
|
+
|
|
543
|
+
```sh
|
|
544
|
+
localghost teardown
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
Remove the generated Caddyfile too:
|
|
492
548
|
|
|
493
549
|
```sh
|
|
494
550
|
localghost teardown --remove-caddyfile
|
|
495
551
|
```
|
|
496
552
|
|
|
497
|
-
|
|
553
|
+
### macOS Widget
|
|
554
|
+
|
|
555
|
+
Localghost includes a tiny native macOS widget under `apps/macos-widget`. It reads the shared activity file and shows known setup/running instances.
|
|
556
|
+
|
|
557
|
+
Build it from source:
|
|
558
|
+
|
|
559
|
+
```sh
|
|
560
|
+
npm run build
|
|
561
|
+
npm run macos:widget:build
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
The app bundle is written to `dist/LocalghostWidget.app`.
|
|
565
|
+
|
|
566
|
+
## CLI Reference
|
|
567
|
+
|
|
568
|
+
```sh
|
|
569
|
+
localghost [--cwd path] [--dry-run]
|
|
570
|
+
localghost init [--write-scripts] [--config file] [--host host] [--port port]
|
|
571
|
+
localghost doctor
|
|
572
|
+
localghost setup [--project name] [--config file] [--config-pattern regex] [--https|--ssl]
|
|
573
|
+
localghost repair [--project name] [--config file] [--config-pattern regex] [--https|--ssl] [--trust]
|
|
574
|
+
localghost trust [--project name] [--config file] [--config-pattern regex] [--https|--ssl]
|
|
575
|
+
localghost reset [--project name]
|
|
576
|
+
localghost teardown [--project name] [--remove-caddyfile]
|
|
577
|
+
localghost status [--ready] [--json]
|
|
578
|
+
localghost ps [--json]
|
|
579
|
+
localghost update [--json]
|
|
580
|
+
localghost dev [--config file] [--config-pattern regex] [--https|--ssl] [--auto-repair yes|no] [--trust]
|
|
581
|
+
localghost run [--config file] [--config-pattern regex] [--https|--ssl] [--auto-repair yes|no] [--trust] [--dynamic-port] -- command
|
|
582
|
+
localghost routes [--https|--ssl]
|
|
583
|
+
localghost print [--config file] [--config-pattern regex]
|
|
584
|
+
```
|
|
498
585
|
|
|
499
586
|
## API
|
|
500
587
|
|
|
501
588
|
```ts
|
|
502
589
|
import {
|
|
590
|
+
assertSecureGhostTunnelRequest,
|
|
591
|
+
constructGhostTunnelUrl,
|
|
503
592
|
getConfigFileCandidates,
|
|
504
593
|
initLocalghost,
|
|
505
594
|
readDevHosts,
|
|
506
595
|
readLocalghostState,
|
|
596
|
+
readLocalghostProjectConfig,
|
|
507
597
|
removeSystemHosts,
|
|
508
598
|
renderCaddyfile,
|
|
509
599
|
renderHostsBlock,
|
|
@@ -511,34 +601,30 @@ import {
|
|
|
511
601
|
updateSystemHosts
|
|
512
602
|
} from "@hamedb89/localghost";
|
|
513
603
|
|
|
514
|
-
|
|
515
|
-
readDevHosts({ configPattern: /^\.localghost\.(private|preview)$/ });
|
|
604
|
+
import { localGhostPlugin } from "@hamedb89/localghost/vite";
|
|
516
605
|
```
|
|
517
606
|
|
|
518
|
-
Vite helper
|
|
607
|
+
`localHostsPlugin` is also exported as a compatibility alias for the Vite helper.
|
|
519
608
|
|
|
520
|
-
|
|
521
|
-
import { localGhostPlugin } from "@hamedb89/localghost/vite";
|
|
522
|
-
```
|
|
609
|
+
## Trust
|
|
523
610
|
|
|
524
|
-
|
|
611
|
+
- CI runs typecheck, build, site build, and npm package dry-run on Node 20 and 22.
|
|
612
|
+
- GitHub Pages is deployed by Actions from the checked-in `site/`, `docs/`, and `assets/` sources.
|
|
613
|
+
- Preview the exact Pages artifact locally with `npm run site:serve`, then open `http://127.0.0.1:4173/`.
|
|
614
|
+
- npm publish is guarded by `prepublishOnly` and the release workflow publishes with npm provenance.
|
|
615
|
+
- Runtime dependencies are intentionally small: `commander` and `execa`. Vite is an optional peer dependency.
|
|
616
|
+
- No postinstall scripts, hidden Homebrew installs, surprise browser tabs, or broad hosts-file rewrites.
|
|
617
|
+
- Update checks are best-effort, cached for 24 hours, and can be disabled with `LOCALGHOST_NO_UPDATE_CHECK=1` or `--no-update-check`.
|
|
525
618
|
|
|
526
619
|
## More Docs
|
|
527
620
|
|
|
528
|
-
Localghost copy can be mysterious, goofy, magical, funny, and a little absurd. The product behavior should stay boring in the best way: explicit commands, exact paths, clear errors, and no hidden installs.
|
|
529
|
-
|
|
530
621
|
- [Website](https://hamedb89.github.io/localghost/)
|
|
531
|
-
- [
|
|
532
|
-
- [
|
|
622
|
+
- [Rendered docs](https://hamedb89.github.io/localghost/docs/)
|
|
623
|
+
- [User flows](./docs/flows.md)
|
|
533
624
|
- [CLI reference](./docs/localghost.1.md)
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
<p align="center">
|
|
538
|
-
<img src="./assets/localghost-mascot.png" alt="Localghost mascot" width="180">
|
|
539
|
-
<br>
|
|
540
|
-
<img src="./assets/localghost-wordmark.png" alt="Localghost wordmark" width="420">
|
|
541
|
-
</p>
|
|
625
|
+
- [Ghost Tunnel guide](./docs/ghost-tunnel.md)
|
|
626
|
+
- [macOS widget notes](./docs/macos-widget.md)
|
|
627
|
+
- [Brand guidelines](./docs/brand.md)
|
|
542
628
|
|
|
543
629
|
## License
|
|
544
630
|
|