Kanora
Kanora
All dispatches
Architecture

Designing Host/Client Architecture: Bringing iTunes Home Sharing to Kanora

January 20, 20267 min readBy Kanora Team
kanoraarchitecturenetworkingiosmacosbonjourdesign

One of the most compelling features that made iTunes special was Home Sharing—the ability to browse and play music from another computer on your home network without any faffing about with accounts, subscriptions, or cloud sync. It just worked. You'd open iTunes on your Mac, see your partner's library appear in the sidebar, and click through to play their music.

That simplicity has been missing from modern music players for years. Most solutions now funnel you toward streaming services or require complex setup. Kanora is going the other way: we're building a distributed library system that brings back that iTunes Home Sharing magic, but properly designed for 2026 and built with service architecture.

The Vision

macOS becomes the host. Your Mac is where you store everything—external drives plugged in, terabytes of lossless audio, your curated library. It's plugged into your HiFi stack, always available, tuned and ready.

iOS becomes the client. Your iPhone and iPad discover that Mac automatically on your home network via Bonjour. The library appears as another source in the sidebar, just like "On This Device". Browse artists, albums, playlists using the same UI you already know. Stream or download tracks to take with you.

Playback options:

  • Stream to the client: Download to iOS cache, play locally while you move around the house
  • Remote playback: (Future phase) Control what plays through your HiFi speakers from your phone
  • Download for offline: Background queue pre-downloads albums so they're ready when you leave home

This is iTunes Home Sharing for the post-streaming era—local, private, device-agnostic, no subscription.

Architecture: Service-Based Discovery & Switching

The design hinges on one core principle: the data layer is pluggable. Views don't know whether they're talking to local Core Data or a remote REST API. They just ask the service layer for artists, albums, and playlists.

How Discovery Works

  1. Mac advertises: When Kanora launches on macOS, it registers itself via Bonjour with service type _kanora._tcp. This includes the host name, IP, and port.

  2. iOS listens: The iOS app runs a background Bonjour listener that picks up any Kanora services on the home network. Discovered hosts are stored in Core Data—name, IP, port, last-seen timestamp.

  3. User picks a library: In the sidebar, you see "On This Device" and "Ben's Mac" and "Studio Mac". Tapping one switches which library you're browsing. A quick health check ensures the host is reachable, then the view hierarchy swaps in the remote data provider.

No authentication, no pairing tokens for MVP. We trust the home network boundary. If you're on WiFi at home, you get access. This can be enhanced later if needed, but the MVP stays simple.

The LibraryServiceProvider Pattern

Instead of views querying Core Data directly, they use an injected service:

protocol LibraryServiceProvider {
    func fetchArtists() async throws -> [Artist]
    func fetchArtist(id: UUID) async throws -> Artist
    func fetchAlbums() async throws -> [Album]
    func fetchAlbum(id: UUID) async throws -> Album
    func fetchPlaylists() async throws -> [Playlist]
    func fetchPlaylist(id: UUID) async throws -> Playlist
}

Two implementations:

  • LocalLibraryService: Queries Core Data (existing behavior, just refactored)
  • RemoteLibraryService: Makes HTTP requests to the Mac API

When you switch libraries in the sidebar, ServiceContainer swaps which provider is active. Existing views re-render using the new data source. No view code changes needed—the abstraction handles everything.

The Mac's REST API

The Mac runs a simple HTTP server (port 8008 or user-configurable) that exposes endpoints mirroring your Core Data structure:

  • GET /api/artists → all artists with metadata
  • GET /api/albums/{id} → album details with track list
  • GET /api/playlists/{id} → playlist with tracks
  • GET /api/files/{trackId}/stream → audio file bytes (supports HTTP 206 range requests)

Responses are plain JSON, designed to deserialize directly into the same Swift models used locally. No transformation layer—just get the JSON, decode it, use it.

This is intentionally boring and stateless. The API doesn't manage sessions or track state. It's a dumb file server that happens to know about your music library.

Streaming & Caching Strategy

When you play a track from the remote library, this happens:

  1. Check cache: Is the file already downloaded locally?
  2. Download to cache: If not, fetch from Mac via the /stream endpoint
  3. Play from cache: Once available (or while downloading), send the file to AVAudioEngine and start playback
  4. Background queue: After the current track starts, the system queues remaining album/playlist tracks for download at low priority

This download-then-play model builds the foundation for offline listening later. Cached files aren't archived; they're ephemeral session cache for now. But the plumbing is there to evolve into "save for offline" when you're ready.

A new RemoteFileCache service manages this:

  • Priority queue (current/next tracks first)
  • LRU cleanup (old files removed when storage fills)
  • State tracking (pending, downloading, complete, failed)
  • Integrates seamlessly with existing PlaybackService

Why This Approach?

Simplicity first: No complex auth, no pairing dance, no cloud sync delays. Bonjour finds it. You tap it. It works.

Privacy by default: Your music library never leaves your home network. No cloud, no tracking, no ads.

Reuse everything: Existing UI components (ArtistsView, AlbumsView, playlists) work unchanged. Just swap the data source. This keeps the feature maintainable and reduces the risk of introducing bugs in existing browsing.

Service-oriented: Fits perfectly with Kanora's existing architecture. Tests mock the provider; implementation details (Core Data vs HTTP) are invisible to views.

Incremental delivery: We can ship discovery → browsing → streaming → offline download in phases without reworking the foundation each time.

Implementation Roadmap

Phase 1: Discovery & Sidebar (iOS)

  • Bonjour listener for _kanora._tcp
  • Store discovered hosts in Core Data
  • Sidebar picker to switch between libraries
  • Health check before connecting

Phase 2: Remote API (macOS)

  • REST endpoints for artists, albums, playlists, tracks
  • Audio file streaming with range request support
  • JSON serialization

Phase 3: Data Layer Abstraction (iOS)

  • LibraryServiceProvider protocol
  • LocalLibraryService refactored from existing queries
  • RemoteLibraryService HTTP client
  • ServiceContainer integration

Phase 4: Download & Streaming (iOS)

  • RemoteFileCache service
  • Download queue with priority
  • Background pre-download for albums/playlists
  • PlaybackService integration

Each phase ships working features. Phase 1 lets you browse remote libraries (read-only). Phase 4 adds streaming and offline prep.

What's Out of Scope (For Now)

  • Authentication: Trusting home network for MVP
  • Remote playback control: Telling your Mac what to play comes later
  • Offline archival: Download to keep feature is future work
  • External network access: Only home network for now
  • Multi-user sync: But stateless API means multiple devices can browse simultaneously

Why Now?

Streaming services have optimized for convenience, but they've also optimized for lock-in. Kanora is built on the belief that you should own your music library and be able to access it however you want, wherever you are—without paying per month.

The host/client architecture makes that real. Your Mac becomes the source of truth. Your phone becomes a window into that library. No internet required. No subscription. Just your music, your way.

This is what music software felt like when it trusted the user. Kanora's bringing it back.


Next post in this series: We'll dive into the Bonjour discovery implementation and how iOS finds Mac hosts on the network.