Skip to content

Inspect a study over SSH

Inspect DICOM files on a remote machine without copying them to a third-party viewer or exposing the server on the network.

  • dcmview is installed on the remote machine that can read the DICOM files.
  • You can connect to that machine with SSH.
  • A chosen port is available on both machines, or you can adapt the forwarding command.

On the remote machine, choose a fixed loopback port so the forwarding command is predictable:

Terminal window
dcmview --no-browser --host 127.0.0.1 --port 8888 /path/to/study

Wait for the startup message:

dcmview: (on a remote server? run on your local machine: ssh -L 8888:localhost:8888 user@host)
dcmview: then open http://localhost:8888/#token=<session-token>
dcmview: server running at http://127.0.0.1:8888/#token=<session-token>
dcmview: press Ctrl+C to stop

The #token=… fragment is this session’s access token. Every API request needs it, so the bare address without the fragment does not load the study. Treat the printed links as credentials.

The server remains bound to the remote loopback interface. Do not replace the host with 0.0.0.0 for this workflow: the token does not encrypt anything, so over plain HTTP on a network interface the token and the DICOM data are readable in transit.

On your local machine, open a second terminal and run the printed command, replacing user@host with your SSH destination:

Terminal window
ssh -L 8888:localhost:8888 user@remote-server

Keep the SSH session open, then copy the then open link from the remote terminal into your local browser, with its #token=… fragment:

http://localhost:8888/#token=<session-token>

The browser connects to the local end of the tunnel, SSH carries requests to the remote loopback server, and DICOM files remain on the remote machine. The viewer removes the token from the address bar once the page has loaded and keeps it for that tab, so reloading the tab still works.

Run the forwarding command on your own machine, not on the remote server. dcmview no longer offers the --tunnel options that tried to start forwarding from the serving machine.

Omit --port when a fixed remote port is inconvenient:

Terminal window
dcmview --no-browser /path/to/study

The printed ssh -L hint and then open link already contain the assigned port. For example, if dcmview binds port 43127:

Terminal window
ssh -L 43127:localhost:43127 user@remote-server

Then open the printed http://localhost:43127/#token=… link.

Use a private Unix socket on a shared server

Section titled “Use a private Unix socket on a shared server”

A loopback port can be reached by every account on the remote machine. The access token keeps those accounts out of the API, but on Linux and macOS you can also stop them from connecting at all by serving the viewer through a Unix domain socket:

Terminal window
dcmview --unix-socket ~/dcmview/scan.sock /path/to/study
Startup message
dcmview: listening on /home/alice/dcmview/scan.sock
dcmview: on your local machine run: ssh -L 8080:/home/alice/dcmview/scan.sock user@host
dcmview: then open http://localhost:8080/#token=<session-token>
dcmview: press Ctrl+C to stop

On your local machine, run the printed command with your SSH destination, then open the printed link with its #token=… fragment:

Terminal window
ssh -L 8080:/home/alice/dcmview/scan.sock user@remote-server

You can choose another local port; change it in both the command and the link.

Rules for the socket path:

  • Give an explicit path. There is no default socket location.
  • Directory. The socket’s directory must be owned by your account and must not be writable by group or others. If it is missing, dcmview creates that one directory with mode 0700; its own parent must already exist. An unsafe directory is refused before anything in it is touched.
  • Permissions. The socket is created with mode 0600, and a connection from another user ID is dropped.
  • Lock file. A scan.sock.lock file beside the socket is held while the viewer runs, so a second launch on the same path reports that the socket is already in use instead of replacing it. The directory needs a filesystem that supports file locks. Some network filesystems do not; use a local directory such as one under $XDG_RUNTIME_DIR.
  • Existing entries. A regular file, symlink, or other non-socket entry at the path is refused and left untouched. A stale socket left by a forced kill is replaced by the next launch on that path.
  • Cleanup. The socket and its lock file are removed when the viewer shuts down.

Socket mode has these limits:

  • It needs OpenSSH 6.7 or newer on both ends to forward a Unix socket.
  • It is available on Linux and macOS only. Windows builds reject the option.
  • It is not available from Python view() or in the VS Code extension, and a dcmview --unix-socket command in a VS Code terminal always runs the local viewer instead of opening a webview.
  • It conflicts with --host and --port, and it never opens a browser, with or without --no-browser.
  • The access token is still required. The socket limits who can connect; it does not replace the token.

Add --timeout to stop a forgotten viewer. The idle clock starts only after the scan finishes, so a long scan of a large directory does not count against it:

Terminal window
dcmview --no-browser --port 8888 --timeout 1800 /path/to/study

The viewer loads its assets and API with page-relative URLs, so it also works behind a reverse proxy that serves it under a path prefix, such as a Jupyter server proxy route. The proxy must strip the prefix before forwarding and serve the page with a trailing slash, such as .../8888/. Append the #token=… fragment of the printed link to the proxied address so the viewer can authenticate. The proxy must pass the Authorization header through unchanged; if it uses that header for its own login, such as HTTP Basic authentication, start dcmview with --no-token instead.

If the proxy already authenticates every request, you can start dcmview with --no-token to turn the token check off. dcmview prints a warning, because in that mode anything that can reach the listener directly can use the API.

Press Ctrl+C in the remote dcmview terminal, then close the SSH forwarding session. The temporary server, its token, and in-memory state are removed.

  • If the page says This viewer needs its access link, you opened the address without the #token=… fragment. Paste the full printed link into the same tab.
  • If the page says The viewer restarted, the tab belongs to an earlier dcmview process. Paste the new link from the terminal into the same tab.
  • If the local port is busy, use a different first port: ssh -L 8899:localhost:8888 …, then open http://localhost:8899/ followed by the same #token=… fragment.
  • If the browser cannot connect, confirm that both processes are still running and that the forwarded remote port matches the dcmview startup URL.
  • If the viewer shows Disconnected, restore the dcmview process or SSH session, then choose Retry.
  • If server startup fails, select another remote port or use the default automatic port.
  • If --unix-socket fails to start, see socket troubleshooting.
  • It does not encrypt traffic. SSH forwarding does; a non-loopback bind over plain HTTP does not.
  • When dcmview opens the browser itself, the operating system’s opener receives the launch link, token included, as a process argument that other local users can read on some systems. On a shared machine, keep --no-browser and open the printed link, or use --unix-socket.
  • A token fixed with DCMVIEW_TOKEN is not rate-limited; use a long random value.

Anyone who has the token and can reach the listener can access pixels, tags, file paths, identifiers, and annotations. See ephemeral state and network security for the complete boundary.