introducing a portfolio as a tui app served over ssh! https://devcomp.xyz
Find a file
2025-09-22 08:09:21 +01:00
.cargo build(cargo): be more faithful (use /usr/bin/env) 2025-09-01 17:54:45 +01:00
.config feat!: persist ssh keys across builds by writing them in data dir 2025-08-27 10:34:50 +01:00
.github ci: fix commit-msg being referred to as pr-title 2025-09-18 11:23:34 +01:00
assets feat: impl portfolio design with complete about page 2025-02-02 18:28:57 +00:00
patches/ratatui-image-8.0.1 build(rs): use cargo-rustc-patch script instead of patch-crate 2025-09-01 12:13:17 +01:00
src feat: display running version in app tui 2025-09-17 11:49:00 +01:00
www style(nix): apply treefmt formatting 2025-09-22 08:09:21 +01:00
.dockerignore chore(dockerignore): svelte.config.ts shouldn't be ignored 2025-09-04 08:23:08 +01:00
.envrc feat: init, basic rendering with ratatui to ssh backend 2025-01-28 05:40:02 +00:00
.gitignore build(nix): make build process accept features and export different flavors 2025-09-07 19:53:14 +01:00
build.rs build(rs): use cargo-rustc-patch script instead of patch-crate 2025-09-01 12:13:17 +01:00
Cargo.lock chore(pkg): bump to v0.1.4, update dependencies, add homepage 2025-09-08 09:24:17 +01:00
Cargo.toml chore(pkg): bump to v0.1.4, update dependencies, add homepage 2025-09-08 09:24:17 +01:00
default.nix build(nix): make build process accept features and export different flavors 2025-09-07 19:53:14 +01:00
deny.toml chore: add cargo-deny configs 2025-09-01 19:27:13 +01:00
Dockerfile build(docker): use --locked while installing bun deps 2025-09-08 07:31:42 +01:00
flake.lock build(nix/deps): update input dependencies in lockfile 2025-09-21 09:08:37 +01:00
flake.nix build(nix): add cargo-deny checks to nix flake 2025-09-08 09:07:23 +01:00
LICENSE chore(LICENSE): fuck MIT (use AGPL v3 instead) 2025-08-31 12:35:10 +01:00
README.md chore(README): format nix codeblock and paragraph better 2025-09-08 08:46:34 +01:00
rust-toolchain build(toolchain): use rust v1.91 nightly equivalent 2025-09-01 13:56:41 +01:00
rustfmt.toml feat(blog): implement proper blog pages with full rendering 2025-08-18 19:10:47 +01:00

ssh-portfolio

made with ratatui rust ci version MIT licensed

what if the internet as we knew it was different? what if the world wide web never came to be? what if... we lived in our own little ssh services? introducing a portfolio as a tui app served over ssh!

try it out:

ssh -o SendEnv=TERM_PROGRAM erica@devcomp.xyz

Tip

make sure you have a nerd font installed or some features may not work!

features

  • about & projects tab
  • wip blog powered by atproto (whitewind)
  • http landing page

showcase

desc img
about portfolio tui with the about tab selected, erica introduces themselves
projects portfolio tui with the projects tab selected, the projects are laid out as cards in a grid
web landing website landing page, the page contains the ssh command and the keybinds for the portfolio tui, the page is made to look like the portfolio tui

running

with cargo

you'll need to have rust installed, you can do that using rustup. then, run using:

cargo run --release --no-default-features -- --help

with nix

the nix flake exports a package with an overridable features attribute. default.nix exports two variants using this, ssh-portfolio and ssh-portfolio-blog.

nix build --file . ssh-portflio      # without blog
nix build --file . ssh-portflio-blog # with blog

# then run it:
./result/bin/ssh-portfolio --help

or with your own set of features to build for:

# assuming this is in `custom.nix` in the same directory as `flake.nix`:
{ pkgs ? import <nixpkgs> { } }:
rec {
  ssh-portfolio = (builtins.getFlake (builtins.toString ./.)).packages.${pkgs.system}.ssh-portfolio;
  ssh-portfolio-custom = ssh-portfolio.override { features = [ ... ]; };
}

with docker

build an image:

docker build -t ssh-portfolio:latest --build-arg CARGO_FEATURES= .

to run it:

docker run ssh-portfolio:latest -- --help

configuration

configuration options can be specified within a configuration file. this file is located within your configuration directory.

  • Linux: $XDG_CONFIG_HOME/ssh-portfolio or ~/.config/ssh-portfolio
  • macOS: ~/Library/Application Support/xyz.devcomp.ssh-portfolio
  • Windows: %LOCALAPPDATA%\devcomp\ssh-portfolio\config

the directory can be overridden using the SSH_PORTFOLIO_CONFIG environment variable. the name of the config file can be: config.json5, config.json, config.yaml, config.toml, and config.ini.

the default config is as follows:

{
  "private_keys": {
    "ssh-rsa": "$DATA_DIR/ssh/id_rsa",
    "ecdsa-sha2-nistp256": "$DATA_DIR/ssh/id_ecdsa",
    "ssh-ed25519": "$DATA_DIR/ssh/id_ed25519"
  },
  "keybindings": {
    "Home": {
      "<q>": "Quit",
      "<Ctrl-d>": "Quit",
      "<Ctrl-c>": "Quit",
      "<Esc>": "Quit",
      "<Ctrl-z>": "Suspend",
      "<right>": "NextTab",
      "<left>": "PrevTab",
      "<down>": "SelectNext",
      "<up>": "SelectPrev",
      "<enter>": "Continue"
    }
  }
}

private_keys

specifies the path to the files containing the SSH private keys to use. the following variables are expanded:

  • $DATA_DIR: the data directory, this can be overridden with the SSH_PORTFOLIO_DATA environment variable. defaults to:
    • Linux: $XDG_DATA_HOME/ssh-portfolio or ~/.local/share/ssh-portfolio
    • macOS: ~/Library/Application Support/xyz.devcomp.ssh-portfolio
    • Windows: %LOCALAPPDATA%\devcomp\ssh-portfolio\data
  • $CONFIG_DIR: the configuration directory, as described above.
  • $HOME: the home directory, aka ~.

keybindings

specifies the keybinds! this is an object where the key corresponds to a mode and the value is an object mapping a key to an action. currently, the only mode available is Home.

these actions can be specified:

  • General
    • Tick: do a tick
    • Render: renders the tui
    • Suspend: suspends
    • Resume: resumes after a suspend
    • Quit: quits
    • ClearScreen: clears the screen
  • Tabs
    • NextTab: go to the next tab
    • PrevTab: go to the previous tab
  • Selection
    • SelectNext: select the next item
    • SelectPrev: select the previous item
    • Continue: activate the currently selected item