Architecture
Understanding the design and components of Swiftdock.
System Overview
CLI (swiftdock)
↓ HTTP (localhost:8080) ↓
Daemon (swiftdockd)
↓
Runtime + ImageStore
↓
~/.swiftdock/
Components
1. CLI (apps/cli)
Command-line interface that users interact with. Built with ArgumentParser.
- Sends HTTP requests to the daemon
- Parses command-line arguments
- Formats output for users
- Commands:
pull,run,ps,logs
2. Daemon (apps/daemon)
HTTP server that manages containers and images. Built with Hummingbird.
- Listens on
localhost:8080 - Exposes REST API for container operations
- Manages container lifecycle
- Controllers:
ImageController,ContainerController
3. Core Packages
Common (packages/Common)
ImageReference- Parse image names (e.g.,alpine:latest)SwiftdockClient- HTTP client for daemon communication- DTOs - Data transfer objects for API requests/responses
RegistryClient (packages/RegistryClient)
- Fetches Docker/OCI manifests from registries
- Handles Docker Hub authentication
- Downloads image layers (blobs)
- Supports manifest lists (multi-platform images)
ImageStore (packages/ImageStore)
- Stores image layers in
~/.swiftdock/blobs/ - Unpacks layers to
~/.swiftdock/images/<name>/rootfs/ - Verifies blob integrity (SHA256)
- Uses system
tarfor extraction
Runtime (packages/Runtime)
ContainerHost- Manages container lifecycle- Creates container filesystems
- Spawns processes using
Process() - Captures stdout/stderr to
std.log - Tracks container status (created, running, exited)
Data Flow
Pulling an Image
- User runs
swiftdock pull alpine:latest - CLI sends
POST /images/pullto daemon - Daemon uses
RegistryClientto fetch manifest RegistryClientdownloads layers from Docker HubImageStoreverifies and stores blobsImageStoreunpacks layers to rootfs- Daemon returns success to CLI
Running a Container
- User runs
swiftdock run alpine -- echo "hi" - CLI sends
POST /containers/create - Daemon creates container metadata and rootfs
- CLI sends
POST /containers/:id/start ContainerHostspawnsProcess()with command- Process runs in container rootfs directory
- stdout/stderr captured to
std.log - CLI polls until container exits
- CLI fetches and displays logs
Storage Layout
~/.swiftdock/
├── blobs/
│ └── sha256:abc123... # Compressed layer data
├── images/
│ └── alpine_latest/
│ └── rootfs/ # Unpacked filesystem
│ ├── bin/
│ ├── etc/
│ └── ...
└── containers/
└── <uuid>/
├── config.json # Container metadata
├── rootfs/ # Copy of image rootfs
└── std.log # stdout/stderr
Design Decisions
Why HTTP API?
- Enables remote control and custom clients
- Language-agnostic (curl, Python, Swift, etc.)
- Foundation for future SwiftUI apps
Why Process() instead of namespaces?
- macOS doesn't support Linux namespaces
- Process() is native and simple
- Focus on educational/lightweight use case
Why Copy-on-Run?
- Simplicity over CoW filesystems
- Each container gets isolated rootfs
- Future: Use APFS clones for efficiency