The problem

Every portfolio site looks the same shape: hero section, project grid, contact form. I’ve built that shape, it works, it’s also completely forgettable.

Approach

CLIFolio is an interactive terminal portfolio, reachable over SSH, no browser required. Someone can ssh into it and navigate my projects, skills, and experience with arrow keys.

Terminal window
ssh clifolio.example.com

No install, no clone, no npm run dev. Whoever’s checking it out gets dropped straight into the interface the moment the connection opens.

Why SSH instead of just a downloadable binary

I could’ve shipped this as a go install-able CLI, same as GitFlow TUI. Went with SSH access instead, changed more of the architecture than expected.

SSH removes that friction almost entirely, one command, no local Go toolchain. Wish, the SSH server library from Charm, made this possible without hand-rolling an SSH server myself.

The tradeoff’s real, though. A downloadable binary runs on the visitor’s machine, scales for free. An SSH server is a server, a process I keep running, unlike a static site that sits on a CDN forever.

The Stack

Bubble Tea for the TUI framework, Lipgloss for styling, both from Charm. The Elm Architecture sits underneath: Model holds state, Update handles keypresses, View renders it.

titleStyle := lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#7D56F4")).
Padding(1, 2)

Lipgloss is basically CSS for text, none of the DOM, none of the layout engine fighting you. You describe how a block should look, Bubble Tea renders it into the terminal buffer.

Why Go, specifically, for a portfolio

I’d been eyeing Go for a while, never a real excuse to use it outside a tutorial. Go’s whole pitch is simplicity, a standard library that already covers what you need, a compiler fast enough you forget you’re compiling.

go run main.go and you’re live. In a slower language I’d think twice before trying something, in Go I just tried it, basically free. A lot of CLIFolio’s design came from exactly that kind of fast, low-stakes experimentation.

Go’s concurrency mattered here too. Fetching live GitHub data shouldn’t freeze the whole session, so those calls run on a goroutine, and the UI stays interactive even if GitHub’s API is slow.

Handling multiple people at once

Every connection gets its own Bubble Tea program instance under Wish. Getting isolation right meant being careful nothing reached for global mutable state.

What Surprised Me Building a Second TUI

I’d built one TUI before, assumed the second would just be faster since I already knew the framework. Partly true. What actually surprised me: how much of “frontend work” is state management wearing a costume.

Terminal resize handling is still its own small nightmare. No framework gives free reflow the way a browser gives free flexbox, you recalculate layout by hand every time. Solved this once in the first TUI, still had to rethink it here since CLIFolio’s layout has more panels, more nested views.

The Honest Take

Practical? Debatable. A recruiter skimming applications on their phone isn’t opening a terminal and SSHing into anything, I know that going in.

That’s who it’s actually for: another developer, someone who lives in a terminal already, who’ll see ssh clifolio.example.com in a bio and immediately get what it is. Small audience. The right audience for what this project’s trying to do.