Skip to content

Portside V3: full native macOS rewrite in SwiftUI - #1

Merged
Mac2100 merged 3 commits into
mainfrom
claude/portside-v3-macos-d9zdew
Jul 30, 2026
Merged

Portside V3: full native macOS rewrite in SwiftUI#1
Mac2100 merged 3 commits into
mainfrom
claude/portside-v3-macos-d9zdew

Conversation

@Mac2100

@Mac2100 Mac2100 commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Summary

Rebuilds Portside from the ground up as a native macOS app in SwiftUI (macOS 14+, Swift Package Manager), replacing the Electron implementation while preserving all existing functionality. Architecture, styling, packaging, CI, release process, and the one-click update experience follow the patterns established in SentryHub and Buckett.

What's here

Core (Services/)

  • DockerTransport — a minimal HTTP/1.1 client over Network.framework with mutual TLS: client certificate auth, server verification anchored to the imported ca.pem (only the hostname check is waived, since NAS certs name the device while we connect by IP), streaming bodies, and the Connection: Upgrade hijack the exec API needs — something URLSession cannot do.
  • TLSIdentity — PEM parsing (PKCS#1 / PKCS#8 / EC) into a SecIdentity, plus certificate summaries for expiry warnings.
  • DockerClient — typed Engine API: containers, images, volumes, networks, events (with 24 h backfill), stats, logs (fetch + follow), exec (interactive + capture), archive get/put with a minimal tar implementation, pulls with X-Registry-Auth, prunes, and the shared stop → rename → create → start → delete replace dance with rollback.
  • RegistryClient / ImageUpdateService — manifest digest comparison with bearer-token auth (list digest OR any platform digest counts as up-to-date), per-container auto-update with a same-digest guard.
  • GitHubService — release/commit watching and Git Deploy via a throwaway alpine/git container (token passed only at run time).
  • ComposeParser (Yams), ContainerExport (compose.yml / docker run), CrashLogStore, HistoryStore, Notifier, Keychain.
  • UpdateChecker + SelfUpdater — the same in-place DMG update flow as Buckett/SentryHub: download, swap the bundle with rollback, relaunch.

UI (Views/)

  • Dashboard (rings, Swift Charts history with Live/24h/7d, top consumers), Insights (attention / updates / housekeeping action list), Activity feed.
  • Containers as a card grid with live sparklines or a compact list; compose stacks and manual groups; bulk actions; detail inspector; customize sheet.
  • Live logs with ANSI colors (pause / search / export), a SwiftTerm exec terminal, file browser with mount chips and in-app editing.
  • Deploy wizard, compose import with preview, edit-and-recreate, export, Git Deploy sheets.
  • Settings (hosts, certificates, appearance, notifications, registries, updates/watch), ⌘K command palette, menu bar companion.
  • Buckett-style theme system: six accent themes, System/Light/Dark, glass cards, toasts.

Destructive-action design carried over from V2: removing containers requires an explicit acknowledgment; destroying volumes requires typing the name.

Migration: V3 reads the same ~/Library/Application Support/Portside/config.json and certificate layout as V2, so hosts, groups, customizations, notification rules, and watch lists carry over. Registry credentials and the GitHub token move to the Keychain and are re-entered once.

Repo cleanup

  • Electron sources (main.js, preload.js, renderer, HTML/CSS, icons, tests, npm manifests) and the old ci.yml/release.yml removed.
  • New scripts/make_app.sh (universal binary → .app → DMG) and a single build.yml workflow: every push/PR builds and uploads a DMG artifact; tag pushes (v*) publish a GitHub Release the in-app updater consumes.
  • README rewritten in the style of the other repos; license switched to MIT per request.

Testing

There is no macOS toolchain in the authoring environment, so compilation is verified by the CI workflow in this PR (macos-14 runner). Runtime behavior against a real Docker host should be smoke-tested from the built DMG artifact: connect, dashboard metrics, logs, terminal, files, deploy, and an image update cycle.


🤖 Generated with Claude Code

https://claude.ai/code/session_01GMD3Q7bnJ9vJDdkaiuBN4Y


Generated by Claude Code

claude added 3 commits July 30, 2026 00:33
Rebuild the Electron app as a native SwiftUI application (macOS 14+,
Swift Package Manager), preserving all V2 functionality:

- Docker Engine API over mutual TLS via a Network.framework HTTP/1.1
  transport (client certificates, CA-pinned verification with only the
  hostname check waived, exec stream hijack for the terminal)
- Dashboard with host rings, persisted Live/24h/7d history charts, and
  top-consumer lists; Insights action list; live Activity feed
- Container grid/list with compose stacks, manual groups, per-container
  customization, bulk actions, and a detail inspector
- Live logs with ANSI colors, a SwiftTerm-based exec console, and a
  volume-aware file browser with in-app editing
- Deploy wizard, docker-compose import (Yams), container export to
  compose.yml / docker run, and edit-and-recreate with rollback
- Image update checker with digest comparison, per-container
  auto-update, private registry credentials in the Keychain
- GitHub watch and Git Deploy (notify / auto / nightly), crash log
  snapshots, per-event notification rules, certificate expiry warnings
- Multi-host support with per-host certificates, menu bar companion,
  command palette, six accent themes with light/dark/system appearance
- In-place self-updater against GitHub Releases (download, swap,
  relaunch), DMG packaging script, and a single build/release workflow
- Reads the V2 config file and certificate layout, so existing setups
  migrate automatically

Also: rewrite the README, switch the license to MIT, and remove the
Electron sources, icons, and legacy workflows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GMD3Q7bnJ9vJDdkaiuBN4Y
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GMD3Q7bnJ9vJDdkaiuBN4Y
- Rename the NetworkSummary.IPAM property so it no longer shadows the
  nested IPAM type
- Copy the image map before the update-check task group (Sendable
  capture) and split the too-complex shortName expression
- Pass Unmanaged<CFError> to SecCertificateCopyValues
- Use concrete Colors in the certificate-expiry ternary
- Give explicit initializers to views constructed from other files
  (private @State storage makes the synthesized memberwise initializer
  private)
- Color chart series explicitly instead of via chartForegroundStyleScale

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GMD3Q7bnJ9vJDdkaiuBN4Y
@Mac2100
Mac2100 merged commit c0a1967 into main Jul 30, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants