# How witr Detects and Identifies SSH Sessions and Remote Terminal Connections from Source Code

> Discover how witr detects SSH sessions and remote terminal connections by analyzing process ancestry and environment variables. Learn its reliable identification methods.

- Repository: [Pranshu Parmar/witr](https://github.com/pranshuparmar/witr)
- Tags: how-to-guide
- Published: 2026-08-10

---

**witr detects SSH sessions by combining process ancestry inspection (checking for `sshd` in parent processes) with fallback environment variable checks (`SSH_CONNECTION`, `SSH_CLIENT`, `SSH_TTY`) to reliably identify remote terminal connections even when environment data is partially stripped.**

The `witr` project implements a robust source detection system written in Go that answers the question "Where am I running?" The SSH detection logic in this repository demonstrates how to programmatically distinguish local shells from remote sessions. This article examines the actual implementation in `pranshuparmar/witr`, revealing how the tool inspects process trees and environment variables to build accurate session descriptions.

## Core SSH Detection Strategy

The detection algorithm follows a two-tier approach designed for reliability across different execution contexts:

1. **Primary check**: Walk the process ancestry looking for `sshd`
2. **Secondary check**: Parse SSH-specific environment variables when ancestry fails

This dual-method design handles common scenarios where `sudo`, `su`, or containerization might strip environment variables while preserving enough process metadata for detection.

## How Process Ancestry Detection Works

The `detectSSH` function in [`internal/source/ssh.go`](https://github.com/pranshuparmar/witr/blob/main/internal/source/ssh.go) implements the primary detection mechanism by examining the process tree that launched the current program.

### Ancestry Walking Implementation

```go
// Conceptual flow based on witr's internal/source/ssh.go
func detectSSH(ancestry []model.Process) *model.Source {
    hasSSHD := false
    
    for _, proc := range ancestry {
        if proc.Executable == "sshd" {
            hasSSHD = true
            break
        }
    }
    
    if !hasSSHD {
        // Fall back to environment variable detection
        return detectSSHEnv()
    }
    
    // Build source description from SSH connection data
    return &model.Source{
        Type:        model.SourceSSH,
        Description: buildSSHDescription(),
    }
}

```

The `ancestry` slice is populated by `proc.GetAncestry()`, implemented in [`internal/proc/process.go`](https://github.com/pranshuparmar/witr/blob/main/internal/proc/process.go) with operating-system-specific variants. This function walks from the current process ID up to the init system, collecting executable names at each step.

### Why Ancestry Detection Matters

| Scenario | Environment Variables | Ancestry Detection |
|----------|----------------------|-------------------|
| Direct SSH login | Present | ✓ Works |
| `ssh` → `sudo -i` | Stripped | ✓ Still detects `sshd` parent |
| `ssh` → `su` | Modified | ✓ Still detects `sshd` parent |
| Container with `--pid=host` | Missing | ✓ Detects host's `sshd` |
| Container without host PID | Missing | ✗ Falls back to env vars |

## Environment Variable Fallback Detection

When process ancestry does not reveal `sshd`, the detection logic examines three environment variables defined by OpenSSH:

| Variable | Format | Information Extracted |
|----------|--------|----------------------|
| `SSH_CONNECTION` | `client_ip server_ip client_port server_port` | Remote IP address, connection endpoints |
| `SSH_CLIENT` | `client_ip client_port server_port` | Remote IP address (simpler format) |
| `SSH_TTY` | `/dev/pts/N` | Pseudo-terminal device name |

According to the source code analysis, `detectSSH` uses `SSH_CONNECTION` preferentially when building session descriptions because it provides the most complete connection metadata, including the remote IP address that witr displays to users.

### Environment Parsing Logic

```go
// Based on detection flow in internal/source/ssh.go
func detectSSHEnv() *model.Source {
    conn := os.Getenv("SSH_CONNECTION")
    client := os.Getenv("SSH_CLIENT")
    tty := os.Getenv("SSH_TTY")
    
    // Must have at least one SSH indicator
    if conn == "" && client == "" && tty == "" {
        return nil
    }
    
    // Prefer SSH_CONNECTION for IP extraction
    remoteIP := extractIP(conn)
    if remoteIP == "" {
        remoteIP = extractIP(client)
    }
    
    user := os.Getenv("USER")
    host, _ := os.Hostname()
    
    return &model.Source{
        Type: model.SourceSSH,
        Description: fmt.Sprintf("SSH session from %s (%s@%s)", 
            remoteIP, user, host),
    }
}

```

## Integration with the Detection Pipeline

The SSH detector integrates into witr's broader source detection system through [`internal/source/detect.go`](https://github.com/pranshuparmar/witr/blob/main/internal/source/detect.go). This orchestration file calls multiple specialized detectors in priority order and returns the first successful match.

### Detection Orchestration

```go
// Representative structure from internal/source/detect.go
func Detect(ancestry []model.Process) *model.Source {
    // Priority-ordered detection attempts
    detectors := []func([]model.Process) *model.Source{
        detectContainer,  // Docker, containerd, etc.
        detectSSH,        // SSH sessions
        detectSystemd,    // systemd units
        detectWSL,        // Windows Subsystem for Linux
        // ... additional detectors
    }
    
    for _, detector := range detectors {
        if src := detector(ancestry); src != nil {
            return src
        }
    }
    
    return nil // Unknown source
}

```

SSH detection typically executes after container detection because containers propagated over SSH may want to identify as the container environment rather than the SSH transport.

## Source Model and Type Constants

The detection results are typed through `model.Source`, defined in [`pkg/model/source.go`](https://github.com/pranshuparmar/witr/blob/main/pkg/model/source.go). This structure standardizes how witr represents execution contexts across all detector implementations.

```go
// From pkg/model/source.go
package model

type SourceType string

const (
    SourceSSH       SourceType = "ssh"
    SourceDocker    SourceType = "docker"
    SourceSystemd   SourceType = "systemd"
    SourceWSL       SourceType = "wsl"
    // ... additional types
)

type Source struct {
    Type        SourceType
    Description string
    // Additional metadata fields
}

```

The `SourceSSH` constant enables type-safe switching throughout the codebase, while the `Description` field carries human-readable context like "SSH session from 203.0.113.42 (alice@myhost)".

## Practical Usage and Verification

### CLI Output Example

When running witr with verbose output, users see automatically detected session information:

```bash
$ ssh alice@remote-server
alice@remote-server:~$ witr --verbose
...
Source: SSH session from 198.51.100.22 (alice@remote-server)

```

### Programmatic Verification

For applications building on witr's detection logic:

```go
package main

import (
    "fmt"
    "log"
    "os"
    
    "github.com/pranshuparmar/witr/internal/proc"
    "github.com/pranshuparmar/witr/internal/source"
)

func main() {
    ancestry, err := proc.GetAncestry()
    if err != nil {
        log.Fatalf("Failed to get process ancestry: %v", err)
    }
    
    src := source.Detect(ancestry)
    if src == nil {
        fmt.Println("Running in unknown/localhost environment")
        os.Exit(0)
    }
    
    fmt.Printf("Detected source type: %s\n", src.Type)
    fmt.Printf("Description: %s\n", src.Description)
    
    if src.Type == "ssh" {
        fmt.Println("⚠️  Running via remote connection - " +
            "consider connection stability for long operations")
    }
}

```

## Platform Coverage and Edge Cases

The detection implementation accounts for platform differences in process inspection:

- **Linux**: Parses `/proc/[pid]/stat` and `/proc/[pid]/status` for parent relationships
- **macOS**: Uses `sysctl` with `KERN_PROCINFO` and `libproc`
- **Windows**: Implements alternative detection primarily through environment variables due to different process semantics
- **BSD variants**: Similar to Linux with `/proc` or `kvm` interfaces

The environment variable fallback proves especially valuable on Windows, where OpenSSH's native port sets the same `SSH_CONNECTION` and `SSH_CLIENT` variables as Unix systems.

## Summary

- **Process ancestry is the primary detection method**: [`internal/source/ssh.go`](https://github.com/pranshuparmar/witr/blob/main/internal/source/ssh.go) walks parent processes seeking `sshd`, making detection resilient against `sudo` and `su` environment stripping.
- **Environment variables provide reliable fallback**: `SSH_CONNECTION`, `SSH_CLIENT`, and `SSH_TTY` supply session metadata when process inspection is unavailable or inconclusive.
- **[`internal/source/detect.go`](https://github.com/pranshuparmar/witr/blob/main/internal/source/detect.go) orchestrates priority-ordered detection**: SSH detection integrates into a broader system that prioritizes more specific contexts like containers.
- **`model.Source` standardizes results**: The type system in [`pkg/model/source.go`](https://github.com/pranshuparmar/witr/blob/main/pkg/model/source.go) ensures consistent handling across witr's CLI, API, and library interfaces.

## Frequently Asked Questions

### How does witr handle SSH sessions inside containers?

When a container runs with `--pid=host` or equivalent, witr's process ancestry detection can see the host's `sshd` and identify the SSH context. Without host PID namespace access, the container appears isolated and detection falls back to environment variable inspection, which will fail if variables were not propagated during `docker exec` or similar operations.

### Can witr detect other remote terminal protocols like Mosh or Telnet?

The current implementation specifically targets OpenSSH's process and environment signatures. Mosh uses different environment variables (`MOSH_CONNECTION`) and a distinct wrapper architecture that would require separate detection logic. Telnet sessions lack standardized environment markers, making reliable programmatic detection substantially more difficult.

### What happens when `SSH_CONNECTION` contains IPv6 addresses?

According to the source structure, witr extracts the remote IP component from `SSH_CONNECTION` using standard string parsing. IPv6 addresses in bracketed notation (e.g., `[2001:db8::1]`) are handled by the formatting logic in `buildSSHDescription()`, ensuring the display description remains readable regardless of address family.