<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Arturo Ramírez-Montesinos Krogulski</title>
    <description>The latest articles on DEV Community by Arturo Ramírez-Montesinos Krogulski (@arturormk).</description>
    <link>https://dev.to/arturormk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2940313%2Ff8fded27-1f4c-484c-ab4d-838f0615f4a2.png</url>
      <title>DEV Community: Arturo Ramírez-Montesinos Krogulski</title>
      <link>https://dev.to/arturormk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arturormk"/>
    <language>en</language>
    <item>
      <title>StaSSH: My SSH Client Is Fine. The Rest of My SSH Life Wasn't</title>
      <dc:creator>Arturo Ramírez-Montesinos Krogulski</dc:creator>
      <pubDate>Sat, 05 Sep 2026 17:02:06 +0000</pubDate>
      <link>https://dev.to/arturormk/stassh-my-ssh-client-is-fine-the-rest-of-my-ssh-life-wasnt-1jec</link>
      <guid>https://dev.to/arturormk/stassh-my-ssh-client-is-fine-the-rest-of-my-ssh-life-wasnt-1jec</guid>
      <description>&lt;p&gt;I have been using SSH for a very long time.&lt;/p&gt;

&lt;p&gt;SSH itself is not the problem.&lt;/p&gt;

&lt;p&gt;OpenSSH is mature, ubiquitous, scriptable, compatible with just about everything, and already knows how to deal with keys, agents, jump hosts, port forwards, certificates, host verification and all the other things accumulated over decades of people administering Unix machines.&lt;/p&gt;

&lt;p&gt;I had no particular desire to replace any of that.&lt;/p&gt;

&lt;p&gt;My problem was everything &lt;em&gt;around&lt;/em&gt; SSH.&lt;/p&gt;

&lt;p&gt;After enough years and enough computers, your SSH setup stops being a configuration file and starts becoming archaeology.&lt;/p&gt;

&lt;p&gt;Some hosts are in &lt;code&gt;~/.ssh/config&lt;/code&gt;. Some are in &lt;code&gt;/etc/hosts&lt;/code&gt;. Some are in notes. Some are remembered by hostname, some by IP address, some by whatever nickname made sense six years ago. There are jump hosts, special ports, different identities, local forwards, commands you always run after connecting, and machines you vaguely remember require a particular ritual before they will cooperate.&lt;/p&gt;

&lt;p&gt;And, of course, some of this exists on one laptop but not another.&lt;/p&gt;

&lt;p&gt;I wanted somewhere for my SSH life to live.&lt;/p&gt;

&lt;p&gt;That became &lt;strong&gt;StaSSH&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ij5toqrq4llb51x4yzg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ij5toqrq4llb51x4yzg.jpg" alt="GUI Overview" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;StaSSH is a portable, offline-first workspace for SSH. It organizes hosts, folders, notes, tags, identities, jump chains, forwards and reusable workflows, and then lets the system OpenSSH client do the actual connecting.&lt;/p&gt;

&lt;p&gt;Or, put another way:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;StaSSH doesn't replace SSH. It remembers how I use SSH.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  I wanted my SSH collection to travel
&lt;/h2&gt;

&lt;p&gt;Portability was one of the original motivations.&lt;/p&gt;

&lt;p&gt;I use several computers, and not all of them are particularly impressive computers.&lt;/p&gt;

&lt;p&gt;Some are old machines that had been sitting unused for years. I kept them mostly because throwing away working computers felt like turning them into e-waste for no good reason. I eventually installed lightweight Linux systems on some of them, encrypted the disks, and turned them into what I think of as “losable” computers: machines I can take somewhere without being particularly upset if one gets damaged or disappears.&lt;/p&gt;

&lt;p&gt;A fifteen-year-old laptop is still spectacularly overqualified for being an SSH terminal.&lt;/p&gt;

&lt;p&gt;The problem is that modern desktop software increasingly disagrees.&lt;/p&gt;

&lt;p&gt;StaSSH therefore has three frontends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stassh       CLI
stassh-tui   terminal UI
stassh-gui   desktop GUI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The TUI in particular is deliberately light on resources. Rust helps considerably here, and I have also tried to keep open a path for building StaSSH for 32-bit x86 Linux. There are still perfectly functional old machines out there that have been abandoned by much of the modern application stack.&lt;/p&gt;

&lt;p&gt;I don't need an old laptop to run a browser-sized desktop application just so I can remember the name of a server.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;p&gt;The TUI has another feature I particularly wanted. When StaSSH is itself running inside tmux or byobu, I can highlight a host and press &lt;code&gt;t&lt;/code&gt; to open that connection in a new tmux window or byobu tab.&lt;/p&gt;

&lt;p&gt;That turns an old laptop into a remarkably useful little administration station.&lt;/p&gt;

&lt;p&gt;I can keep StaSSH in one tab, open machines into other tabs, jump between them, and never need a desktop environment at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three interfaces, one program
&lt;/h2&gt;

&lt;p&gt;The CLI, TUI and GUI are not three separate implementations that happen to read similar files.&lt;/p&gt;

&lt;p&gt;StaSSH is a Rust Cargo workspace built around a shared crate called &lt;code&gt;stassh-core&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Conceptually, it looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                         stassh-core
                             |
               +-------------+-------------+
               |             |             |
              CLI           TUI           GUI
            stassh      stassh-tui     stassh-gui
               |             |             |
               +-------------+-------------+
                             |
                       system OpenSSH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core owns the important concepts: hosts, folders, storage, identities, search, diagnostics, OpenSSH command generation, jump chains, forwards, Actions and so on.&lt;/p&gt;

&lt;p&gt;The frontends are different ways of operating the same workspace.&lt;/p&gt;

&lt;p&gt;That means I can use the GUI at my main workstation, the TUI on an ancient laptop, and the CLI from a script without maintaining three collections of hosts.&lt;/p&gt;

&lt;p&gt;And underneath all of them is still the SSH implementation I already trust:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/usr/bin/ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;StaSSH constructs the appropriate command, or a temporary OpenSSH configuration when something more complicated is needed, and runs the system client.&lt;/p&gt;

&lt;p&gt;Existing SSH agents still work. Existing keys still work. Known hosts still work. OpenSSH remains OpenSSH.&lt;/p&gt;

&lt;p&gt;This was an explicit design decision. I was trying to organize SSH, not reimplement it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The portable part and the local part are different things
&lt;/h2&gt;

&lt;p&gt;One of the problems with carrying an SSH setup between computers is that not everything &lt;em&gt;should&lt;/em&gt; be portable.&lt;/p&gt;

&lt;p&gt;StaSSH's default configuration lives under:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.ssh/stassh/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with three files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vault.json
local.json
secrets.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;vault.json&lt;/code&gt; describes the portable workspace: folders, hosts, usernames, ports, notes, tags, jump chains, forwarding definitions, Actions and references to identities.&lt;/p&gt;

&lt;p&gt;It is deliberately plain JSON.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;local.json&lt;/code&gt;, on the other hand, answers questions specific to the computer currently running StaSSH.&lt;/p&gt;

&lt;p&gt;An identity in the portable vault can be represented by the fingerprint of its public key. The corresponding private key itself is not stored in the StaSSH vault. &lt;code&gt;local.json&lt;/code&gt; can say where that identity actually lives on this particular machine.&lt;/p&gt;

&lt;p&gt;So one laptop might map an identity to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/arturo/.ssh/work_ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while another computer can map the same fingerprint to a different local path.&lt;/p&gt;

&lt;p&gt;The portable workspace knows &lt;em&gt;which identity&lt;/em&gt; I mean.&lt;/p&gt;

&lt;p&gt;The local configuration knows &lt;em&gt;where this computer keeps it&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The same idea applies to local programs used by StaSSH Actions. An Action can refer to a capability such as a VNC viewer without embedding the executable path used on one particular computer.&lt;/p&gt;

&lt;p&gt;This separation turned out to be one of the more useful architectural decisions in the project.&lt;/p&gt;

&lt;p&gt;Copy the portable workspace to another computer, configure its local mappings, and the meaning of the SSH estate travels without pretending every computer is identical.&lt;/p&gt;

&lt;h2&gt;
  
  
  About passwords
&lt;/h2&gt;

&lt;p&gt;There is also an optional &lt;code&gt;secrets.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I want to be careful about what this is, because &lt;strong&gt;StaSSH is not intended to be a password manager&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Most of the time I connect to my machines with SSH keys, and most routine work does not require a password at all. But occasionally I need to do some administrative work with &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The strange thing about passwords for machines you have personally configured is that there is a category of password you don't exactly &lt;em&gt;remember&lt;/em&gt;, but you recognize instantly when you see it.&lt;/p&gt;

&lt;p&gt;While configuring a particular machine I may have typed its password fifty times. Six months later, among dozens of hosts, I cannot necessarily recall it cold.&lt;/p&gt;

&lt;p&gt;Show it to me once and I think:&lt;/p&gt;

&lt;p&gt;“Oh, right. &lt;em&gt;That&lt;/em&gt; one.”&lt;/p&gt;

&lt;p&gt;And then I remember it for the rest of the session.&lt;/p&gt;

&lt;p&gt;That is the niche &lt;code&gt;secrets.json&lt;/code&gt; is intended to cover: host-associated fallback operational secrets, not your entire digital life.&lt;/p&gt;

&lt;p&gt;Secret values stored there are encrypted; the surrounding metadata should still be considered private. I would not put my banking passwords in it, and I don't recommend anyone else does either.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then there are Actions
&lt;/h2&gt;

&lt;p&gt;Hosts are only half of an SSH workflow.&lt;/p&gt;

&lt;p&gt;Quite often I don't merely want to &lt;em&gt;connect&lt;/em&gt; to a computer. I want to &lt;em&gt;do the thing I normally do with that computer&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That distinction led to StaSSH Actions.&lt;/p&gt;

&lt;p&gt;An Action can do several things around an SSH session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run something locally
        |
        v
prepare values or ports
        |
        v
start SSH, possibly with temporary forwards
        |
        v
run something on the remote machine
        |
        v
launch something locally
        |
        v
cleanup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Described like that, it sounds suspiciously like I have invented a complicated way to avoid typing commands.&lt;/p&gt;

&lt;p&gt;So let me give the example that made the feature worthwhile for me.&lt;/p&gt;

&lt;h3&gt;
  
  
  VNC
&lt;/h3&gt;

&lt;p&gt;Sometimes I want a remote graphical desktop.&lt;/p&gt;

&lt;p&gt;Manually, that can mean starting a VNC server remotely, creating an SSH tunnel, choosing a free local port and then launching the correct viewer against it.&lt;/p&gt;

&lt;p&gt;With a StaSSH Action, I select the host and choose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VNC forwarded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the VNC window appears.&lt;/p&gt;

&lt;p&gt;The Action can start &lt;code&gt;x11vnc&lt;/code&gt; remotely, create a temporary SSH forward from an automatically selected local port to port 5900 on the remote machine, and launch my local VNC viewer against that port.&lt;/p&gt;

&lt;p&gt;StaSSH has not become a VNC client.&lt;/p&gt;

&lt;p&gt;It has simply learned how I use my VNC client.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fopmqzmybj9fziifvyrvf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fopmqzmybj9fziifvyrvf.jpg" alt="GUI Action" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A feature I didn't add
&lt;/h2&gt;

&lt;p&gt;While discussing StaSSH, I realized that it couldn't upload a file to a remote machine.&lt;/p&gt;

&lt;p&gt;The obvious response would have been:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Add file transfer to StaSSH.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That would mean starting down the road toward file browsers, transfer queues, progress interfaces and eventually some sort of built-in SFTP subsystem.&lt;/p&gt;

&lt;p&gt;Instead, I asked the coding agent to write an Action.&lt;/p&gt;

&lt;p&gt;The repository now contains a &lt;strong&gt;Send file to home&lt;/strong&gt; example. The Action invokes a small local helper, lets me choose a file and sends it to the selected host using &lt;code&gt;scp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;StaSSH itself did not acquire an SCP implementation.&lt;/p&gt;

&lt;p&gt;It didn't acquire a file manager either.&lt;/p&gt;

&lt;p&gt;The new workflow was mostly composed out of software that was already there.&lt;/p&gt;

&lt;p&gt;That is increasingly how I think about Actions.&lt;/p&gt;

&lt;p&gt;They are an escape hatch from feature creep.&lt;/p&gt;

&lt;p&gt;I don't particularly want StaSSH to grow its own VNC client, browser, file manager, database frontend and every other application I might conceivably use through an SSH connection.&lt;/p&gt;

&lt;p&gt;Those programs already exist.&lt;/p&gt;

&lt;p&gt;StaSSH can remember how I use them together.&lt;/p&gt;

&lt;p&gt;The first file-transfer Action is intentionally simple. One direction I am considering for a future release—possibly the 1.2 timeframe—is tighter SCP support, particularly so transfers can automatically inherit more of StaSSH's identity and jump-host knowledge.&lt;/p&gt;

&lt;p&gt;But even this simple example demonstrated the point nicely.&lt;/p&gt;

&lt;p&gt;A missing feature did not necessarily require a new subsystem.&lt;/p&gt;

&lt;p&gt;It required a new recipe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Actions are deliberately written as JSON
&lt;/h2&gt;

&lt;p&gt;There is currently no graphical Action designer.&lt;/p&gt;

&lt;p&gt;That is intentional.&lt;/p&gt;

&lt;p&gt;Actions can combine local commands, environment values, port allocation, SSH forwards, remote commands, local applications and cleanup. A GUI capable of expressing every possible combination would either become surprisingly elaborate or gradually restrict the underlying model to whatever the GUI happened to support.&lt;/p&gt;

&lt;p&gt;So Actions are JSON-first.&lt;/p&gt;

&lt;p&gt;There is a &lt;code&gt;HOWTO-Actions.md&lt;/code&gt; file in the repository explaining the schema, template variables, capabilities, forwarding and dry-run workflow.&lt;/p&gt;

&lt;p&gt;You can write one yourself.&lt;/p&gt;

&lt;p&gt;Or, this being 2026, you can give that document to an AI coding agent and describe what you want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read HOWTO-Actions.md and create an Action that...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is also an interesting little experiment in interface design.&lt;/p&gt;

&lt;p&gt;Not every interface necessarily needs to be optimized for a human to author manually.&lt;/p&gt;

&lt;p&gt;A structured format can be easy for a human to inspect, easy for the application to validate, and easy for an AI agent to generate.&lt;/p&gt;

&lt;p&gt;The important part is that the result remains visible.&lt;/p&gt;

&lt;p&gt;It's still JSON. I can read it, change it, put it under version control if appropriate, and run the Action in dry-run mode to see exactly what StaSSH intends to execute before letting it do anything.&lt;/p&gt;

&lt;p&gt;That feels like a much healthier use of AI than hiding another layer of magic behind a button.&lt;/p&gt;

&lt;h2&gt;
  
  
  A desktop workspace when I do want a desktop
&lt;/h2&gt;

&lt;p&gt;None of this means I dislike graphical interfaces.&lt;/p&gt;

&lt;p&gt;On my normal workstation I generally want one.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;stassh-gui&lt;/code&gt; is built with Tauri and gives the same workspace a persistent host tree, inspector/editor panes and embedded terminal sessions.&lt;/p&gt;

&lt;p&gt;Multiple SSH terminals can remain open at once. They can also be arranged into terminal layouts, including equal grids and layouts with one main terminal and smaller surrounding panes.&lt;/p&gt;

&lt;p&gt;There is broadcast input for a layout when I genuinely want to type the same thing into several sessions, terminal find, full-screen panes, and indicators in the host tree showing which machines currently have sessions open.&lt;/p&gt;

&lt;p&gt;The idea is not to turn SSH into a shiny dashboard.&lt;/p&gt;

&lt;p&gt;It is to give an SSH-heavy working session somewhere to live.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw9dv1ay6ab2eb1v2mjim.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw9dv1ay6ab2eb1v2mjim.jpg" alt="Multiple GUI terminals" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The GUI's terminal sessions are real PTYs when not running in simulation mode, and real connections are still made using OpenSSH.&lt;/p&gt;

&lt;p&gt;The desktop interface is another view over the same machinery, not a separate SSH universe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simulation mode
&lt;/h2&gt;

&lt;p&gt;You may have noticed that all of the screenshot instructions in this article use &lt;code&gt;--simulation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That feature exists partly because screenshots of SSH tools are a privacy accident waiting to happen.&lt;/p&gt;

&lt;p&gt;A realistic SSH interface naturally contains exactly the things you don't want to accidentally publish: hostnames, usernames, network addresses, notes, infrastructure topology and potentially terminal output from real machines.&lt;/p&gt;

&lt;p&gt;Both the TUI and GUI therefore have a deterministic simulation mode.&lt;/p&gt;

&lt;p&gt;It uses an in-memory demo workspace with fake folders, hosts, identities, forwards, jump chains, diagnostics and secrets. Simulated terminal sessions behave like tiny scripted SSH shells instead of making real network connections.&lt;/p&gt;

&lt;p&gt;That makes it useful not only for documentation and demonstrations but also for visual regression testing.&lt;/p&gt;

&lt;p&gt;The GUI test suite uses deterministic simulation data with Playwright screenshot baselines, so a feature originally useful for safely showing the program also became useful for checking that its interface hasn't quietly broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  The files are the product
&lt;/h2&gt;

&lt;p&gt;Another deliberate omission is built-in synchronization.&lt;/p&gt;

&lt;p&gt;StaSSH doesn't require an account and doesn't have a cloud backend.&lt;/p&gt;

&lt;p&gt;I already have ways to copy files between computers. Other people use Syncthing, Git, Nextcloud, Dropbox, rsync, a USB stick or whatever system they trust.&lt;/p&gt;

&lt;p&gt;I don't want an SSH organizer to require me to adopt another synchronization service.&lt;/p&gt;

&lt;p&gt;The portable files are meant to be portable &lt;em&gt;as files&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That is decidedly unfashionable software architecture, and I rather like it.&lt;/p&gt;

&lt;p&gt;There is also an important safety detail here: StaSSH frontends keep track of the &lt;code&gt;vault.json&lt;/code&gt; state they loaded. If another program changes the vault on disk, StaSSH will refuse to blindly overwrite it and require a reload first.&lt;/p&gt;

&lt;p&gt;Plain files should not mean careless files.&lt;/p&gt;

&lt;h2&gt;
  
  
  This project was written by an AI
&lt;/h2&gt;

&lt;p&gt;There is one other unusual thing about StaSSH.&lt;/p&gt;

&lt;p&gt;I did not write its code.&lt;/p&gt;

&lt;p&gt;The project is part of an experiment I have been conducting with a different way of developing software. I decide what the program should do, define constraints, make architectural decisions, test it, reject things I don't like, discover missing features and keep steering the project.&lt;/p&gt;

&lt;p&gt;The coding itself is done by an AI coding agent.&lt;/p&gt;

&lt;p&gt;All of it.&lt;/p&gt;

&lt;p&gt;Even if the change would take me one line, I ask the agent to make the change. Mixing my own quick fixes into the code would defeat part of the experiment.&lt;/p&gt;

&lt;p&gt;I have spent decades programming by hand, so this is a strange way to work. But that is precisely why I am interested in exploring it seriously rather than treating AI as autocomplete with better publicity.&lt;/p&gt;

&lt;p&gt;StaSSH itself has provided some nice examples of this relationship.&lt;/p&gt;

&lt;p&gt;I wanted VNC to be one Action.&lt;/p&gt;

&lt;p&gt;The Action machinery evolved around that requirement.&lt;/p&gt;

&lt;p&gt;Then I noticed I wanted to transfer files.&lt;/p&gt;

&lt;p&gt;Instead of telling the agent to build file transfer into StaSSH, I asked whether the existing Action system could express it.&lt;/p&gt;

&lt;p&gt;It could.&lt;/p&gt;

&lt;p&gt;For me, that design decision is still the human part of software development.&lt;/p&gt;

&lt;p&gt;The interesting question was not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can you write an SCP feature?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Should StaSSH have an SCP feature at all?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What StaSSH is not
&lt;/h2&gt;

&lt;p&gt;StaSSH is not a server-monitoring suite.&lt;/p&gt;

&lt;p&gt;It is not Ansible.&lt;/p&gt;

&lt;p&gt;It is not a password manager.&lt;/p&gt;

&lt;p&gt;It is not a proprietary SSH service.&lt;/p&gt;

&lt;p&gt;It doesn't want custody of my private keys.&lt;/p&gt;

&lt;p&gt;It doesn't replace OpenSSH.&lt;/p&gt;

&lt;p&gt;It doesn't require a cloud account.&lt;/p&gt;

&lt;p&gt;And, unless an Action tells it to invoke one, it isn't trying to become my VNC client, browser or file manager either.&lt;/p&gt;

&lt;p&gt;It is much closer to a memory and workbench for the SSH tools I already use.&lt;/p&gt;

&lt;p&gt;That scope is deliberate.&lt;/p&gt;

&lt;p&gt;There is always another remote-administration feature that could be added. At some point an SSH organizer can quietly turn into an operating environment.&lt;/p&gt;

&lt;p&gt;I would rather StaSSH stay small enough to run happily on an old laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;StaSSH is open source, and the project is on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/arturormk/stassh-rust" rel="noopener noreferrer"&gt;StaSSH on GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Linux packages provide the CLI, TUI and GUI together, or you can build the Rust workspace from source.&lt;/p&gt;

&lt;p&gt;If your SSH world still fits comfortably in &lt;code&gt;~/.ssh/config&lt;/code&gt;, you may not need any of this.&lt;/p&gt;

&lt;p&gt;But if your collection of machines has begun leaking into configuration files, notes, shell history, old scripts and your increasingly unreliable biological memory, StaSSH may look familiar.&lt;/p&gt;

&lt;p&gt;SSH itself was never the problem.&lt;/p&gt;

&lt;p&gt;I just needed somewhere to remember how I use it.&lt;/p&gt;

</description>
      <category>cli</category>
      <category>devops</category>
      <category>networking</category>
      <category>tools</category>
    </item>
    <item>
      <title>From Panorama to Planetarium: Building PanoPose</title>
      <dc:creator>Arturo Ramírez-Montesinos Krogulski</dc:creator>
      <pubDate>Tue, 01 Sep 2026 21:43:30 +0000</pubDate>
      <link>https://dev.to/arturormk/from-panorama-to-planetarium-building-panopose-3h4m</link>
      <guid>https://dev.to/arturormk/from-panorama-to-planetarium-building-panopose-3h4m</guid>
      <description>&lt;p&gt;Astronomical software can tell me precisely when the Moon rises.&lt;/p&gt;

&lt;p&gt;Unfortunately, the Moon does not rise over a mathematical horizon from my observing site. It rises behind trees, roofs, hills, antennas, and whatever else happens to occupy that particular azimuth.&lt;/p&gt;

&lt;p&gt;A planet may technically have risen while remaining invisible behind a ridge for another twenty minutes. A conjunction may happen low enough that one tree makes the difference between seeing it and missing it. A young crescent Moon may appear in a narrow gap in the landscape. An eclipse or planetary event close to sunrise or sunset may be theoretically visible but practically hidden.&lt;/p&gt;

&lt;p&gt;For an observer, rise and set times are only theoretical until you know the shape of the real horizon.&lt;/p&gt;

&lt;p&gt;That is the problem PanoPose is meant to solve.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/arturormk/panopose-rust" rel="noopener noreferrer"&gt;PanoPose&lt;/a&gt; is a free and open-source Linux desktop application for taking a full-sphere 360° panorama of a real location, calibrating it against real altitude and azimuth coordinates, and turning it into something useful for astronomical planning — including a ready-to-install &lt;a href="https://stellarium.org/es/" rel="noopener noreferrer"&gt;Stellarium&lt;/a&gt; landscape.&lt;/p&gt;

&lt;p&gt;The basic idea is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Take a picture of your horizon, teach the picture where it is looking, and let the planetarium use the real landscape.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Capturing the whole horizon used to be the hard part
&lt;/h2&gt;

&lt;p&gt;Years ago, when I wanted a panorama of an observing site, I did it the traditional way.&lt;/p&gt;

&lt;p&gt;Take a photograph.&lt;/p&gt;

&lt;p&gt;Rotate the camera.&lt;/p&gt;

&lt;p&gt;Take another.&lt;/p&gt;

&lt;p&gt;Keep enough overlap.&lt;/p&gt;

&lt;p&gt;Repeat until you have covered the full 360°.&lt;/p&gt;

&lt;p&gt;If you also need a large vertical field of view, repeat the process at different elevations.&lt;/p&gt;

&lt;p&gt;Then feed everything into &lt;a href="https://hugin.sourceforge.io/" rel="noopener noreferrer"&gt;Hugin&lt;/a&gt; or another panorama stitcher and hope that the seams, exposure differences, moving branches, clouds, and projection geometry all cooperate.&lt;/p&gt;

&lt;p&gt;That still works, and for very high-resolution panoramas it may still be the right solution.&lt;/p&gt;

&lt;p&gt;But consumer 360° cameras have changed the practical equation.&lt;/p&gt;

&lt;p&gt;I now use an old &lt;a href="https://www.mobilefun.com/official-huawei-envizion-360-panoramic-camera-cv60-grey-68215" rel="noopener noreferrer"&gt;Huawei CV60&lt;/a&gt; connected to my phone and mounted on a monopod. The monopod is useful because a full-sphere camera sees almost everything, including whatever is holding it up, so minimizing the footprint makes the final image cleaner.&lt;/p&gt;

&lt;p&gt;One shot gives me a &lt;strong&gt;5376×2688 pixel, 2:1 equirectangular panorama&lt;/strong&gt; covering the complete 360×180° sphere.&lt;/p&gt;

&lt;p&gt;No stitching. No sequence of exposures. No carefully measured rotation.&lt;/p&gt;

&lt;p&gt;You press the button and you have the panorama.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuomzjjr8c0qvrleopnf3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuomzjjr8c0qvrleopnf3.jpg" alt="Example raw 360° equirectangular panorama" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That makes capturing the site almost trivial.&lt;/p&gt;

&lt;p&gt;There is still one problem.&lt;/p&gt;

&lt;p&gt;The panorama does not know which way it is looking.&lt;/p&gt;

&lt;h2&gt;
  
  
  A photograph without coordinates
&lt;/h2&gt;

&lt;p&gt;A 360° image can contain the entire landscape and still have no useful relationship with astronomical coordinates.&lt;/p&gt;

&lt;p&gt;Which pixel is north?&lt;/p&gt;

&lt;p&gt;Where exactly is the geometric horizon?&lt;/p&gt;

&lt;p&gt;Is the camera perfectly level?&lt;/p&gt;

&lt;p&gt;How much roll, pitch, or yaw is present?&lt;/p&gt;

&lt;p&gt;If I see a tree in the panorama, what is its actual azimuth? If its canopy reaches a certain height in the image, what altitude does that correspond to?&lt;/p&gt;

&lt;p&gt;That is where PanoPose begins.&lt;/p&gt;

&lt;p&gt;Instead of editing the panorama as a flat rectangle, PanoPose places it on the inside of a sphere. You view the landscape from the same conceptual position from which it was photographed.&lt;/p&gt;

&lt;p&gt;Over that sphere it can draw an Alt/Az coordinate grid, calculate the positions of astronomical objects for a chosen location and time, and let you rotate the panorama until the photograph agrees with the real sky.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhg0dnhc3hz6z1i4mhc7i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhg0dnhc3hz6z1i4mhc7i.png" alt="PanoPose with panorama, Alt/Az grid, and astronomy markers" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The job is calibration rather than image editing.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, let gravity tell you which way is up
&lt;/h2&gt;

&lt;p&gt;The first step is getting the panorama level.&lt;/p&gt;

&lt;p&gt;Buildings make this surprisingly easy.&lt;/p&gt;

&lt;p&gt;Corners, columns, window frames, lamp posts, utility poles and similar structures provide vertical lines. Tilt the panorama until those features agree with the vertical grid lines in PanoPose.&lt;/p&gt;

&lt;p&gt;If you are photographing somewhere completely natural and there are no trustworthy vertical features, you can create one.&lt;/p&gt;

&lt;p&gt;Tie a cord to a tree, hang a rock from the end, and make sure the improvised plumb line appears somewhere in the 360° photograph.&lt;/p&gt;

&lt;p&gt;Gravity has now drawn a vertical reference into your panorama.&lt;/p&gt;

&lt;p&gt;It is crude surveying equipment, but physics does not care that the weight is a rock.&lt;/p&gt;

&lt;p&gt;Once the panorama is level, one major uncertainty is gone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then let the Sun tell you which way you are looking
&lt;/h2&gt;

&lt;p&gt;This is the part that makes the workflow much easier than I initially expected.&lt;/p&gt;

&lt;p&gt;If the Sun appears in the photograph, PanoPose can calculate where the Sun actually was when the photograph was taken.&lt;/p&gt;

&lt;p&gt;The application reads the timestamp and GPS coordinates from the image metadata when they are available. Location and time can also be entered manually.&lt;/p&gt;

&lt;p&gt;Given those values, PanoPose calculates the Sun's expected Alt/Az position and displays a marker at that point in the spherical view.&lt;/p&gt;

&lt;p&gt;Then the calibration procedure is essentially:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Switch to &lt;strong&gt;Align Target&lt;/strong&gt; mode.&lt;/li&gt;
&lt;li&gt;Find the photographed Sun.&lt;/li&gt;
&lt;li&gt;Drag the panorama until the photographed Sun coincides with the calculated Sun marker.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That establishes the panorama's absolute orientation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8b466w0gpjjxauc7mjb7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8b466w0gpjjxauc7mjb7.jpg" alt="Photographed Sun offset from calculated marker" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz9yb0pbv8jjmb8mqti01.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz9yb0pbv8jjmb8mqti01.jpg" alt="Sun aligned with calculated marker" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In practice, I have found that this two-stage procedure gets remarkably close:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gravity gives you vertical.&lt;br&gt;
The Sun gives you orientation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For most purposes, that may already be all you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  At night, the stars become precision instruments
&lt;/h2&gt;

&lt;p&gt;If you want to refine the calibration further, the night sky provides an almost ridiculous number of free reference points.&lt;/p&gt;

&lt;p&gt;Given an observer's geographical location and the time, the expected Alt/Az coordinates of stars can be calculated very accurately.&lt;/p&gt;

&lt;p&gt;That means the stars are effectively lots of little precision Alt/Az gauges scattered across the sky.&lt;/p&gt;

&lt;p&gt;PanoPose includes a &lt;strong&gt;Planetarium&lt;/strong&gt; mode using a bundled catalog of bright stars. Set the correct location and time and the expected star field appears above the horizon.&lt;/p&gt;

&lt;p&gt;You can then take a nighttime photograph — even an ordinary phone photograph if the phone is sensitive enough — that includes both stars and recognizable horizon features.&lt;/p&gt;

&lt;p&gt;Perhaps a star appears just above a roof corner.&lt;/p&gt;

&lt;p&gt;Perhaps another sits beside a particular branch.&lt;/p&gt;

&lt;p&gt;Perhaps one is visible through a notch in a tree canopy.&lt;/p&gt;

&lt;p&gt;If PanoPose predicts that star in exactly the same place relative to the landscape, the calibration agrees. If not, the remaining discrepancy tells you that there is still a small orientation error.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2hxz2flu9h5ecmf1fqr3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2hxz2flu9h5ecmf1fqr3.jpg" alt="Nighttime panorama or reference photograph with Planetarium mode" width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I found that leveling against vertical structures and aligning against the Sun was already extremely good. Stars are where you go when you want to chase the remaining tiny fraction of a degree.&lt;/p&gt;

&lt;p&gt;So the full calibration hierarchy becomes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gravity gives you vertical.&lt;br&gt;
The Sun gives you orientation.&lt;br&gt;
The stars give you precision.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The geometric horizon is not your horizon
&lt;/h2&gt;

&lt;p&gt;Once a panorama is calibrated, it stops being merely a picture of the site.&lt;/p&gt;

&lt;p&gt;It becomes a directional model of the local horizon.&lt;/p&gt;

&lt;p&gt;Suppose Stellarium tells you that an object rises at 05:42.&lt;/p&gt;

&lt;p&gt;That means it crosses altitude 0° at 05:42.&lt;/p&gt;

&lt;p&gt;But perhaps there is a ridge at altitude 6° in that direction.&lt;/p&gt;

&lt;p&gt;Or a large tree.&lt;/p&gt;

&lt;p&gt;Or two trees with a useful gap between them.&lt;/p&gt;

&lt;p&gt;The question you normally care about is not:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When does the object geometrically rise?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When will I actually be able to see it from here?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A calibrated panorama lets the real landscape answer that question.&lt;/p&gt;

&lt;p&gt;This is especially useful for phenomena that happen close to the horizon: conjunctions, young or old crescent Moons, Mercury and Venus, objects in twilight, eclipses near sunrise or sunset, and photographic compositions involving celestial objects and landscape features.&lt;/p&gt;

&lt;p&gt;Sometimes the difference between seeing an event and missing it really is one particular tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparing one horizon with another
&lt;/h2&gt;

&lt;p&gt;PanoPose can also load reference panoramas.&lt;/p&gt;

&lt;p&gt;This is useful if you already have a calibrated panorama and later photograph the same location again.&lt;/p&gt;

&lt;p&gt;Vegetation grows. Trees lose branches. Trees fall. New trees appear in gaps that used to be open. Buildings and other structures change too.&lt;/p&gt;

&lt;p&gt;PanoPose can place the new panorama, called the &lt;strong&gt;target&lt;/strong&gt;, over an older &lt;strong&gt;reference&lt;/strong&gt; panorama and compare them directly.&lt;/p&gt;

&lt;p&gt;You can blend the two images or adjust their opacity manually, but one of the most revealing comparison modes is &lt;strong&gt;Blink&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In Blink mode, PanoPose automatically alternates between the target panorama and the reference panorama.&lt;/p&gt;

&lt;p&gt;The effect is simple but surprisingly effective.&lt;/p&gt;

&lt;p&gt;A tree that has grown several degrees higher seems to jump between the two states. A missing branch becomes immediately obvious. A tree that has fallen simply disappears and reappears as the images alternate. Small changes that are difficult to notice while looking at two static images become conspicuous when the whole horizon flips back and forth.&lt;/p&gt;

&lt;p&gt;That makes an older calibrated panorama more than an archive. It becomes a measuring reference for the new one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Saving the calibration with the photograph
&lt;/h2&gt;

&lt;p&gt;Once a panorama has been aligned, it would be annoying if the calibration existed only inside the current PanoPose session.&lt;/p&gt;

&lt;p&gt;PanoPose can optionally save a copy of the loaded image containing the calibration metadata.&lt;/p&gt;

&lt;p&gt;That includes the three pose angles together with time and location information. PanoPose uses GPano metadata where appropriate and also stores its own calibration information in XMP metadata.&lt;/p&gt;

&lt;p&gt;Open that image again later and the application can recover the pose.&lt;/p&gt;

&lt;p&gt;In other words, you only have to teach the panorama where it is looking once.&lt;/p&gt;

&lt;p&gt;PanoPose can also import time and GPS information from another ordinary photograph without loading that photograph as the panorama itself. That can be useful when another camera or phone has the metadata you want to use for the calibration. It also makes it easy to show the star positions at the precise time you took that night-time reference photo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Removing the sky sounded easier than it was
&lt;/h2&gt;

&lt;p&gt;For a Stellarium landscape, it is necessary to make the real photographic sky transparent so that Stellarium can draw its own sky behind the landscape.&lt;/p&gt;

&lt;p&gt;At first this looked like a problem that ought to have a pleasantly simple solution.&lt;/p&gt;

&lt;p&gt;Start at the top of the image.&lt;/p&gt;

&lt;p&gt;Identify sky-like colors.&lt;/p&gt;

&lt;p&gt;Follow connected regions.&lt;/p&gt;

&lt;p&gt;Perhaps look at brightness or gradients.&lt;/p&gt;

&lt;p&gt;Remove everything that looks like sky.&lt;/p&gt;

&lt;p&gt;That works wonderfully until it doesn't.&lt;/p&gt;

&lt;p&gt;Clouds are not blue. Sunset skies are not blue. Haze reduces boundaries. Tree branches produce thousands of tiny edges. Bright gaps appear between leaves. Buildings have reflections. Thin objects cross the sky. A heuristic that works perfectly on one panorama can remove part of a tree on the next.&lt;/p&gt;

&lt;p&gt;After trying simpler approaches, I eventually stopped trying to turn the problem into something simpler than it really is.&lt;/p&gt;

&lt;p&gt;PanoPose uses the open-source &lt;strong&gt;&lt;a href="https://github.com/knyipab/skyseg-ncnn" rel="noopener noreferrer"&gt;skyseg-ncnn&lt;/a&gt;&lt;/strong&gt; program for optional sky segmentation. It runs a small trained model specifically intended to distinguish sky from non-sky regions.&lt;/p&gt;

&lt;p&gt;PanoPose deliberately treats this as an external optional tool rather than bundling it.&lt;/p&gt;

&lt;p&gt;At startup, the application checks whether the &lt;code&gt;skyseg-ncnn&lt;/code&gt; executable exists in the user's &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If it does, the &lt;strong&gt;Remove Sky&lt;/strong&gt; option becomes available.&lt;/p&gt;

&lt;p&gt;If it does not, nothing else changes. PanoPose works normally without sky removal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy9priwhofqatrg99y5rx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy9priwhofqatrg99y5rx.jpg" alt="Sky removed" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The resulting mask is converted into transparency, including partially transparent edges around trees and other fine structures.&lt;/p&gt;

&lt;p&gt;For a Stellarium landscape, the result is much cleaner than simply placing the original photographed sky over the simulated one.&lt;/p&gt;

&lt;h2&gt;
  
  
  From panorama to planetarium
&lt;/h2&gt;

&lt;p&gt;Once the panorama is calibrated, PanoPose can export it as a corrected full-sphere PNG.&lt;/p&gt;

&lt;p&gt;The orientation is baked into the pixels while preserving the original equirectangular format and dimensions.&lt;/p&gt;

&lt;p&gt;More importantly for astronomy users, PanoPose can export a &lt;strong&gt;Stellarium spherical landscape ZIP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You provide a name, author and description, and PanoPose generates both the posed panorama texture and the &lt;code&gt;landscape.ini&lt;/code&gt; file that Stellarium expects.&lt;/p&gt;

&lt;p&gt;The current site latitude, longitude and elevation are included automatically.&lt;/p&gt;

&lt;p&gt;If sky removal is enabled, the exported landscape can contain transparency so Stellarium's own sky appears above the photographed horizon.&lt;/p&gt;

&lt;p&gt;The result can then be installed in Stellarium like any other custom landscape.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdwj2vxkq5wla1ghv1zwf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdwj2vxkq5wla1ghv1zwf.jpg" alt="Stellarium export dialog" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7xqgsj5n8cz0472cyl7g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7xqgsj5n8cz0472cyl7g.jpg" alt="Finished calibrated landscape running in Stellarium" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That closes the loop:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real location → 360° photograph → calibrated Alt/Az panorama → Stellarium landscape.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  It is not a panorama stitcher
&lt;/h2&gt;

&lt;p&gt;PanoPose is deliberately narrow in scope.&lt;/p&gt;

&lt;p&gt;It does not convert ordinary photographs into a spherical panorama.&lt;/p&gt;

&lt;p&gt;It does not replace Hugin.&lt;/p&gt;

&lt;p&gt;It does not attempt automatic computer-vision registration.&lt;/p&gt;

&lt;p&gt;It is not a complete planetarium.&lt;/p&gt;

&lt;p&gt;And its Planetarium mode is not intended to compete with Stellarium.&lt;/p&gt;

&lt;p&gt;Its job is the missing step between those tools:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Take an existing 2:1 spherical panorama and establish precisely how that panorama corresponds to the real world.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once that relationship exists, the image becomes much more useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note about how it was built
&lt;/h2&gt;

&lt;p&gt;PanoPose was developed entirely by OpenAI Codex from my requirements, testing, and iteration, and went from concept to v1.0 in about 48 hours on my $20/mo ChatGPT Plus plan. That is not really the point of the project, but it is worth stating for completeness. This is 2026; I wanted the tool, so I had the machine write it. Developers should probably get comfortable with the tools of the present before somebody starts calling them the tools of the past.&lt;/p&gt;

&lt;p&gt;More interesting than who typed the code is whether the resulting software solves the problem.&lt;/p&gt;

&lt;p&gt;For me, it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting PanoPose
&lt;/h2&gt;

&lt;p&gt;PanoPose is &lt;strong&gt;free, open source, and currently a Linux application&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The project supports Linux packaging as &lt;code&gt;.deb&lt;/code&gt;, &lt;code&gt;.rpm&lt;/code&gt;, and AppImage builds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project repository:&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://github.com/arturormk/panopose-rust" rel="noopener noreferrer"&gt;https://github.com/arturormk/panopose-rust&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The repository also contains a guided build/install script for people who prefer to build from source.&lt;/p&gt;

&lt;p&gt;Two external programs are relevant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ExifTool&lt;/strong&gt; is used when writing calibration and metadata back into image files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;skyseg-ncnn&lt;/strong&gt; is optional and is needed only for automatic sky removal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The application itself remains usable without &lt;code&gt;skyseg-ncnn&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The repository contains a script that will clone the &lt;code&gt;ncnn&lt;/code&gt; and &lt;code&gt;skyseg-ncnn&lt;/code&gt; repositories, patch the latter to work around a version mismatch, build the tool, and install it in your system.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;If you want to try PanoPose with your own observing site, the workflow is roughly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Capture or stitch a full-sphere &lt;strong&gt;2:1 equirectangular panorama&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Open it in PanoPose.&lt;/li&gt;
&lt;li&gt;Let PanoPose read the image's location and timestamp, or enter them manually.&lt;/li&gt;
&lt;li&gt;Level the panorama using trustworthy vertical features — or an improvised plumb line.&lt;/li&gt;
&lt;li&gt;Switch to &lt;strong&gt;Align Target&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Drag the photographed Sun onto PanoPose's calculated Sun marker.&lt;/li&gt;
&lt;li&gt;Optionally use stars and Planetarium mode to refine the alignment.&lt;/li&gt;
&lt;li&gt;Save the calibrated metadata if you want to preserve the pose in the image.&lt;/li&gt;
&lt;li&gt;Optionally remove the sky.&lt;/li&gt;
&lt;li&gt;Export the result as a corrected PNG or a Stellarium landscape ZIP.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A modern 360° camera makes the first step almost trivial.&lt;/p&gt;

&lt;p&gt;The Sun and stars provide the calibration references for free.&lt;/p&gt;

&lt;p&gt;PanoPose handles the bit in between.&lt;/p&gt;

&lt;p&gt;If your astronomical horizon contains more than an infinite flat ocean, that bit may be useful.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>astronomy</category>
      <category>photography</category>
      <category>linux</category>
    </item>
    <item>
      <title>Promptcraft for Play: Endless Conversational Adventures with ChatGPT</title>
      <dc:creator>Arturo Ramírez-Montesinos Krogulski</dc:creator>
      <pubDate>Thu, 06 Nov 2025 11:04:38 +0000</pubDate>
      <link>https://dev.to/arturormk/promptcraft-for-play-endless-conversational-adventures-with-chatgpt-5dhg</link>
      <guid>https://dev.to/arturormk/promptcraft-for-play-endless-conversational-adventures-with-chatgpt-5dhg</guid>
      <description>&lt;p&gt;Rediscover interactive storytelling in the age of AI.&lt;/p&gt;




&lt;p&gt;Long before games had graphics, imagination did all the rendering.&lt;/p&gt;

&lt;p&gt;Players typed things like &lt;code&gt;open door&lt;/code&gt;, &lt;code&gt;look north&lt;/code&gt;, or &lt;code&gt;take lantern&lt;/code&gt;, and the computer responded with text. That was enough to create entire worlds. These were the classic &lt;em&gt;text adventures&lt;/em&gt; — interactive fiction where the story unfolded through conversation.&lt;/p&gt;

&lt;p&gt;With ChatGPT, this form of storytelling is reborn.&lt;/p&gt;

&lt;p&gt;You can now play through worlds that &lt;em&gt;don’t need&lt;/em&gt; predefined verbs or rigid parsers. You can simply &lt;em&gt;talk&lt;/em&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I shine the flashlight into the dark corridor and listen for movement.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And ChatGPT will respond naturally, adapting to your intent, describing what you see, and evolving the story dynamically. Every playthrough is different — and the world exists only between you and the AI.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Simple Prompt That Starts It All
&lt;/h3&gt;

&lt;p&gt;Here’s the seed that unlocks a private adventure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are to create a short text adventure for me to explore.
Keep the entire world, map, and solution secret.
Once you have prepared it, tell me only that it is ready.
I will then begin exploring by asking questions or giving commands in natural language.
Never reveal what lies ahead — only describe what I perceive or what happens in response to my actions.
The goal is for me to solve the adventure through exploration and reasoning.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s all you need. The model will silently build a self-contained world — then let you explore it through natural dialogue.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why It Works So Well
&lt;/h3&gt;

&lt;p&gt;A good adventure is a closed world with hidden logic. The player discovers meaning by testing the unknown.&lt;/p&gt;

&lt;p&gt;ChatGPT excels at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintaining world continuity&lt;/li&gt;
&lt;li&gt;Generating vivid, consistent descriptions&lt;/li&gt;
&lt;li&gt;Reacting naturally to freeform language&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of a parser, you have a narrator — one that adapts in real time.&lt;/p&gt;




&lt;h3&gt;
  
  
  Adventures You Can Try
&lt;/h3&gt;

&lt;p&gt;ChatGPT will ask you some followup questions about what kind of world you would like it to invent. You can give it broad strokes (“I'd like sci-fi“) and let it surprise you, or you can go for more detailed scenarios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sci-fi survival&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I awaken in a derelict space habitat with partial power. The rest of the crew is missing. Keep the layout and dangers secret.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Mystery&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I am the caretaker of a silent manor. Each night, something unseen moves in the hallways.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Noir detective&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Write a short noir mystery in 1940s Chicago. I’m a private investigator trying to solve a murder.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Fantasy quest&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Build a world where I seek a relic guarded by riddles. Keep the map hidden.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each becomes a self-contained, conversational world — a microcosm of imagination that lives and dies in one chat.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Joy of Exploration
&lt;/h3&gt;

&lt;p&gt;You don’t have to win fast. You just have to &lt;em&gt;wonder&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Ask questions, examine details, experiment. ChatGPT will respond to curiosity, not just commands.&lt;/p&gt;

&lt;p&gt;These adventures aren’t nostalgic recreations — they’re &lt;em&gt;new forms&lt;/em&gt; of storytelling, born from the conversation itself. They show what happens when a reasoning engine and a narrative engine merge.&lt;/p&gt;

&lt;p&gt;It’s not just prompt engineering.&lt;/p&gt;

&lt;p&gt;It’s &lt;strong&gt;promptcraft for play.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>adventure</category>
    </item>
    <item>
      <title>Software Curatorship: A New Practice for the Age of Intelligent Code</title>
      <dc:creator>Arturo Ramírez-Montesinos Krogulski</dc:creator>
      <pubDate>Tue, 28 Oct 2025 18:44:44 +0000</pubDate>
      <link>https://dev.to/arturormk/software-curatorship-a-new-practice-for-the-age-of-intelligent-code-5880</link>
      <guid>https://dev.to/arturormk/software-curatorship-a-new-practice-for-the-age-of-intelligent-code-5880</guid>
      <description>&lt;p&gt;&lt;em&gt;Seven essays on how humans and AI can build meaning together&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;The rise of AI-assisted coding has changed everything — and yet, our methods haven’t caught up.&lt;/p&gt;

&lt;p&gt;Autocomplete turned into code generation, code generation into system design, and before long we found ourselves surrounded by software that writes software.&lt;/p&gt;

&lt;p&gt;But who, then, preserves meaning? Who ensures that what AI produces still reflects human intent, ethics, and vision?&lt;/p&gt;

&lt;p&gt;That question gave birth to a new discipline: &lt;strong&gt;Software Curatorship&lt;/strong&gt; — the art and method of guiding AI-driven development so that systems remain coherent, transparent, and aligned with purpose.&lt;/p&gt;

&lt;p&gt;Over the past months, I explored this idea through a series of essays on Medium. Below is a summary of that series, tracing the evolution from philosophy to methodology.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;1. Prometaphysics: Where AI Meets the Edge of Consciousness&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://medium.com/@arturormk/prometaphysics-where-ai-meets-the-edge-of-consciousness-4867beb37ee9" rel="noopener noreferrer"&gt;Read on Medium&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A philosophical reflection on collaboration between minds — human and synthetic — and how creative intelligence might evolve beyond pure utility.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;2. Coding in the Age of AI: What No One’s Telling You&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://medium.com/@arturormk/software-curatorship-the-art-of-guiding-artificial-creation-eb42f5c9753b" rel="noopener noreferrer"&gt;Read on Medium&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;How automation changed the meaning of “software development.”&lt;br&gt;
The essay introduces the concept of the &lt;strong&gt;curator&lt;/strong&gt; — the human responsible for intent and coherence in AI-assisted creation.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;3. The Making of a Software Curator&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://medium.com/@arturormk/the-making-of-a-software-curator-7b9271ce72e1" rel="noopener noreferrer"&gt;Read on Medium&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;From developer to curator: redefining authorship, responsibility, and skill in the era of generative code.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;4. Coding vs. Curating: What Sets Curators Apart from Vibe Coders&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://medium.com/@arturormk/iv-structured-creativity-what-sets-software-curators-apart-from-vibe-coders-e982916e1bbf" rel="noopener noreferrer"&gt;Read on Medium&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A practical distinction between “vibe coding” — letting AI generate code without direction — and true Curatorship, where the human provides architecture, intent, and moral compass.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;5. Software Curatorship in Practice&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://medium.com/@arturormk/v-software-curatorship-in-practice-part-1-the-method-behind-the-meaning-c13d32dbce0d" rel="noopener noreferrer"&gt;Part 1 – The Method Behind Meaning&lt;/a&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://medium.com/@arturormk/v-software-curatorship-in-practice-part-2-the-curatorial-loop-f2c1beccfb0d" rel="noopener noreferrer"&gt;Part 2 – The Curatorial Loop&lt;/a&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://medium.com/@arturormk/v-software-curatorship-in-practice-part-3-the-human-at-the-center-b40f0be5a3ef" rel="noopener noreferrer"&gt;Part 3 – The Human at the Center&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These three final chapters ground the philosophy in method.&lt;/p&gt;

&lt;p&gt;They describe the &lt;strong&gt;Curatorial Loop&lt;/strong&gt; — a continuous cycle of intent, generation, review, and reflection — and introduce the &lt;strong&gt;Curation Stack&lt;/strong&gt;, a layered system where code, documentation, and rationale evolve together.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Why This Matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Software Curatorship isn’t a metaphor. It’s a working methodology for aligning AI development with human purpose — one that integrates version control, documentation, testing, and reflection into a single living process.&lt;/p&gt;

&lt;p&gt;It reimagines the developer’s role as a &lt;strong&gt;philosophical engineer&lt;/strong&gt;: someone who maintains semantic coherence and ethical integrity while machines handle execution.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;The Road Ahead&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The goal of Software Curatorship is not to slow down automation — it’s to give it &lt;em&gt;direction&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;As AI systems become more capable, we’ll need curators to preserve the dialogue between &lt;em&gt;what we build&lt;/em&gt; and &lt;em&gt;why we build it&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I’m currently preparing the &lt;strong&gt;Software Curator’s Handbook&lt;/strong&gt;, an open project that will document practical tools, YAML schemas, and examples for Curatorial development.&lt;/p&gt;

&lt;p&gt;Follow me here or on &lt;a href="https://medium.com/@arturormk" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; if you’d like to join the discussion.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>softwareengineering</category>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
