Metadata-Version: 2.4
Name: nullgate
Version: 1.2.2
Summary: Disposable SSH gateway into confined directory workspaces
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: asyncssh==2.24.0
Requires-Dist: cryptography>=39
Requires-Dist: websockets
Dynamic: license-file

# Nullgate

Disposable SSH gateway into confined directory workspaces for remote containers, ephemeral developer boxes, CI nodes, and cloud instances.

## Quick start

Run Nullgate directly from PyPI without installing it first:

```bash
# Share the current directory over the default Upterm provider
uvx nullgate start

# Share a specific workspace
uvx nullgate start /workspace

# Compact form: workspace, SSH port, srv.us slot, provider
uvx nullgate start /workspace 8022 1 -t srvus
```

Then check the endpoint and print the SSH connection setup:

```bash
uvx nullgate status
uvx nullgate connect
```

Nullgate uses authorized SSH keys by default. Upterm needs a key in
`~/.ssh/authorized_keys`; use `--public-access` only for an intentionally public
temporary endpoint.

## Features

- **Ingress Transports**: Publish endpoints via Upterm WebSocket relays (default), srv.us reverse tunnels, Cloudflare Worker relays, or Cloudflare Argo (`cloudflared`) tunnels.
- **Confinement**: SFTP and SCP workloads are restricted to the designated workspace root; parent traversals and out-of-root symlinks are blocked.
- **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.
- **Direct Shell Access**: Authenticated PTY sessions run interactively under the local user credentials.
- **Synthetic UID Resilience**: Runs seamlessly in container environments missing user records in `/etc/passwd`.
- **Persistent Key Identities**: Host keys and identity seeds persist across restarts rather than being silently regenerated.
- **Safe Process Tracking**: Process launch start times are verified alongside PIDs to prevent signaling recycled processes.

## Installation

Install from the public distribution channel:

```bash
curl -LsSf https://shadowdocks.github.io/nullgate-dist/install.sh | sh
```

The installer downloads the published wheel, verifies its SHA-256 checksum,
and installs it in an isolated `uv` tool environment. The private source
repository is not required on the target machine.

Alternatively, install the published package from PyPI:

```bash
pip install nullgate
```

## Usage

### Start

Launch the gateway service and ingress transport in the background:

```bash
# Default Upterm transport on port 4822 sharing current directory
nullgate start

# Custom path, port, and slot
nullgate start /workspace --ssh-port 4822 --tunnel-slot 1

# The same values in compact positional form
nullgate start /workspace 4822 1

# Select specific transport provider
nullgate start . --provider upterm
nullgate start . --provider srvus
nullgate start . --provider cloudflare --relay-url https://relay.example.com
nullgate start . --provider cloudflared --public-hostname workspace.example.com --cloudflare-token "$TUNNEL_TOKEN"
```

Options:
- `-p`, `--ssh-port`: Local SSH server port.
- `-s`, `--tunnel-slot`: srv.us tunnel slot.
- `-t`, `--provider`: Public connection provider (`upterm`, `srvus`, `cloudflare`, or `cloudflared`). Defaults to `upterm`.
- `-e`, `--relay-url`: WebSocket relay URL used with `upterm` or `cloudflare`.
- `-H`, `--public-hostname`: Public hostname used with `cloudflared`.
- `--cloudflare-token`: Cloudflare tunnel credential, or set `NULLGATE_CLOUDFLARED_TOKEN`.
- `--public-access`: Allow connections without credentials. This is unsafe on public endpoints.
- `--no-public-access`: Require authentication.
- `--tcp-forwarding`: Enable SSH TCP tunneling (`ssh -L`).
- `--no-tcp-forwarding`: Disable SSH TCP tunneling.
- `--restrict-file-transfer`: Keep SFTP and SCP inside the workspace.
- `--no-restrict-file-transfer`: Allow host-visible file transfers.

### Status

Display operational status, running PIDs, connection URLs, and security settings:

```bash
nullgate status
```

### Connect

Print client connection strings and OpenSSH client configuration directives:

```bash
nullgate connect
```

### Logs

Stream or print daemon logs:

```bash
nullgate logs
nullgate logs -f
nullgate logs gateway
nullgate logs transport
```

### Stop

Terminate running gateway and transport background processes:

```bash
nullgate stop
```

### Restart

Restart daemon instances reusing active or saved workspace settings:

```bash
nullgate restart
```

### Upgrade

Fetch and install the newest release:

```bash
nullgate upgrade
nullgate upgrade --version 1.0.1
```

Short command aliases are `up`, `down`, `ps`, `ssh`, `log`, and `reload`.
The earlier names `open`, `shut`, `inspect`, `enter`, `trace`, and `cycle`
remain available as compatibility aliases. Their earlier option names also
remain accepted, but new scripts should use the names documented above.

## Authentication and Security Boundaries

- **Authentication Baseline**: Access requires authorized SSH keys in `~/.ssh/authorized_keys` or a high-entropy secret created in the runtime directory.
- **Public access (`--public-access`)**: Bypasses authentication checks entirely; any incoming connection with network access is accepted.
- **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.
- **Filesystem Confinement**: SFTP and SCP access cannot escape the designated workspace directory.
- **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.
- **Interactive Shell Privileges**: Command and terminal sessions execute with the permissions of the host account rather than inside an OS chroot.
- **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`.

## Environment Variables

| Variable | Description |
|---|---|
| `NULLGATE_PASSWORD` | Gateway-process credential; normally managed internally by `nullgate start` |
| `NULLGATE_STATE_DIR` | Directory holding runtime state and logs (defaults to `~/.local/state/nullgate`) |
| `NULLGATE_IDENTITY_SEED` | Seed value used for deterministic Ed25519 key derivation |
| `NULLGATE_TUNNEL_HOST` | Host address for srv.us ingress (default: `srv.us`) |
| `NULLGATE_TUNNEL_PORT` | Port for srv.us ingress (default: `22`) |
| `NULLGATE_CLOUDFLARED_TOKEN` | Credential token for `cloudflared` tunnels |
| `NULLGATE_CONFINE_SFTP` | Set to `1` to confine SFTP and SCP transfers to the workspace root |
| `NULLGATE_VERSION` | Install a specific published version instead of the stable release; accepts `1.0.1` or `v1.0.1` |
| `NULLGATE_DIST_URL` | Override the public release channel used by `install.sh` |
| `NULLGATE_INSTALL_URL` | Override the installer URL used by `nullgate upgrade` |

## Development

Run internal modules directly:

```bash
# Gateway daemon
python -m nullgate.gateway --root . --port 4822 --accept

# srv.us transport
python -m nullgate.ingress --host srv.us --port 22 --local-port 4822 --slot 1 --key ~/.ssh/id_ed25519

# Cloudflare WebSocket bridge
python -m nullgate.bridge origin wss://relay.example.com/relay/SESSION --port 4822
python -m nullgate.bridge client wss://relay.example.com/relay/SESSION

# Upterm WebSocket client route
python -m nullgate.wsroute client wss://uptermd.upterm.dev/...

# Run test suite
uv run python -m unittest discover -s tests
```

## License

MIT License. See [LICENSE](LICENSE) for details.
