Core Philosophy
The architecture follows these principles:- Never Fail on User Error - Configuration errors are isolated and reported, never crash the app
- Reactive E2E - Changes propagate from filesystem to UI without restarts
- Type Safety First - Runtime validation at every boundary
- Cache as Database - External API calls are expensive; cache aggressively
- Defense in Depth - Hash-based package IDs prevent arbitrary API requests
System Architecture
Key Architectural Patterns
1. Config-First Design
Unlike traditional apps that store configuration in a database, Shipped uses YAML files as runtime configuration. This enables:- Easy editing without UI complexity - just edit files
- Atomic updates via file replacement
- Fast startup - no database queries needed
- Hot reloading - changes reflected immediately without restarts
config/ directory on your deployed server and are watched for changes.
Learn More
Deep dive into the configuration system architecture
2. Package Hashing
Packages are identified by a hash of their complete configuration, not by name. This provides:- Security - Only pre-configured packages can be queried
- Cache Invalidation - Changing any config property changes the hash
- Uniqueness - Same package with different provider settings = different hash
Learn More
Learn about the package system architecture
3. Effect TS on Server
The server uses Effect TS for all business logic, providing:- Typed errors - Every failure case is explicit
- Composability - Effects chain and combine safely
- Testability - Easy to mock and test
- Resource safety - Automatic cleanup and error handling
server/services/config/index.ts:12:
4. Multi-Layer Caching
Package data is cached in two layers:- L1 (Memory) - Fast access, lost on restart
- L2 (File) - Survives restarts, slower
5. Request Coalescing
Multiple concurrent requests for the same package result in a single external API call:server/libs/cache/coalescing-cache.ts:48:
Data Flow
Config Loading Flow
Package Fetching Flow
Project Structure
Key Directories Explained
libs/ vs server/libs/
libs/- Shared domain logic, no server dependencies, safe for clientserver/libs/- Server-only infrastructure (cache, file system, errors)
layers/ - Nuxt Layer Organization
01-base/- Core functionality, RPC, config system02-packages/- Package-specific UI components
Technology Stack
| Layer | Technology | Purpose |
|---|---|---|
| Frontend | Nuxt 4 + Vue 3 | SSR, routing, UI framework |
| Styling | Tailwind CSS + Nuxt UI | Consistent design system |
| Data Fetching | TanStack Query | Client-side data fetching, caching, and synchronization |
| Backend | Nuxt Server + Nitro | API layer |
| Server Logic | Effect TS | Functional programming, error handling |
| Validation | Effect Schema & Zod v4 | Effect Schema for server logic, Zod for RPC boundaries |
| RPC | ORPC | Type-safe API calls |
| Caching | BentoCache | Multi-layer caching |
| Config | chokidar | Config file watching and hot reloading |
Why This Architecture?
Why YAML over Database?
- Easy editing without UI complexity
- Hot reloading - changes reflect immediately
- No database migrations for config schema changes
- Simple backup/restore (just copy files)
Why Effect TS?
Traditional try/catch error handling is implicit and easy to miss. Effect makes errors:- Explicit in the type signature
- Composable - errors propagate through chains
- Recoverable - easy to catch and handle specific cases
Why Hash-Based Package IDs?
Security is paramount for self-hosted apps. By requiring packages to be pre-configured:- Users can’t probe arbitrary packages
- API rate limits are predictable
- Configuration drives all behavior
Why SSE over WebSockets?
Server-Sent Events provide:- Simple reconnection - automatic with EventSource
- HTTP-based - works through proxies/firewalls
- One-way streaming - perfect for config updates
- Less overhead - no handshake for each client
Effect TS Patterns
Services and Dependency Injection
Effect TS uses a service-oriented architecture with compile-time dependency injection: Defining a Service (server/services/config/index.ts:10):
Error Handling
Effect TS makes all errors explicit in type signatures: Fromserver/libs/provider/index.ts:7:
Effect.Effect<Package, ProviderError> explicitly declares:
- Success type:
Package - Error types:
ProviderError(union of specific errors) - Dependencies: None in this simplified signature
Resource Management
Effect provides automatic cleanup withEffect.acquireRelease:
From server/libs/chokidar/index.ts:18:
Next Steps
Config System
Deep dive into configuration loading, validation, and hot-reloading
Package System
Learn about package hashing, caching, and security model