Editors and SSH

Every session has an SSH server, so the editor you already use can work inside it: VS Code, Cursor and Windsurf over Remote-SSH, JetBrains IDEs through Gateway, or a plain terminal. This page covers each of them, the SSH config ciresk dev manages for you, port forwarding, VS Code in the browser, your dotfiles, and what your organization's policy can turn off.

Error codes on this page link to their entry in troubleshooting.md.

There are no SSH keys to manage. ciresk dev connects through its per-machine daemon with your Ciresk sign-in, and pins each session's host key for you, so the first connection asks no questions.

Hosts and the managed SSH config

Every session has two SSH host names:

HostWhat it opens
<label>.dev.cireskThe read-write shell (shell): the workspace, the session's services on 127.0.0.1, your egress.
<label>-ro.dev.cireskThe read-only shell (shell-ro): its own container with the workspace mounted read-only, a terminal and file reads, no route to the session's services.

<label> is the session's label, as ciresk dev status prints it. Both containers start with the first connection or ciresk dev exec and stop after the last one closes (30 minutes by default; the template's shell.idle_stop).

Terminal
ciresk dev ssh-config              # write or update the managed block in ~/.ssh/config
ciresk dev ssh-config --dry-run    # show the change without writing it
ciresk dev ssh-config --remove     # take the block out again

ssh-config writes one block at the end of ~/.ssh/config, between two marker lines; edits inside it are replaced the next time. It names both hosts of every live session you own or that is shared with you, so your editor's host picker lists them, and one Host *.dev.ciresk entry with these options:

OptionValue
ProxyCommandThe ciresk-dev binary by its absolute path, with ssh --stdio %h: it carries the connection through the daemon.
Userciresk (every shell runs as uid 1000 whatever the user).
UserKnownHostsFileA known-hosts file ciresk dev keeps, outside your own ~/.ssh/known_hosts.
StrictHostKeyCheckingyes. Each session's key is written before the first byte is forwarded, and replaced when the session moves to another worker.
ConnectTimeout, ServerAliveInterval0 and 15: a session that is still starting is waited for, up to 10 minutes.
ForwardAgent, ForwardX11no.

ciresk dev code writes the block too, so you need ssh-config only for an editor or tool you start yourself. Run it again after you start a new session, so the picker lists it; the wildcard entry already serves any label.

VS Code, Cursor and Windsurf

Terminal
ciresk dev code                        # VS Code, at the directory you are in
ciresk dev code apps/web               # a directory relative to here
ciresk dev code --editor cursor
ciresk dev code --editor windsurf

ciresk dev code writes the managed block and opens the editor with Remote-SSH on <label>.dev.ciresk, at the same directory inside the session's /workspace. In a large monorepo, open the part you work on rather than the root, so the editor's file watchers and indexer stay small. The template's ide.exclude adds to the editor's watcher and search excludes (templates.md).

FlagDefaultWhat it does
<path>the current directoryThe directory to open, relative to where you are. It must stay inside the worktree.
--editorcodecode (VS Code), cursor or windsurf. The command must be on your PATH; otherwise editor_not_found (exit 1).

The session has no internet access of its own, so the editor cannot download its server inside it. Before the first connection, set this in VS Code's or Cursor's settings, so the server is downloaded on your machine and copied over:

settings.json
"remote.SSH.localServerDownload": "always"

The copy is an upload into the session, which your organization's upload policy must allow. The editor's server runs in the read-write shell, so size it with the template's shell.memory, or ide.memory when the editor needs more.

A view member has no editor: ciresk dev code refuses with not_allowed (exit 4), and ciresk dev ssh --read-only is the terminal they get.

JetBrains

Terminal
ciresk dev code --jetbrains                         # IntelliJ IDEA
ciresk dev code --jetbrains --ide goland apps/api   # GoLand, on one directory

--ide is one of idea (the default), goland, pycharm, webstorm, rider, clion and rustrover. The command writes the managed block and opens JetBrains Gateway with a jetbrains-gateway://connect#... link that names the session's host (<label>.dev.ciresk), the directory and the IDE. It prints the link either way. If Gateway is not installed or the link does not open, open the link yourself, or connect from Gateway's SSH screen to the same host; Gateway uses the ProxyCommand from ~/.ssh/config.

The session cannot download the IDE's backend. On the first connection, choose "Upload installer from local" in Gateway, so the backend is downloaded on your machine and copied over; like VS Code's server, the copy needs your organization's upload policy. As for the other editors, a view member has no IDE.

Plain SSH

Terminal
ciresk dev ssh                       # a shell in this worktree's session
ciresk dev ssh --read-only           # the read-only shell
ciresk dev ssh -- ls /workspace      # one command instead of a login shell
ciresk dev ssh <label>               # another session of yours, or one shared with you

ciresk dev ssh runs your system's ssh (OpenSSH) with the managed options on its command line, so it works before you ran ssh-config. After ssh-config, ssh <label>.dev.ciresk and tools built on SSH (scp, sftp, rsync) work as well:

  • Copying files out of the session (sftp, scp and rsync reads) needs your organization's download policy.
  • Copying files in needs the upload policy, and works only in the read-write shell.
  • Only LANG, LC_* and TERM are passed from your environment.

The read-only shell is for following along: the workspace is read-only, its home is small and cannot run an uploaded program, and it has no route to the session's services or ports.

Port forwarding

The ports a template declares need no forwarding. Your daemon serves every http port at http://<label>.localhost:<port> and every tcp port on a fixed local port, which ciresk dev status prints (templates.md).

To reach a declared port another way, use an SSH local forward through the read-write host:

Terminal
ssh -N -L 5433:127.0.0.1:5432 app.dev.ciresk

The port to forward to is the one the service listens on inside the session (the port's target, 5432 for a Postgres declared as {port: 55437, target: 5432, kind: tcp}). A forward reaches only the session's own loopback (127.0.0.1, ::1 or localhost), and only a port the template declares as http or tcp; an internal port or a port the template does not declare is refused. A connection holds at most 8 forwards at once. Forwards need your organization's port_forward policy and the read-write host, so a view member has none. Remote forwards (-R), agent forwarding and X11 are always refused.

Forwarding a port the template does not declare is not available yet. Declare it in the template instead.

VS Code in the browser

A session can also open VS Code in your browser, from the session's page in the console, with nothing installed on your machine. The "VS Code" button shows when:

  • your organization has turned on web_ide (it is off by default), and its browser view (web) is not off;
  • the session is ready or degraded;
  • you are its owner or a member.

The editor is read-write for the owner and use members when your organization's web ceiling is use, and read-only otherwise, for view members always. Uploads and downloads in it follow your organization's upload and download policy. The template can pin your organization's own editor image with its extensions built in (ide.image), and name your own Open VSX mirror for more extensions (ide.gallery); see templates.md.

Dotfiles

Terminal
ciresk dev dotfiles set https://github.com/you/dotfiles
ciresk dev dotfiles set https://github.com/you/dotfiles --install script/setup
ciresk dev dotfiles set --repo https://github.com/you/private-dotfiles --token-stdin < token.txt
ciresk dev dotfiles show
ciresk dev dotfiles rm

Set your dotfiles repository once. Every new session you own then clones it into its read-write shell when that shell first starts (your first ciresk dev ssh, editor connection or ciresk dev exec), before it accepts the connection, and sets it up:

  1. git clone --depth 1 of the repository, through the session's own egress, within 40 seconds.
  2. The script you named with --install, else install.sh, else bootstrap.sh, run from the clone within 60 seconds. With none of them, every top-level dot-file of the repository (but .git) is symlinked into your home; a real directory of the same name already there is left alone.

This happens once per session. A session that is already running keeps its shell: set and rm apply to sessions you start afterwards (ciresk dev up --recreate for this worktree's). Members you share a session with keep their own shell and never run your dotfiles, and the read-only shell never runs them.

A failure never blocks the session. ciresk dev status and ciresk dev diagnostics show what happened, ciresk dev logs shell has the line the shell wrote (ciresk-dotfiles: applied ... or ciresk-dotfiles: failed: ...), and an install script's own output is in ~/.ciresk/dotfiles.log in the shell.

What set accepts:

FieldRule
<URL> or --repo <URL>An https:// URL with a host name and a path, at most 512 bytes. No user name or password, query, fragment, IP address or port other than 443, and not a model or tool vendor's host. Give it as the argument or with --repo, not both.
--install <path>A path inside the repository, at most 128 bytes of letters, digits, ., _, / and -, with no ...
--token-stdinReads a token for a private repository from stdin.

An ssh:// or git@ address, or a URL that carries credentials, is refused with dotfiles_private_unsupported. Use the repository's https:// URL; a private one also needs a token.

Private repositories

Terminal
ciresk dev dotfiles set --repo https://github.com/you/private-dotfiles --token-stdin < token.txt

Pipe a read-only token scoped to that one repository on stdin; it is never a command-line argument, and --token-stdin with nothing on stdin is refused before anything is sent. The token is 16 to 8192 bytes with no control characters (dotfiles_invalid otherwise).

  • Where it is kept. In your installation's secret store, never in the session or in any answer, log or audit record. The installation must keep session secrets: without a secret store the token is refused with secrets_unavailable (the repository itself is still saved), and the installation's operator has to configure one.
  • How it is used. When the shell clones, the session holds only a placeholder for the token, and the session's egress proxy puts the real token on requests to the repository's host, and that host only. Any process of yours in the session can still use it against that host, which is why the token should be scoped to the one repository.
  • Who can use it. Only you can store it, never an agent, and only sessions a person starts use it. In a session an agent starts, your dotfiles are skipped with the reason agent_session.
  • When it goes. Pointing your dotfiles at a repository on another host, or ciresk dev dotfiles rm, deletes the token. Running set --token-stdin again replaces it.

ciresk dev dotfiles show says credential stored when a token is kept; it never shows the token. If the clone fails because the token did not reach the session, the shell's ciresk-dotfiles: failed: ... line says so.

ciresk dev dotfiles show prints your repository, its install script, and whether a new session of yours would apply it now. When it would not, it names the reason and the change that fixes it:

ReasonWhat refuses the cloneFix
policy_offYour organization's dotfiles.access is deny.An admin sets it to allow.
host_not_allowedThe host is not in your organization's dotfiles.hosts.An admin lists the host.
vendorThe host is a model or tool vendor.Use a repository on a git host.
egress_ceilingYour organization's egress ceiling does not cover the host on port 443 (for an agent's session, the agent ceiling too).An admin adds the host (egress.md).
egress_deniedThe installation's egress.yaml has no row for the host on port 443 for product dev-session, the product the session's own traffic goes out as.The installation's operator adds the row.
agent_sessionThe repository has a token, and an agent started the session.Start the session yourself (ciresk dev up).

A skip never refuses ciresk dev up: the session starts with the plain shell. With dotfiles.access: deny or a host outside dotfiles.hosts, set itself is refused too (dotfiles_off, dotfiles_host_not_allowed, exit 4).

What your organization's policy controls

Admins set these in the organization's dev policy (admin.md); ciresk dev policy show shows the ones that apply to you. A change that narrows access reaches running sessions within about 20 seconds.

Policy fieldDefaultWhen it is deny or off
sshallowNo SSH at all: ciresk dev ssh, editors, JetBrains and the browser terminal are refused (policy_ssh_denied, exit 4).
ssh_execallowSSH commands without a terminal are refused, which also stops Remote-SSH editors and JetBrains Gateway, and ssh <host> <command>. An interactive ciresk dev ssh still works.
uploadallowNo files into the session over SSH, so editors and JetBrains cannot copy their server in.
downloadallowNo files out of the session over SSH.
port_forwardallowNo SSH port forwards.
clipboardallowNo copying from a session terminal to your clipboard.
web_ideoffNo VS Code in the browser (on turns it on).
webviewoff removes the browser view, including VS Code in the browser; use makes the browser editor read-write for owners and use members.
dotfiles.access, dotfiles.hostsallow, any hostNo dotfiles, or dotfiles only from the listed hosts.