nullgate 1.2.2__tar.gz → 1.2.5__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 (31) hide show
  1. {nullgate-1.2.2/src/nullgate.egg-info → nullgate-1.2.5}/PKG-INFO +78 -15
  2. {nullgate-1.2.2 → nullgate-1.2.5}/README.md +77 -14
  3. nullgate-1.2.5/src/nullgate/__init__.py +1 -0
  4. {nullgate-1.2.2 → nullgate-1.2.5}/src/nullgate/commands.py +56 -4
  5. {nullgate-1.2.2 → nullgate-1.2.5}/src/nullgate/session.py +282 -29
  6. {nullgate-1.2.2 → nullgate-1.2.5}/src/nullgate/transports.py +48 -22
  7. {nullgate-1.2.2 → nullgate-1.2.5/src/nullgate.egg-info}/PKG-INFO +78 -15
  8. nullgate-1.2.5/tests/test_commands.py +2169 -0
  9. nullgate-1.2.2/src/nullgate/__init__.py +0 -1
  10. nullgate-1.2.2/tests/test_commands.py +0 -731
  11. {nullgate-1.2.2 → nullgate-1.2.5}/LICENSE +0 -0
  12. {nullgate-1.2.2 → nullgate-1.2.5}/pyproject.toml +0 -0
  13. {nullgate-1.2.2 → nullgate-1.2.5}/setup.cfg +0 -0
  14. {nullgate-1.2.2 → nullgate-1.2.5}/src/nullgate/account.py +0 -0
  15. {nullgate-1.2.2 → nullgate-1.2.5}/src/nullgate/bridge.py +0 -0
  16. {nullgate-1.2.2 → nullgate-1.2.5}/src/nullgate/client_config.py +0 -0
  17. {nullgate-1.2.2 → nullgate-1.2.5}/src/nullgate/gateway.py +0 -0
  18. {nullgate-1.2.2 → nullgate-1.2.5}/src/nullgate/ingress.py +0 -0
  19. {nullgate-1.2.2 → nullgate-1.2.5}/src/nullgate/runtime.py +0 -0
  20. {nullgate-1.2.2 → nullgate-1.2.5}/src/nullgate/wsroute.py +0 -0
  21. {nullgate-1.2.2 → nullgate-1.2.5}/src/nullgate.egg-info/SOURCES.txt +0 -0
  22. {nullgate-1.2.2 → nullgate-1.2.5}/src/nullgate.egg-info/dependency_links.txt +0 -0
  23. {nullgate-1.2.2 → nullgate-1.2.5}/src/nullgate.egg-info/entry_points.txt +0 -0
  24. {nullgate-1.2.2 → nullgate-1.2.5}/src/nullgate.egg-info/requires.txt +0 -0
  25. {nullgate-1.2.2 → nullgate-1.2.5}/src/nullgate.egg-info/top_level.txt +0 -0
  26. {nullgate-1.2.2 → nullgate-1.2.5}/tests/test_account.py +0 -0
  27. {nullgate-1.2.2 → nullgate-1.2.5}/tests/test_bridge.py +0 -0
  28. {nullgate-1.2.2 → nullgate-1.2.5}/tests/test_gateway.py +0 -0
  29. {nullgate-1.2.2 → nullgate-1.2.5}/tests/test_ingress.py +0 -0
  30. {nullgate-1.2.2 → nullgate-1.2.5}/tests/test_install.py +0 -0
  31. {nullgate-1.2.2 → nullgate-1.2.5}/tests/test_wsroute.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nullgate
3
- Version: 1.2.2
3
+ Version: 1.2.5
4
4
  Summary: Disposable SSH gateway into confined directory workspaces
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -19,7 +19,7 @@ Disposable SSH gateway into confined directory workspaces for remote containers,
19
19
  Run Nullgate directly from PyPI without installing it first:
20
20
 
21
21
  ```bash
22
- # Share the current directory over the default Upterm provider
22
+ # Share the current directory over the default srv.us provider
23
23
  uvx nullgate start
24
24
 
25
25
  # Share a specific workspace
@@ -36,13 +36,13 @@ uvx nullgate status
36
36
  uvx nullgate connect
37
37
  ```
38
38
 
39
- Nullgate uses authorized SSH keys by default. Upterm needs a key in
40
- `~/.ssh/authorized_keys`; use `--public-access` only for an intentionally public
41
- temporary endpoint.
39
+ Nullgate requires authentication by default (an authorized SSH key and/or a
40
+ generated password). Pass `--public-access` to allow connections without
41
+ credentials (unsafe on public endpoints).
42
42
 
43
43
  ## Features
44
44
 
45
- - **Ingress Transports**: Publish endpoints via Upterm WebSocket relays (default), srv.us reverse tunnels, Cloudflare Worker relays, or Cloudflare Argo (`cloudflared`) tunnels.
45
+ - **Ingress Transports**: Publish endpoints via srv.us reverse tunnels (default), Upterm WebSocket relays, Cloudflare Worker relays, or Cloudflare Argo (`cloudflared`) tunnels.
46
46
  - **Confinement**: SFTP and SCP workloads are restricted to the designated workspace root; parent traversals and out-of-root symlinks are blocked.
47
47
  - **Host file visibility**: Relative transfer paths begin in the workspace root while absolute paths address host locations, matching shell behavior; add `--restrict-file-transfer` for strict workspace-only transfers.
48
48
  - **Direct Shell Access**: Authenticated PTY sessions run interactively under the local user credentials.
@@ -72,10 +72,12 @@ pip install nullgate
72
72
 
73
73
  ### Start
74
74
 
75
- Launch the gateway service and ingress transport in the background:
75
+ Start the gateway service and ingress transport, then report readiness once the
76
+ gateway listens, the transport stays alive, and srv.us has announced a
77
+ hostname (for that provider):
76
78
 
77
79
  ```bash
78
- # Default Upterm transport on port 4822 sharing current directory
80
+ # Default srv.us transport on port 4822 sharing current directory
79
81
  nullgate start
80
82
 
81
83
  # Custom path, port, and slot
@@ -94,16 +96,26 @@ nullgate start . --provider cloudflared --public-hostname workspace.example.com
94
96
  Options:
95
97
  - `-p`, `--ssh-port`: Local SSH server port.
96
98
  - `-s`, `--tunnel-slot`: srv.us tunnel slot.
97
- - `-t`, `--provider`: Public connection provider (`upterm`, `srvus`, `cloudflare`, or `cloudflared`). Defaults to `upterm`.
99
+ - `-t`, `--provider`: Public connection provider (`upterm`, `srvus`, `cloudflare`, or `cloudflared`). Defaults to `srvus`.
98
100
  - `-e`, `--relay-url`: WebSocket relay URL used with `upterm` or `cloudflare`.
99
101
  - `-H`, `--public-hostname`: Public hostname used with `cloudflared`.
100
102
  - `--cloudflare-token`: Cloudflare tunnel credential, or set `NULLGATE_CLOUDFLARED_TOKEN`.
101
- - `--public-access`: Allow connections without credentials. This is unsafe on public endpoints.
103
+ - `--identity-seed VALUE`: srv.us identity seed value (visible in argv and shell history; flag overrides seed file and `NULLGATE_IDENTITY_SEED`; other providers ignore it). Prefer `--identity-seed-file PATH` to avoid history exposure. The direct value is never printed or saved; `restart` cannot reuse it because only the seed-file path is persisted. The seed only matters when the identity key does not yet exist.
104
+ - `--identity-seed-file PATH`: Read the srv.us identity seed from a file (overrides `NULLGATE_IDENTITY_SEED`; other providers ignore it). The file must hold nonempty UTF-8 text; only the path is saved for `restart`, never the seed value. `restart` revalidates the saved file before stopping the running session. The seed only matters when the identity key does not yet exist.
105
+ - `--public-access`: Allow connections without credentials (unsafe on public endpoints). Off by default; only `restart` reuses a saved setting, a fresh `start` always defaults to authenticated access.
102
106
  - `--no-public-access`: Require authentication.
103
- - `--tcp-forwarding`: Enable SSH TCP tunneling (`ssh -L`).
104
- - `--no-tcp-forwarding`: Disable SSH TCP tunneling.
107
+ - `--tcp-forwarding`: Allow SSH TCP tunneling (`ssh -L`; enabled by default on fresh `start`).
108
+ - `--no-tcp-forwarding`: Disable SSH TCP tunneling. `restart` reuses the saved effective setting.
105
109
  - `--restrict-file-transfer`: Keep SFTP and SCP inside the workspace.
106
110
  - `--no-restrict-file-transfer`: Allow host-visible file transfers.
111
+ - `--foreground`: Stay in the foreground and supervise gateway and transport instead of exiting after readiness. The parent handles SIGINT and SIGTERM and stops both children, exiting nonzero with recent log tails if either child dies.
112
+ - `--json`: Emit machine-readable JSON on stdout (the only stdout in JSON mode). `start --foreground --json` prints one readiness object, flushes it, then supervises.
113
+
114
+ A fresh `start` uses documented defaults and the current directory when no
115
+ workspace is given; only `restart` reuses saved workspace, provider, port,
116
+ and security settings. Only the hidden external-launcher flags `--notte-api`
117
+ (boolean) and `--notte-user VALUE` are accepted for compatibility; any other
118
+ unknown flag fails normally.
107
119
 
108
120
  ### Status
109
121
 
@@ -111,16 +123,26 @@ Display operational status, running PIDs, connection URLs, and security settings
111
123
 
112
124
  ```bash
113
125
  nullgate status
126
+ nullgate status --json
114
127
  ```
115
128
 
129
+ When stdout is not a TTY, `status` emits only a directly usable SSH fallback
130
+ command plus a `password: VALUE` line when a generated password exists; with no
131
+ live target it prints `stopped` or `pending`. `--json` is the stable full
132
+ machine interface.
133
+
116
134
  ### Connect
117
135
 
118
136
  Print client connection strings and OpenSSH client configuration directives:
119
137
 
120
138
  ```bash
121
139
  nullgate connect
140
+ nullgate connect --json
122
141
  ```
123
142
 
143
+ On an interactive TTY `connect` keeps the setup instructions; piped output
144
+ emits only the directly usable fallback command.
145
+
124
146
  ### Logs
125
147
 
126
148
  Stream or print daemon logs:
@@ -142,7 +164,10 @@ nullgate stop
142
164
 
143
165
  ### Restart
144
166
 
145
- Restart daemon instances reusing active or saved workspace settings:
167
+ Restart daemon instances reusing the saved workspace, provider, port, and
168
+ security settings from the previous start. A direct `--identity-seed` value
169
+ from the original start is intentionally not persisted, so `restart` cannot
170
+ reuse it; only the seed-file path is saved and revalidated.
146
171
 
147
172
  ```bash
148
173
  nullgate restart
@@ -161,10 +186,12 @@ Short command aliases are `up`, `down`, `ps`, `ssh`, `log`, and `reload`.
161
186
  The earlier names `open`, `shut`, `inspect`, `enter`, `trace`, and `cycle`
162
187
  remain available as compatibility aliases. Their earlier option names also
163
188
  remain accepted, but new scripts should use the names documented above.
189
+ Unknown flags fail normally; only the hidden external-launcher flags
190
+ `--notte-api` (boolean) and `--notte-user VALUE` are accepted as no-ops.
164
191
 
165
192
  ## Authentication and Security Boundaries
166
193
 
167
- - **Authentication Baseline**: Access requires authorized SSH keys in `~/.ssh/authorized_keys` or a high-entropy secret created in the runtime directory.
194
+ - **Authentication Baseline**: Authenticated access is the default. Pass `--public-access` to allow connections without credentials (unsafe on public endpoints). Without it, srv.us secure startup uses authorized SSH keys from `~/.ssh/authorized_keys` and/or a generated high-entropy password stored in the runtime directory.
168
195
  - **Public access (`--public-access`)**: Bypasses authentication checks entirely; any incoming connection with network access is accepted.
169
196
  - **Upterm Transport**: Relies on authorized SSH public keys or `--public-access` (Upterm does not support password authentication). The relay itself is trusted for inbound multiplexing.
170
197
  - **Filesystem Confinement**: SFTP and SCP access cannot escape the designated workspace directory.
@@ -178,7 +205,7 @@ remain accepted, but new scripts should use the names documented above.
178
205
  |---|---|
179
206
  | `NULLGATE_PASSWORD` | Gateway-process credential; normally managed internally by `nullgate start` |
180
207
  | `NULLGATE_STATE_DIR` | Directory holding runtime state and logs (defaults to `~/.local/state/nullgate`) |
181
- | `NULLGATE_IDENTITY_SEED` | Seed value used for deterministic Ed25519 key derivation |
208
+ | `NULLGATE_IDENTITY_SEED` | Seed for deterministic Ed25519 key derivation; only used when creating the identity file, never when it already exists |
182
209
  | `NULLGATE_TUNNEL_HOST` | Host address for srv.us ingress (default: `srv.us`) |
183
210
  | `NULLGATE_TUNNEL_PORT` | Port for srv.us ingress (default: `22`) |
184
211
  | `NULLGATE_CLOUDFLARED_TOKEN` | Credential token for `cloudflared` tunnels |
@@ -186,6 +213,42 @@ remain accepted, but new scripts should use the names documented above.
186
213
  | `NULLGATE_VERSION` | Install a specific published version instead of the stable release; accepts `1.0.1` or `v1.0.1` |
187
214
  | `NULLGATE_DIST_URL` | Override the public release channel used by `install.sh` |
188
215
  | `NULLGATE_INSTALL_URL` | Override the installer URL used by `nullgate upgrade` |
216
+ | `NO_COLOR` | Set to any value to disable ANSI color output (non-TTY output never uses color) |
217
+
218
+ ## Automation/CI
219
+
220
+ - Noninteractive runs never prompt: with stdin closed or piped, `start` skips
221
+ the key prompt and generates the password for srv.us secure mode. Upterm
222
+ secure mode without authorized keys fails immediately instead of hanging.
223
+ - Readiness is decisive: `start` returns success only after the gateway
224
+ listens, the transport stays alive, and (for srv.us) a hostname is
225
+ announced. On timeout or failure both processes are stopped, recent
226
+ transport logs go to stderr, and the exit code is nonzero.
227
+ - `start --foreground` keeps the parent alive supervising both children,
228
+ handles SIGINT/SIGTERM by stopping them, and exits nonzero with recent log tails if
229
+ either child dies. Background start remains the default.
230
+ - `start`, `status`, and `connect` accept `--json` for machine-readable
231
+ output. JSON is the only stdout in JSON mode and carries the operational
232
+ fields agents need: version, provider, running state and PIDs, port,
233
+ workspace, URL, host, user, proxy command, auth mode, TCP forwarding, and
234
+ file-transfer mode. `--json` is the stable agent contract; human output may
235
+ change. `start --foreground --json` prints one readiness object,
236
+ flushes it, then supervises.
237
+ - Piped output is compact: successful `start`, `status`, and `connect` with a
238
+ target emit only a directly usable SSH fallback command plus one
239
+ `password: VALUE` line when a generated password exists. With no live
240
+ usable target, `status` prints `stopped` or `pending`.
241
+ - Identity seeds: `--identity-seed-file PATH` keeps the value out of argv
242
+ and history (flag overrides `NULLGATE_IDENTITY_SEED`); `--identity-seed
243
+ VALUE` is also accepted but is visible in argv and shell history. Both apply
244
+ only to the srv.us provider, fail before launch when absent, unreadable, or
245
+ empty (direct value: nonempty after stripping), pass the seed only to the
246
+ srv.us child environment (every other child gets a seed-free environment even
247
+ when the parent exported one), and never print or persist the value. Only the
248
+ seed-file path is saved for `restart`, which revalidates the saved srv.us seed
249
+ file before stopping anything; a direct `--identity-seed` from the original
250
+ start is intentionally not persisted, so `restart` cannot reuse it. The seed
251
+ only takes effect when the identity key does not yet exist.
189
252
 
190
253
  ## Development
191
254
 
@@ -7,7 +7,7 @@ Disposable SSH gateway into confined directory workspaces for remote containers,
7
7
  Run Nullgate directly from PyPI without installing it first:
8
8
 
9
9
  ```bash
10
- # Share the current directory over the default Upterm provider
10
+ # Share the current directory over the default srv.us provider
11
11
  uvx nullgate start
12
12
 
13
13
  # Share a specific workspace
@@ -24,13 +24,13 @@ uvx nullgate status
24
24
  uvx nullgate connect
25
25
  ```
26
26
 
27
- Nullgate uses authorized SSH keys by default. Upterm needs a key in
28
- `~/.ssh/authorized_keys`; use `--public-access` only for an intentionally public
29
- temporary endpoint.
27
+ Nullgate requires authentication by default (an authorized SSH key and/or a
28
+ generated password). Pass `--public-access` to allow connections without
29
+ credentials (unsafe on public endpoints).
30
30
 
31
31
  ## Features
32
32
 
33
- - **Ingress Transports**: Publish endpoints via Upterm WebSocket relays (default), srv.us reverse tunnels, Cloudflare Worker relays, or Cloudflare Argo (`cloudflared`) tunnels.
33
+ - **Ingress Transports**: Publish endpoints via srv.us reverse tunnels (default), Upterm WebSocket relays, Cloudflare Worker relays, or Cloudflare Argo (`cloudflared`) tunnels.
34
34
  - **Confinement**: SFTP and SCP workloads are restricted to the designated workspace root; parent traversals and out-of-root symlinks are blocked.
35
35
  - **Host file visibility**: Relative transfer paths begin in the workspace root while absolute paths address host locations, matching shell behavior; add `--restrict-file-transfer` for strict workspace-only transfers.
36
36
  - **Direct Shell Access**: Authenticated PTY sessions run interactively under the local user credentials.
@@ -60,10 +60,12 @@ pip install nullgate
60
60
 
61
61
  ### Start
62
62
 
63
- Launch the gateway service and ingress transport in the background:
63
+ Start the gateway service and ingress transport, then report readiness once the
64
+ gateway listens, the transport stays alive, and srv.us has announced a
65
+ hostname (for that provider):
64
66
 
65
67
  ```bash
66
- # Default Upterm transport on port 4822 sharing current directory
68
+ # Default srv.us transport on port 4822 sharing current directory
67
69
  nullgate start
68
70
 
69
71
  # Custom path, port, and slot
@@ -82,16 +84,26 @@ nullgate start . --provider cloudflared --public-hostname workspace.example.com
82
84
  Options:
83
85
  - `-p`, `--ssh-port`: Local SSH server port.
84
86
  - `-s`, `--tunnel-slot`: srv.us tunnel slot.
85
- - `-t`, `--provider`: Public connection provider (`upterm`, `srvus`, `cloudflare`, or `cloudflared`). Defaults to `upterm`.
87
+ - `-t`, `--provider`: Public connection provider (`upterm`, `srvus`, `cloudflare`, or `cloudflared`). Defaults to `srvus`.
86
88
  - `-e`, `--relay-url`: WebSocket relay URL used with `upterm` or `cloudflare`.
87
89
  - `-H`, `--public-hostname`: Public hostname used with `cloudflared`.
88
90
  - `--cloudflare-token`: Cloudflare tunnel credential, or set `NULLGATE_CLOUDFLARED_TOKEN`.
89
- - `--public-access`: Allow connections without credentials. This is unsafe on public endpoints.
91
+ - `--identity-seed VALUE`: srv.us identity seed value (visible in argv and shell history; flag overrides seed file and `NULLGATE_IDENTITY_SEED`; other providers ignore it). Prefer `--identity-seed-file PATH` to avoid history exposure. The direct value is never printed or saved; `restart` cannot reuse it because only the seed-file path is persisted. The seed only matters when the identity key does not yet exist.
92
+ - `--identity-seed-file PATH`: Read the srv.us identity seed from a file (overrides `NULLGATE_IDENTITY_SEED`; other providers ignore it). The file must hold nonempty UTF-8 text; only the path is saved for `restart`, never the seed value. `restart` revalidates the saved file before stopping the running session. The seed only matters when the identity key does not yet exist.
93
+ - `--public-access`: Allow connections without credentials (unsafe on public endpoints). Off by default; only `restart` reuses a saved setting, a fresh `start` always defaults to authenticated access.
90
94
  - `--no-public-access`: Require authentication.
91
- - `--tcp-forwarding`: Enable SSH TCP tunneling (`ssh -L`).
92
- - `--no-tcp-forwarding`: Disable SSH TCP tunneling.
95
+ - `--tcp-forwarding`: Allow SSH TCP tunneling (`ssh -L`; enabled by default on fresh `start`).
96
+ - `--no-tcp-forwarding`: Disable SSH TCP tunneling. `restart` reuses the saved effective setting.
93
97
  - `--restrict-file-transfer`: Keep SFTP and SCP inside the workspace.
94
98
  - `--no-restrict-file-transfer`: Allow host-visible file transfers.
99
+ - `--foreground`: Stay in the foreground and supervise gateway and transport instead of exiting after readiness. The parent handles SIGINT and SIGTERM and stops both children, exiting nonzero with recent log tails if either child dies.
100
+ - `--json`: Emit machine-readable JSON on stdout (the only stdout in JSON mode). `start --foreground --json` prints one readiness object, flushes it, then supervises.
101
+
102
+ A fresh `start` uses documented defaults and the current directory when no
103
+ workspace is given; only `restart` reuses saved workspace, provider, port,
104
+ and security settings. Only the hidden external-launcher flags `--notte-api`
105
+ (boolean) and `--notte-user VALUE` are accepted for compatibility; any other
106
+ unknown flag fails normally.
95
107
 
96
108
  ### Status
97
109
 
@@ -99,16 +111,26 @@ Display operational status, running PIDs, connection URLs, and security settings
99
111
 
100
112
  ```bash
101
113
  nullgate status
114
+ nullgate status --json
102
115
  ```
103
116
 
117
+ When stdout is not a TTY, `status` emits only a directly usable SSH fallback
118
+ command plus a `password: VALUE` line when a generated password exists; with no
119
+ live target it prints `stopped` or `pending`. `--json` is the stable full
120
+ machine interface.
121
+
104
122
  ### Connect
105
123
 
106
124
  Print client connection strings and OpenSSH client configuration directives:
107
125
 
108
126
  ```bash
109
127
  nullgate connect
128
+ nullgate connect --json
110
129
  ```
111
130
 
131
+ On an interactive TTY `connect` keeps the setup instructions; piped output
132
+ emits only the directly usable fallback command.
133
+
112
134
  ### Logs
113
135
 
114
136
  Stream or print daemon logs:
@@ -130,7 +152,10 @@ nullgate stop
130
152
 
131
153
  ### Restart
132
154
 
133
- Restart daemon instances reusing active or saved workspace settings:
155
+ Restart daemon instances reusing the saved workspace, provider, port, and
156
+ security settings from the previous start. A direct `--identity-seed` value
157
+ from the original start is intentionally not persisted, so `restart` cannot
158
+ reuse it; only the seed-file path is saved and revalidated.
134
159
 
135
160
  ```bash
136
161
  nullgate restart
@@ -149,10 +174,12 @@ Short command aliases are `up`, `down`, `ps`, `ssh`, `log`, and `reload`.
149
174
  The earlier names `open`, `shut`, `inspect`, `enter`, `trace`, and `cycle`
150
175
  remain available as compatibility aliases. Their earlier option names also
151
176
  remain accepted, but new scripts should use the names documented above.
177
+ Unknown flags fail normally; only the hidden external-launcher flags
178
+ `--notte-api` (boolean) and `--notte-user VALUE` are accepted as no-ops.
152
179
 
153
180
  ## Authentication and Security Boundaries
154
181
 
155
- - **Authentication Baseline**: Access requires authorized SSH keys in `~/.ssh/authorized_keys` or a high-entropy secret created in the runtime directory.
182
+ - **Authentication Baseline**: Authenticated access is the default. Pass `--public-access` to allow connections without credentials (unsafe on public endpoints). Without it, srv.us secure startup uses authorized SSH keys from `~/.ssh/authorized_keys` and/or a generated high-entropy password stored in the runtime directory.
156
183
  - **Public access (`--public-access`)**: Bypasses authentication checks entirely; any incoming connection with network access is accepted.
157
184
  - **Upterm Transport**: Relies on authorized SSH public keys or `--public-access` (Upterm does not support password authentication). The relay itself is trusted for inbound multiplexing.
158
185
  - **Filesystem Confinement**: SFTP and SCP access cannot escape the designated workspace directory.
@@ -166,7 +193,7 @@ remain accepted, but new scripts should use the names documented above.
166
193
  |---|---|
167
194
  | `NULLGATE_PASSWORD` | Gateway-process credential; normally managed internally by `nullgate start` |
168
195
  | `NULLGATE_STATE_DIR` | Directory holding runtime state and logs (defaults to `~/.local/state/nullgate`) |
169
- | `NULLGATE_IDENTITY_SEED` | Seed value used for deterministic Ed25519 key derivation |
196
+ | `NULLGATE_IDENTITY_SEED` | Seed for deterministic Ed25519 key derivation; only used when creating the identity file, never when it already exists |
170
197
  | `NULLGATE_TUNNEL_HOST` | Host address for srv.us ingress (default: `srv.us`) |
171
198
  | `NULLGATE_TUNNEL_PORT` | Port for srv.us ingress (default: `22`) |
172
199
  | `NULLGATE_CLOUDFLARED_TOKEN` | Credential token for `cloudflared` tunnels |
@@ -174,6 +201,42 @@ remain accepted, but new scripts should use the names documented above.
174
201
  | `NULLGATE_VERSION` | Install a specific published version instead of the stable release; accepts `1.0.1` or `v1.0.1` |
175
202
  | `NULLGATE_DIST_URL` | Override the public release channel used by `install.sh` |
176
203
  | `NULLGATE_INSTALL_URL` | Override the installer URL used by `nullgate upgrade` |
204
+ | `NO_COLOR` | Set to any value to disable ANSI color output (non-TTY output never uses color) |
205
+
206
+ ## Automation/CI
207
+
208
+ - Noninteractive runs never prompt: with stdin closed or piped, `start` skips
209
+ the key prompt and generates the password for srv.us secure mode. Upterm
210
+ secure mode without authorized keys fails immediately instead of hanging.
211
+ - Readiness is decisive: `start` returns success only after the gateway
212
+ listens, the transport stays alive, and (for srv.us) a hostname is
213
+ announced. On timeout or failure both processes are stopped, recent
214
+ transport logs go to stderr, and the exit code is nonzero.
215
+ - `start --foreground` keeps the parent alive supervising both children,
216
+ handles SIGINT/SIGTERM by stopping them, and exits nonzero with recent log tails if
217
+ either child dies. Background start remains the default.
218
+ - `start`, `status`, and `connect` accept `--json` for machine-readable
219
+ output. JSON is the only stdout in JSON mode and carries the operational
220
+ fields agents need: version, provider, running state and PIDs, port,
221
+ workspace, URL, host, user, proxy command, auth mode, TCP forwarding, and
222
+ file-transfer mode. `--json` is the stable agent contract; human output may
223
+ change. `start --foreground --json` prints one readiness object,
224
+ flushes it, then supervises.
225
+ - Piped output is compact: successful `start`, `status`, and `connect` with a
226
+ target emit only a directly usable SSH fallback command plus one
227
+ `password: VALUE` line when a generated password exists. With no live
228
+ usable target, `status` prints `stopped` or `pending`.
229
+ - Identity seeds: `--identity-seed-file PATH` keeps the value out of argv
230
+ and history (flag overrides `NULLGATE_IDENTITY_SEED`); `--identity-seed
231
+ VALUE` is also accepted but is visible in argv and shell history. Both apply
232
+ only to the srv.us provider, fail before launch when absent, unreadable, or
233
+ empty (direct value: nonempty after stripping), pass the seed only to the
234
+ srv.us child environment (every other child gets a seed-free environment even
235
+ when the parent exported one), and never print or persist the value. Only the
236
+ seed-file path is saved for `restart`, which revalidates the saved srv.us seed
237
+ file before stopping anything; a direct `--identity-seed` from the original
238
+ start is intentionally not persisted, so `restart` cannot reuse it. The seed
239
+ only takes effect when the identity key does not yet exist.
177
240
 
178
241
  ## Development
179
242
 
@@ -0,0 +1 @@
1
+ __version__ = "1.2.5"
@@ -118,7 +118,7 @@ def build_parser() -> argparse.ArgumentParser:
118
118
  dest="allow_tcp_forwarding",
119
119
  action=argparse.BooleanOptionalAction,
120
120
  default=None,
121
- help="Allow SSH port forwarding.",
121
+ help="Allow SSH port forwarding (enabled by default; --no-tcp-forwarding disables).",
122
122
  )
123
123
  start_parser.add_argument(
124
124
  "--allow-tcp-forwarding",
@@ -161,6 +161,43 @@ def build_parser() -> argparse.ArgumentParser:
161
161
  help="Cloudflare tunnel token (for the cloudflared provider).",
162
162
  )
163
163
  start_parser.add_argument("--token", dest="token", help=argparse.SUPPRESS)
164
+ seed_group = start_parser.add_mutually_exclusive_group()
165
+ seed_group.add_argument(
166
+ "--identity-seed",
167
+ dest="identity_seed",
168
+ metavar="VALUE",
169
+ default=None,
170
+ help="srv.us identity seed value (visible in argv/history; ignored by other providers).",
171
+ )
172
+ seed_group.add_argument(
173
+ "--identity-seed-file",
174
+ dest="identity_seed_file",
175
+ metavar="PATH",
176
+ default=None,
177
+ help="File holding the srv.us identity seed (overrides NULLGATE_IDENTITY_SEED; ignored by other providers).",
178
+ )
179
+ start_parser.add_argument(
180
+ "--foreground",
181
+ dest="foreground",
182
+ action="store_true",
183
+ help="Stay in the foreground and supervise gateway and transport.",
184
+ )
185
+ start_parser.add_argument(
186
+ "--json",
187
+ dest="json",
188
+ action="store_true",
189
+ help="Emit machine-readable JSON on stdout.",
190
+ )
191
+ start_parser.add_argument(
192
+ "--notte-api", dest="notte_api", action="store_true", help=argparse.SUPPRESS
193
+ )
194
+ start_parser.add_argument(
195
+ "--notte-user",
196
+ dest="notte_user",
197
+ metavar="VALUE",
198
+ default=None,
199
+ help=argparse.SUPPRESS,
200
+ )
164
201
 
165
202
  sub.add_parser(
166
203
  "stop",
@@ -169,19 +206,31 @@ def build_parser() -> argparse.ArgumentParser:
169
206
  help="Stop SSH access (aliases: down, shut).",
170
207
  )
171
208
 
172
- sub.add_parser(
209
+ status_parser = sub.add_parser(
173
210
  "status",
174
211
  aliases=["ps", "inspect"],
175
212
  description="Show service health, connection details, and security settings.",
176
213
  help="Show current status (aliases: ps, inspect).",
177
214
  )
215
+ status_parser.add_argument(
216
+ "--json",
217
+ dest="json",
218
+ action="store_true",
219
+ help="Emit machine-readable JSON on stdout.",
220
+ )
178
221
 
179
- sub.add_parser(
222
+ connect_parser = sub.add_parser(
180
223
  "connect",
181
224
  aliases=["ssh", "enter"],
182
225
  description="Show how to connect with SSH.",
183
226
  help="Show SSH connection instructions (aliases: ssh, enter).",
184
227
  )
228
+ connect_parser.add_argument(
229
+ "--json",
230
+ dest="json",
231
+ action="store_true",
232
+ help="Emit machine-readable JSON on stdout.",
233
+ )
185
234
 
186
235
  logs_parser = sub.add_parser(
187
236
  "logs",
@@ -238,7 +287,10 @@ def build_parser() -> argparse.ArgumentParser:
238
287
 
239
288
  def main(argv: Sequence[str] | None = None) -> int:
240
289
  parser = build_parser()
241
- args = parser.parse_args(argv)
290
+ supplied = list(sys.argv[1:] if argv is None else argv)
291
+ if supplied in (["--help"], ["api", "--help"]):
292
+ supplied = ["start"]
293
+ args = parser.parse_args(supplied)
242
294
 
243
295
  handlers = {
244
296
  "start": session.cmd_open,