nullgate 1.1.1__tar.gz → 1.2.1__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.1.1/src/nullgate.egg-info → nullgate-1.2.1}/PKG-INFO +73 -37
  2. {nullgate-1.1.1 → nullgate-1.2.1}/README.md +72 -36
  3. nullgate-1.2.1/src/nullgate/__init__.py +1 -0
  4. nullgate-1.2.1/src/nullgate/commands.py +275 -0
  5. {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/session.py +24 -12
  6. {nullgate-1.1.1 → nullgate-1.2.1/src/nullgate.egg-info}/PKG-INFO +73 -37
  7. {nullgate-1.1.1 → nullgate-1.2.1}/tests/test_commands.py +93 -20
  8. nullgate-1.1.1/src/nullgate/__init__.py +0 -1
  9. nullgate-1.1.1/src/nullgate/commands.py +0 -179
  10. {nullgate-1.1.1 → nullgate-1.2.1}/LICENSE +0 -0
  11. {nullgate-1.1.1 → nullgate-1.2.1}/pyproject.toml +0 -0
  12. {nullgate-1.1.1 → nullgate-1.2.1}/setup.cfg +0 -0
  13. {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/account.py +0 -0
  14. {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/bridge.py +0 -0
  15. {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/client_config.py +0 -0
  16. {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/gateway.py +0 -0
  17. {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/ingress.py +0 -0
  18. {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/runtime.py +0 -0
  19. {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/transports.py +0 -0
  20. {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate/wsroute.py +0 -0
  21. {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate.egg-info/SOURCES.txt +0 -0
  22. {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate.egg-info/dependency_links.txt +0 -0
  23. {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate.egg-info/entry_points.txt +0 -0
  24. {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate.egg-info/requires.txt +0 -0
  25. {nullgate-1.1.1 → nullgate-1.2.1}/src/nullgate.egg-info/top_level.txt +0 -0
  26. {nullgate-1.1.1 → nullgate-1.2.1}/tests/test_account.py +0 -0
  27. {nullgate-1.1.1 → nullgate-1.2.1}/tests/test_bridge.py +0 -0
  28. {nullgate-1.1.1 → nullgate-1.2.1}/tests/test_gateway.py +0 -0
  29. {nullgate-1.1.1 → nullgate-1.2.1}/tests/test_ingress.py +0 -0
  30. {nullgate-1.1.1 → nullgate-1.2.1}/tests/test_install.py +0 -0
  31. {nullgate-1.1.1 → nullgate-1.2.1}/tests/test_wsroute.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nullgate
3
- Version: 1.1.1
3
+ Version: 1.2.1
4
4
  Summary: Disposable SSH gateway into confined directory workspaces
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -14,11 +14,37 @@ Dynamic: license-file
14
14
 
15
15
  Disposable SSH gateway into confined directory workspaces for remote containers, ephemeral developer boxes, CI nodes, and cloud instances.
16
16
 
17
+ ## Quick start
18
+
19
+ Run Nullgate directly from PyPI without installing it first:
20
+
21
+ ```bash
22
+ # Share the current directory over the default Upterm provider
23
+ uvx nullgate start
24
+
25
+ # Share a specific workspace
26
+ uvx nullgate start /workspace
27
+
28
+ # Compact form: workspace, SSH port, srv.us slot, provider
29
+ uvx nullgate start /workspace 8022 1 -t srvus
30
+ ```
31
+
32
+ Then check the endpoint and print the SSH connection setup:
33
+
34
+ ```bash
35
+ uvx nullgate status
36
+ uvx nullgate connect
37
+ ```
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.
42
+
17
43
  ## Features
18
44
 
19
45
  - **Ingress Transports**: Publish endpoints via Upterm WebSocket relays (default), srv.us reverse tunnels, Cloudflare Worker relays, or Cloudflare Argo (`cloudflared`) tunnels.
20
46
  - **Confinement**: SFTP and SCP workloads are restricted to the designated workspace root; parent traversals and out-of-root symlinks are blocked.
21
- - **Host file visibility**: Relative transfer paths begin in the workspace root while absolute paths address host locations, matching shell behavior; add `--confine-sftp` for strict workspace-only transfers.
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.
22
48
  - **Direct Shell Access**: Authenticated PTY sessions run interactively under the local user credentials.
23
49
  - **Synthetic UID Resilience**: Runs seamlessly in container environments missing user records in `/etc/passwd`.
24
50
  - **Persistent Key Identities**: Host keys and identity seeds persist across restarts rather than being silently regenerated.
@@ -44,77 +70,82 @@ pip install nullgate
44
70
 
45
71
  ## Usage
46
72
 
47
- ### Open
73
+ ### Start
48
74
 
49
75
  Launch the gateway service and ingress transport in the background:
50
76
 
51
77
  ```bash
52
78
  # Default Upterm transport on port 4822 sharing current directory
53
- nullgate open
79
+ nullgate start
54
80
 
55
81
  # Custom path, port, and slot
56
- nullgate open /workspace --port 4822 --slot 1
82
+ nullgate start /workspace --ssh-port 4822 --tunnel-slot 1
83
+
84
+ # The same values in compact positional form
85
+ nullgate start /workspace 4822 1
57
86
 
58
87
  # Select specific transport provider
59
- nullgate open . --transport upterm
60
- nullgate open . --transport srvus
61
- nullgate open . --transport cloudflare --endpoint https://relay.example.com
62
- nullgate open . --transport cloudflared --hostname workspace.example.com --token "$TUNNEL_TOKEN"
88
+ nullgate start . --provider upterm
89
+ nullgate start . --provider srvus
90
+ nullgate start . --provider cloudflare --relay-url https://relay.example.com
91
+ nullgate start . --provider cloudflared --public-hostname workspace.example.com --cloudflare-token "$TUNNEL_TOKEN"
63
92
  ```
64
93
 
65
94
  Options:
66
- - `--transport`: Ingress provider (`upterm`, `srvus`, `cloudflare`, `cloudflared`). Defaults to `upterm`.
67
- - `--endpoint`: WebSocket or relay endpoint URL (used with `upterm` or `cloudflare`).
68
- - `--hostname`: Public ingress hostname for `cloudflared`.
69
- - `--token`: Argo tunnel authentication credential (or set via `NULLGATE_CLOUDFLARED_TOKEN`).
70
- - `--accept`: Skip credential checks and admit any client connection.
71
- - `--no-accept`: Restore authentication when saved settings previously enabled unrestricted access.
72
- - `--allow-tcp-forwarding`: Enable SSH TCP tunneling (`ssh -L`).
73
- - `--no-allow-tcp-forwarding`: Disable forwarding when it was enabled in saved settings.
74
- - `--confine-sftp`: Confine SFTP and SCP transfers to the workspace root.
75
- - `--no-confine-sftp`: Restore host-visible transfers when confinement was saved.
76
-
77
- ### Inspect
95
+ - `-p`, `--ssh-port`: Local SSH server port.
96
+ - `-s`, `--tunnel-slot`: srv.us tunnel slot.
97
+ - `-t`, `--provider`: Public connection provider (`upterm`, `srvus`, `cloudflare`, or `cloudflared`). Defaults to `upterm`.
98
+ - `-e`, `--relay-url`: WebSocket relay URL used with `upterm` or `cloudflare`.
99
+ - `-H`, `--public-hostname`: Public hostname used with `cloudflared`.
100
+ - `--cloudflare-token`: Cloudflare tunnel credential, or set `NULLGATE_CLOUDFLARED_TOKEN`.
101
+ - `--public-access`: Allow connections without credentials. This is unsafe on public endpoints.
102
+ - `--no-public-access`: Require authentication.
103
+ - `--tcp-forwarding`: Enable SSH TCP tunneling (`ssh -L`).
104
+ - `--no-tcp-forwarding`: Disable SSH TCP tunneling.
105
+ - `--restrict-file-transfer`: Keep SFTP and SCP inside the workspace.
106
+ - `--no-restrict-file-transfer`: Allow host-visible file transfers.
107
+
108
+ ### Status
78
109
 
79
110
  Display operational status, running PIDs, connection URLs, and security settings:
80
111
 
81
112
  ```bash
82
- nullgate inspect
113
+ nullgate status
83
114
  ```
84
115
 
85
- ### Enter
116
+ ### Connect
86
117
 
87
118
  Print client connection strings and OpenSSH client configuration directives:
88
119
 
89
120
  ```bash
90
- nullgate enter
121
+ nullgate connect
91
122
  ```
92
123
 
93
- ### Trace
124
+ ### Logs
94
125
 
95
126
  Stream or print daemon logs:
96
127
 
97
128
  ```bash
98
- nullgate trace
99
- nullgate trace -f
100
- nullgate trace gateway
101
- nullgate trace transport
129
+ nullgate logs
130
+ nullgate logs -f
131
+ nullgate logs gateway
132
+ nullgate logs transport
102
133
  ```
103
134
 
104
- ### Shut
135
+ ### Stop
105
136
 
106
137
  Terminate running gateway and transport background processes:
107
138
 
108
139
  ```bash
109
- nullgate shut
140
+ nullgate stop
110
141
  ```
111
142
 
112
- ### Cycle
143
+ ### Restart
113
144
 
114
145
  Restart daemon instances reusing active or saved workspace settings:
115
146
 
116
147
  ```bash
117
- nullgate cycle
148
+ nullgate restart
118
149
  ```
119
150
 
120
151
  ### Upgrade
@@ -126,13 +157,18 @@ nullgate upgrade
126
157
  nullgate upgrade --version 1.0.1
127
158
  ```
128
159
 
160
+ Short command aliases are `up`, `down`, `ps`, `ssh`, `log`, and `reload`.
161
+ The earlier names `open`, `shut`, `inspect`, `enter`, `trace`, and `cycle`
162
+ remain available as compatibility aliases. Their earlier option names also
163
+ remain accepted, but new scripts should use the names documented above.
164
+
129
165
  ## Authentication and Security Boundaries
130
166
 
131
167
  - **Authentication Baseline**: Access requires authorized SSH keys in `~/.ssh/authorized_keys` or a high-entropy secret created in the runtime directory.
132
- - **Unrestricted Mode (`--accept`)**: Bypasses authentication checks entirely; any incoming connection with network access is accepted.
133
- - **Upterm Transport**: Relies on authorized SSH public keys or `--accept` (Upterm does not support password authentication). The relay itself is trusted for inbound multiplexing.
168
+ - **Public access (`--public-access`)**: Bypasses authentication checks entirely; any incoming connection with network access is accepted.
169
+ - **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.
134
170
  - **Filesystem Confinement**: SFTP and SCP access cannot escape the designated workspace directory.
135
- - **Host Transfer Mode (default)**: Relative SFTP and SCP paths begin in the workspace directory, while absolute paths refer to host filesystem locations subject to OS user permissions. Pass `--confine-sftp` to limit transfers to the workspace namespace and reject symlink escapes.
171
+ - **Host Transfer Mode (default)**: Relative SFTP and SCP paths begin in the workspace directory, while absolute paths refer to host filesystem locations subject to OS user permissions. Pass `--restrict-file-transfer` to limit transfers to the workspace namespace and reject symlink escapes.
136
172
  - **Interactive Shell Privileges**: Command and terminal sessions execute with the permissions of the host account rather than inside an OS chroot.
137
173
  - **State Storage**: Daemon PIDs and logs are maintained in `gateway.pid`, `transport.pid`, `gateway.log`, and `transport.log`, with session records kept in `manifest.json` and `settings.json`.
138
174
 
@@ -140,7 +176,7 @@ nullgate upgrade --version 1.0.1
140
176
 
141
177
  | Variable | Description |
142
178
  |---|---|
143
- | `NULLGATE_PASSWORD` | Gateway-process credential; normally managed internally by `nullgate open` |
179
+ | `NULLGATE_PASSWORD` | Gateway-process credential; normally managed internally by `nullgate start` |
144
180
  | `NULLGATE_STATE_DIR` | Directory holding runtime state and logs (defaults to `~/.local/state/nullgate`) |
145
181
  | `NULLGATE_IDENTITY_SEED` | Seed value used for deterministic Ed25519 key derivation |
146
182
  | `NULLGATE_TUNNEL_HOST` | Host address for srv.us ingress (default: `srv.us`) |
@@ -2,11 +2,37 @@
2
2
 
3
3
  Disposable SSH gateway into confined directory workspaces for remote containers, ephemeral developer boxes, CI nodes, and cloud instances.
4
4
 
5
+ ## Quick start
6
+
7
+ Run Nullgate directly from PyPI without installing it first:
8
+
9
+ ```bash
10
+ # Share the current directory over the default Upterm provider
11
+ uvx nullgate start
12
+
13
+ # Share a specific workspace
14
+ uvx nullgate start /workspace
15
+
16
+ # Compact form: workspace, SSH port, srv.us slot, provider
17
+ uvx nullgate start /workspace 8022 1 -t srvus
18
+ ```
19
+
20
+ Then check the endpoint and print the SSH connection setup:
21
+
22
+ ```bash
23
+ uvx nullgate status
24
+ uvx nullgate connect
25
+ ```
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.
30
+
5
31
  ## Features
6
32
 
7
33
  - **Ingress Transports**: Publish endpoints via Upterm WebSocket relays (default), srv.us reverse tunnels, Cloudflare Worker relays, or Cloudflare Argo (`cloudflared`) tunnels.
8
34
  - **Confinement**: SFTP and SCP workloads are restricted to the designated workspace root; parent traversals and out-of-root symlinks are blocked.
9
- - **Host file visibility**: Relative transfer paths begin in the workspace root while absolute paths address host locations, matching shell behavior; add `--confine-sftp` for strict workspace-only transfers.
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.
10
36
  - **Direct Shell Access**: Authenticated PTY sessions run interactively under the local user credentials.
11
37
  - **Synthetic UID Resilience**: Runs seamlessly in container environments missing user records in `/etc/passwd`.
12
38
  - **Persistent Key Identities**: Host keys and identity seeds persist across restarts rather than being silently regenerated.
@@ -32,77 +58,82 @@ pip install nullgate
32
58
 
33
59
  ## Usage
34
60
 
35
- ### Open
61
+ ### Start
36
62
 
37
63
  Launch the gateway service and ingress transport in the background:
38
64
 
39
65
  ```bash
40
66
  # Default Upterm transport on port 4822 sharing current directory
41
- nullgate open
67
+ nullgate start
42
68
 
43
69
  # Custom path, port, and slot
44
- nullgate open /workspace --port 4822 --slot 1
70
+ nullgate start /workspace --ssh-port 4822 --tunnel-slot 1
71
+
72
+ # The same values in compact positional form
73
+ nullgate start /workspace 4822 1
45
74
 
46
75
  # Select specific transport provider
47
- nullgate open . --transport upterm
48
- nullgate open . --transport srvus
49
- nullgate open . --transport cloudflare --endpoint https://relay.example.com
50
- nullgate open . --transport cloudflared --hostname workspace.example.com --token "$TUNNEL_TOKEN"
76
+ nullgate start . --provider upterm
77
+ nullgate start . --provider srvus
78
+ nullgate start . --provider cloudflare --relay-url https://relay.example.com
79
+ nullgate start . --provider cloudflared --public-hostname workspace.example.com --cloudflare-token "$TUNNEL_TOKEN"
51
80
  ```
52
81
 
53
82
  Options:
54
- - `--transport`: Ingress provider (`upterm`, `srvus`, `cloudflare`, `cloudflared`). Defaults to `upterm`.
55
- - `--endpoint`: WebSocket or relay endpoint URL (used with `upterm` or `cloudflare`).
56
- - `--hostname`: Public ingress hostname for `cloudflared`.
57
- - `--token`: Argo tunnel authentication credential (or set via `NULLGATE_CLOUDFLARED_TOKEN`).
58
- - `--accept`: Skip credential checks and admit any client connection.
59
- - `--no-accept`: Restore authentication when saved settings previously enabled unrestricted access.
60
- - `--allow-tcp-forwarding`: Enable SSH TCP tunneling (`ssh -L`).
61
- - `--no-allow-tcp-forwarding`: Disable forwarding when it was enabled in saved settings.
62
- - `--confine-sftp`: Confine SFTP and SCP transfers to the workspace root.
63
- - `--no-confine-sftp`: Restore host-visible transfers when confinement was saved.
64
-
65
- ### Inspect
83
+ - `-p`, `--ssh-port`: Local SSH server port.
84
+ - `-s`, `--tunnel-slot`: srv.us tunnel slot.
85
+ - `-t`, `--provider`: Public connection provider (`upterm`, `srvus`, `cloudflare`, or `cloudflared`). Defaults to `upterm`.
86
+ - `-e`, `--relay-url`: WebSocket relay URL used with `upterm` or `cloudflare`.
87
+ - `-H`, `--public-hostname`: Public hostname used with `cloudflared`.
88
+ - `--cloudflare-token`: Cloudflare tunnel credential, or set `NULLGATE_CLOUDFLARED_TOKEN`.
89
+ - `--public-access`: Allow connections without credentials. This is unsafe on public endpoints.
90
+ - `--no-public-access`: Require authentication.
91
+ - `--tcp-forwarding`: Enable SSH TCP tunneling (`ssh -L`).
92
+ - `--no-tcp-forwarding`: Disable SSH TCP tunneling.
93
+ - `--restrict-file-transfer`: Keep SFTP and SCP inside the workspace.
94
+ - `--no-restrict-file-transfer`: Allow host-visible file transfers.
95
+
96
+ ### Status
66
97
 
67
98
  Display operational status, running PIDs, connection URLs, and security settings:
68
99
 
69
100
  ```bash
70
- nullgate inspect
101
+ nullgate status
71
102
  ```
72
103
 
73
- ### Enter
104
+ ### Connect
74
105
 
75
106
  Print client connection strings and OpenSSH client configuration directives:
76
107
 
77
108
  ```bash
78
- nullgate enter
109
+ nullgate connect
79
110
  ```
80
111
 
81
- ### Trace
112
+ ### Logs
82
113
 
83
114
  Stream or print daemon logs:
84
115
 
85
116
  ```bash
86
- nullgate trace
87
- nullgate trace -f
88
- nullgate trace gateway
89
- nullgate trace transport
117
+ nullgate logs
118
+ nullgate logs -f
119
+ nullgate logs gateway
120
+ nullgate logs transport
90
121
  ```
91
122
 
92
- ### Shut
123
+ ### Stop
93
124
 
94
125
  Terminate running gateway and transport background processes:
95
126
 
96
127
  ```bash
97
- nullgate shut
128
+ nullgate stop
98
129
  ```
99
130
 
100
- ### Cycle
131
+ ### Restart
101
132
 
102
133
  Restart daemon instances reusing active or saved workspace settings:
103
134
 
104
135
  ```bash
105
- nullgate cycle
136
+ nullgate restart
106
137
  ```
107
138
 
108
139
  ### Upgrade
@@ -114,13 +145,18 @@ nullgate upgrade
114
145
  nullgate upgrade --version 1.0.1
115
146
  ```
116
147
 
148
+ Short command aliases are `up`, `down`, `ps`, `ssh`, `log`, and `reload`.
149
+ The earlier names `open`, `shut`, `inspect`, `enter`, `trace`, and `cycle`
150
+ remain available as compatibility aliases. Their earlier option names also
151
+ remain accepted, but new scripts should use the names documented above.
152
+
117
153
  ## Authentication and Security Boundaries
118
154
 
119
155
  - **Authentication Baseline**: Access requires authorized SSH keys in `~/.ssh/authorized_keys` or a high-entropy secret created in the runtime directory.
120
- - **Unrestricted Mode (`--accept`)**: Bypasses authentication checks entirely; any incoming connection with network access is accepted.
121
- - **Upterm Transport**: Relies on authorized SSH public keys or `--accept` (Upterm does not support password authentication). The relay itself is trusted for inbound multiplexing.
156
+ - **Public access (`--public-access`)**: Bypasses authentication checks entirely; any incoming connection with network access is accepted.
157
+ - **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.
122
158
  - **Filesystem Confinement**: SFTP and SCP access cannot escape the designated workspace directory.
123
- - **Host Transfer Mode (default)**: Relative SFTP and SCP paths begin in the workspace directory, while absolute paths refer to host filesystem locations subject to OS user permissions. Pass `--confine-sftp` to limit transfers to the workspace namespace and reject symlink escapes.
159
+ - **Host Transfer Mode (default)**: Relative SFTP and SCP paths begin in the workspace directory, while absolute paths refer to host filesystem locations subject to OS user permissions. Pass `--restrict-file-transfer` to limit transfers to the workspace namespace and reject symlink escapes.
124
160
  - **Interactive Shell Privileges**: Command and terminal sessions execute with the permissions of the host account rather than inside an OS chroot.
125
161
  - **State Storage**: Daemon PIDs and logs are maintained in `gateway.pid`, `transport.pid`, `gateway.log`, and `transport.log`, with session records kept in `manifest.json` and `settings.json`.
126
162
 
@@ -128,7 +164,7 @@ nullgate upgrade --version 1.0.1
128
164
 
129
165
  | Variable | Description |
130
166
  |---|---|
131
- | `NULLGATE_PASSWORD` | Gateway-process credential; normally managed internally by `nullgate open` |
167
+ | `NULLGATE_PASSWORD` | Gateway-process credential; normally managed internally by `nullgate start` |
132
168
  | `NULLGATE_STATE_DIR` | Directory holding runtime state and logs (defaults to `~/.local/state/nullgate`) |
133
169
  | `NULLGATE_IDENTITY_SEED` | Seed value used for deterministic Ed25519 key derivation |
134
170
  | `NULLGATE_TUNNEL_HOST` | Host address for srv.us ingress (default: `srv.us`) |
@@ -0,0 +1 @@
1
+ __version__ = "1.2.1"
@@ -0,0 +1,275 @@
1
+ """Command-line parser and entry point dispatch for Nullgate."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import sys
7
+ from typing import Sequence
8
+
9
+ from nullgate import session, transports
10
+
11
+ SUBCOMMANDS = (
12
+ "start",
13
+ "up",
14
+ "stop",
15
+ "down",
16
+ "status",
17
+ "ps",
18
+ "connect",
19
+ "ssh",
20
+ "logs",
21
+ "log",
22
+ "restart",
23
+ "reload",
24
+ "upgrade",
25
+ "open",
26
+ "shut",
27
+ "inspect",
28
+ "enter",
29
+ "trace",
30
+ "cycle",
31
+ )
32
+
33
+
34
+ def build_parser() -> argparse.ArgumentParser:
35
+ parser = argparse.ArgumentParser(
36
+ prog="nullgate",
37
+ description="Disposable SSH gateway into confined directory workspaces.",
38
+ )
39
+ parser.add_argument(
40
+ "-V",
41
+ "--version",
42
+ action="version",
43
+ version=f"nullgate {session._version()}",
44
+ )
45
+ sub = parser.add_subparsers(dest="command", required=True, metavar="command")
46
+
47
+ start_parser = sub.add_parser(
48
+ "start",
49
+ aliases=["up", "open"],
50
+ description="Start SSH access to a workspace.",
51
+ help="Start SSH access to a workspace (aliases: up, open).",
52
+ )
53
+ start_parser.add_argument(
54
+ "workspace",
55
+ nargs="?",
56
+ help="Directory to make available (default: current directory).",
57
+ )
58
+ start_parser.add_argument(
59
+ "port_arg",
60
+ nargs="?",
61
+ type=int,
62
+ help="Local SSH server port (shorthand positional).",
63
+ )
64
+ start_parser.add_argument(
65
+ "slot_arg",
66
+ nargs="?",
67
+ type=int,
68
+ help="srv.us tunnel slot (shorthand positional).",
69
+ )
70
+ start_parser.add_argument(
71
+ "-p",
72
+ "--ssh-port",
73
+ dest="port",
74
+ type=int,
75
+ default=None,
76
+ help=f"Local SSH server port (default: {session.DEFAULT_PORT}).",
77
+ )
78
+ start_parser.add_argument("--port", dest="port", type=int, help=argparse.SUPPRESS)
79
+ start_parser.add_argument(
80
+ "-s",
81
+ "--tunnel-slot",
82
+ dest="slot",
83
+ type=int,
84
+ default=None,
85
+ help=f"srv.us tunnel slot (default: {session.DEFAULT_SLOT}).",
86
+ )
87
+ start_parser.add_argument("--slot", dest="slot", type=int, help=argparse.SUPPRESS)
88
+ start_parser.add_argument(
89
+ "-t",
90
+ "--provider",
91
+ dest="transport",
92
+ choices=transports.VALID_TRANSPORTS,
93
+ default=None,
94
+ help=f"Public connection provider (default: {transports.DEFAULT_TRANSPORT}).",
95
+ )
96
+ start_parser.add_argument(
97
+ "--transport",
98
+ dest="transport",
99
+ choices=transports.VALID_TRANSPORTS,
100
+ help=argparse.SUPPRESS,
101
+ )
102
+ start_parser.add_argument(
103
+ "--public-access",
104
+ dest="accept",
105
+ action=argparse.BooleanOptionalAction,
106
+ default=None,
107
+ help="Allow connections without SSH credentials (unsafe on public endpoints).",
108
+ )
109
+ start_parser.add_argument(
110
+ "--accept",
111
+ dest="accept",
112
+ action=argparse.BooleanOptionalAction,
113
+ default=None,
114
+ help=argparse.SUPPRESS,
115
+ )
116
+ start_parser.add_argument(
117
+ "--tcp-forwarding",
118
+ dest="allow_tcp_forwarding",
119
+ action=argparse.BooleanOptionalAction,
120
+ default=None,
121
+ help="Allow SSH port forwarding.",
122
+ )
123
+ start_parser.add_argument(
124
+ "--allow-tcp-forwarding",
125
+ dest="allow_tcp_forwarding",
126
+ action=argparse.BooleanOptionalAction,
127
+ default=None,
128
+ help=argparse.SUPPRESS,
129
+ )
130
+ start_parser.add_argument(
131
+ "--restrict-file-transfer",
132
+ dest="confine_sftp",
133
+ action=argparse.BooleanOptionalAction,
134
+ default=None,
135
+ help="Keep SFTP and SCP transfers inside the workspace.",
136
+ )
137
+ start_parser.add_argument(
138
+ "--confine-sftp",
139
+ dest="confine_sftp",
140
+ action=argparse.BooleanOptionalAction,
141
+ default=None,
142
+ help=argparse.SUPPRESS,
143
+ )
144
+ start_parser.add_argument(
145
+ "-e",
146
+ "--relay-url",
147
+ dest="endpoint",
148
+ help="WebSocket relay URL (for upterm or cloudflare providers).",
149
+ )
150
+ start_parser.add_argument("--endpoint", dest="endpoint", help=argparse.SUPPRESS)
151
+ start_parser.add_argument(
152
+ "-H",
153
+ "--public-hostname",
154
+ dest="hostname",
155
+ help="Public hostname (for the cloudflared provider).",
156
+ )
157
+ start_parser.add_argument("--hostname", dest="hostname", help=argparse.SUPPRESS)
158
+ start_parser.add_argument(
159
+ "--cloudflare-token",
160
+ dest="token",
161
+ help="Cloudflare tunnel token (for the cloudflared provider).",
162
+ )
163
+ start_parser.add_argument("--token", dest="token", help=argparse.SUPPRESS)
164
+
165
+ sub.add_parser(
166
+ "stop",
167
+ aliases=["down", "shut"],
168
+ description="Stop SSH access and its connection provider.",
169
+ help="Stop SSH access (aliases: down, shut).",
170
+ )
171
+
172
+ sub.add_parser(
173
+ "status",
174
+ aliases=["ps", "inspect"],
175
+ description="Show service health, connection details, and security settings.",
176
+ help="Show current status (aliases: ps, inspect).",
177
+ )
178
+
179
+ sub.add_parser(
180
+ "connect",
181
+ aliases=["ssh", "enter"],
182
+ description="Show how to connect with SSH.",
183
+ help="Show SSH connection instructions (aliases: ssh, enter).",
184
+ )
185
+
186
+ logs_parser = sub.add_parser(
187
+ "logs",
188
+ aliases=["log", "trace"],
189
+ description="View or follow service logs.",
190
+ help="View or follow service logs (aliases: log, trace).",
191
+ )
192
+ logs_parser.add_argument(
193
+ "target",
194
+ nargs="?",
195
+ default="all",
196
+ choices=["all", "gateway", "transport"],
197
+ help="Target log stream to inspect (default all).",
198
+ )
199
+ logs_parser.add_argument(
200
+ "-f",
201
+ "--follow",
202
+ dest="f",
203
+ action="store_true",
204
+ help="Follow log stream output continuously.",
205
+ )
206
+
207
+ sub.add_parser(
208
+ "restart",
209
+ aliases=["reload", "cycle"],
210
+ description="Restart SSH access using the saved settings.",
211
+ help="Restart using saved settings (aliases: reload, cycle).",
212
+ )
213
+
214
+ upgrade_parser = sub.add_parser(
215
+ "upgrade",
216
+ description="Fetch and install latest Nullgate release.",
217
+ help="Fetch and install latest Nullgate release.",
218
+ )
219
+ upgrade_parser.add_argument(
220
+ "--version",
221
+ help="Install a specific published version instead of the latest stable release.",
222
+ )
223
+
224
+ proxy_parser = sub.add_parser(
225
+ "proxy",
226
+ description=argparse.SUPPRESS,
227
+ )
228
+ proxy_parser.add_argument("url")
229
+
230
+ upterm_proxy = sub.add_parser(
231
+ "upterm-proxy",
232
+ description=argparse.SUPPRESS,
233
+ )
234
+ upterm_proxy.add_argument("url")
235
+
236
+ return parser
237
+
238
+
239
+ def main(argv: Sequence[str] | None = None) -> int:
240
+ parser = build_parser()
241
+ args = parser.parse_args(argv)
242
+
243
+ handlers = {
244
+ "start": session.cmd_open,
245
+ "up": session.cmd_open,
246
+ "open": session.cmd_open,
247
+ "stop": session.cmd_shut,
248
+ "down": session.cmd_shut,
249
+ "shut": session.cmd_shut,
250
+ "status": session.cmd_inspect,
251
+ "ps": session.cmd_inspect,
252
+ "inspect": session.cmd_inspect,
253
+ "connect": session.cmd_enter,
254
+ "ssh": session.cmd_enter,
255
+ "enter": session.cmd_enter,
256
+ "logs": session.cmd_trace,
257
+ "log": session.cmd_trace,
258
+ "trace": session.cmd_trace,
259
+ "restart": session.cmd_cycle,
260
+ "reload": session.cmd_cycle,
261
+ "cycle": session.cmd_cycle,
262
+ "upgrade": session.cmd_upgrade,
263
+ "proxy": transports.cmd_proxy,
264
+ "upterm-proxy": transports.cmd_upterm_proxy,
265
+ }
266
+
267
+ handler = handlers.get(args.command)
268
+ if handler is None:
269
+ parser.print_help(sys.stderr)
270
+ return 2
271
+ return handler(args)
272
+
273
+
274
+ if __name__ == "__main__":
275
+ sys.exit(main())