Overview
Shipped uses a unique hash-based package identification system to secure the application against unauthorized access to external package registries. Unlike traditional REST APIs that accept arbitrary resource identifiers, Shipped requires all packages to be pre-configured, and each package is identified by a cryptographic hash of its configuration.The Security Problem
Traditional Approach
Most package tracking services use human-readable identifiers:- Arbitrary access - Anyone can query any package
- API abuse - Users can exhaust rate limits by probing random packages
- Privacy leaks - Attackers can probe for private package names
- Unpredictable costs - Cannot forecast external API usage
Shipped’s Approach
Shipped uses content-addressed hashes:- Package name
- Provider type
- Package-level configuration
- Provider-level configuration
- Pre-configuration required - Only configured packages are accessible
- Rate limit predictability - Known package set = predictable API usage
- No probing - Cannot guess valid hashes
- Automatic cache invalidation - Config changes produce new hashes
Hash Generation
What’s in the Hash?
The package hash is computed from its complete configuration:docs/architecture/package-system.md:110-143
Implementation
docs/architecture/package-system.md:124-143
Development vs Production
| Environment | Format | Example | Purpose |
|---|---|---|---|
| Development | name:provider:extras_hash:provider_hash | vuejs/vue:github:a1b2:c3d4 | Human-readable debugging |
| Production | Cryptographic hash | a1b2c3d4e5f6789... | Security through obscurity |
Security Validation
Request Flow
Every package request must pass hash validation:Hash Validation Logic
docs/architecture/package-system.md:406-427
The Package Map
All valid packages are stored in an O(1) lookup map:docs/architecture/package-system.md:188-214
This provides constant-time validation regardless of package count.
Attack Prevention
Scenario 1: Package Probing
Attack: Try to discover configured packages by guessing hashes- Hash space: 2^256 possible hashes (SHA-256)
- Success probability: ~1 in 10^77 per guess
- Infeasible: Would take trillions of years to find one valid hash
Scenario 2: Arbitrary Package Access
Attack: Try to fetch unconfigured packages- User cannot know the
providerExtravalues used in the hash - Even if they guess the package spec, the provider config will be wrong
- Result: Hash mismatch → 404 Not Found
Scenario 3: API Rate Limit Exhaustion
Attack: Make rapid requests to exhaust external API rate limits- Hash validation - Invalid hashes rejected before external API call
- Rate limiting - ORPC middleware limits requests per IP
- Caching - Valid requests are cached (no repeated API calls)
- Request coalescing - Duplicate requests share single API call
Scenario 4: Configuration Injection
Attack: Try to inject malicious config to create new hashes Defense: Config is file-based, not user-editable- Config files live on the server filesystem
- No API endpoints accept config changes
- Users cannot modify
lists.yamlwithout server access - If attacker has server access, game over anyway (different threat model)
Configuration Security
Package Whitelisting
All accessible packages must be declared in configuration:Provider Configuration Isolation
Provider settings affect the hash:Cache Invalidation
Changing any hashed configuration produces a new hash:abc123 is not returned for new config def456. Each config variation has its own isolated cache entry.
Reference: Described in docs/architecture/package-system.md:116-120
Threat Model
In Scope
Threats the hash system protects against:| Threat | Protection |
|---|---|
| Unauthorized package access | Hash validation blocks unconfigured packages |
| API rate limit abuse | Predictable package set limits external calls |
| Package enumeration | Hashes are not guessable or reversible |
| Cache poisoning | Config changes invalidate old hashes |
| Privilege escalation | No API to add packages without config file access |
Out of Scope
Threats the hash system does NOT protect against:| Threat | Reason |
|---|---|
| Server compromise | Attacker with server access can modify config files |
| DDoS attacks | Rate limiting handles this, not hashing |
| External API compromise | Shipped trusts provider APIs (GitHub, NPM, etc.) |
| Stolen credentials | Config file access = legitimate access |
Assumptions
- Config files are secure - Only authorized users can modify them
- Hash function is secure - Using
ohashwith SHA-256 - Client cannot forge hashes - Hashing is server-side only
- Provider APIs are trusted - We trust GitHub, NPM, etc.
Best Practices
Minimize Package Exposure
Only configure packages you actually need:Protect Configuration Files
Use proper file permissions:Use Environment Variables for Secrets
Never put secrets in config files:Monitor for Suspicious Activity
Log failed hash validations:Rate Limiting
Combine hash security with rate limiting:server/rpc/routes/config.ts:75-81
Limitations
Not True Access Control
The hash system is not a substitute for authentication:- Anyone with a valid hash can access the package
- Hashes are included in URLs (visible in logs, browser history)
- If you need true access control, add authentication middleware
Development Mode Exposure
Development hashes are human-readable:- Package name
- Provider type
- Partial configuration hashes
Client-Side Visibility
Hashes are visible to clients:Summary
Shipped’s hash-based security model provides:- Configuration-driven access control - Only pre-configured packages are accessible
- Automatic cache invalidation - Config changes produce new hashes
- Attack prevention - Cannot guess, enumerate, or inject package requests
- Rate limit predictability - Known package set enables forecasting
- Defense in depth - Combines with caching, coalescing, and rate limiting
- Admin controls configuration via files
- Users consume pre-configured packages
- External API costs must be predictable
- Attack surface should be minimal