Skip to main content

Storage Layer

Audience: End users, system administrators Last Updated: 2026-04-05 Version: 2.0.0


Overview

The Storage Layer is Llamafin's persistent data foundation. It saves your settings, authentication state, playback preferences, download queues, and scheduler jobs so that everything is restored when you reopen the app — even after a restart or update.

Built on IndexedDB (the modern browser standard for client-side storage), it provides a reliable key-value store that works across all platforms: iOS, Android, Windows, macOS, and Linux.

What It Does

  • Persists Your Session: Keeps you logged in between app restarts
  • Saves Settings: All preferences, themes, audio settings, and configurations
  • Restores Playback State: Remembers what you were listening to, shuffle/repeat mode
  • Maintains Download Queues: Offline downloads survive app closures
  • Preserves Scheduler Jobs: Background tasks are saved and resume on restart
  • Stores Device Metrics: Historical performance data for analysis
  • Caches Library State: Active library selection and browsing position

Key Concepts

IndexedDB Storage

Llamafin uses IndexedDB as its primary storage engine. This is the same storage technology used by modern web browsers and mobile app web views for large-scale client-side data persistence.

PropertyValue
Storage TypeIndexedDB (via Ionic Storage library)
FallbacklocalStorage (if IndexedDB unavailable)
Data FormatJSON objects (key-value pairs)
CapacityTypically 50MB-1GB+ depending on platform
PersistenceSurvives app restarts and browser closes

Key-Value Model

All data is stored as simple key-value pairs, like a dictionary:

ConceptDescriptionExample
KeyA unique identifier string"settings", "auth", "downloads"
ValueAny JSON-serializable dataObjects, arrays, strings, numbers, booleans

Startup initialisation

Storage is the first feature initialised when the app loads:

  1. App starts → Storage initialisation begins
  2. IndexedDB connection established
  3. storageReady signal sent
  4. Core services begin loading (settings, downloads, network)
  5. App continues startup

If storage fails to initialise within 10 seconds, the app startup times out and reports an error.


Configuration

The Storage Layer operates transparently with no user-facing configuration. However, several application-level settings interact with storage:

Version-Based Migration

The app tracks schema versions for major features:

Version KeyCurrent VersionPurpose
llamafin_settings_db_version49Settings schema version
llamafin_downloads_db_version49Downloads schema version

When these version numbers increase (via app updates), migration logic runs automatically to update stored data to the new format.

Storage Keys

The app uses 29 distinct storage keys across all features:

CategoryKeysPurpose
Authenticationauth, user_id, hashed_user_idSession and identity
Settingssettings, download_settings, device_detailsUser preferences
Playbackcurrent_item, shuffle_status, repeat_status, playback_targetPlayer state
Librarymusic_id, active_library_id, offline_libraryBrowsing state
Downloadsdownloads, llamafin_downloads_db_versionDownload queues
NetworknetworkSpeed history
DJ ModedjActive mode
SchedulerschedulerBackground jobs
Device Metricsdevice-metricsPerformance history
Player UIplayer-uiInterface preferences
PlaystateplaystatePlayback reports
Systemjellyfin_url, llamafin_app_versionServer and version
Devicemobile_performance_settings_applied, hardware_specsDevice info
Connectplayback_targetRemote control target
Securityllamafin_device_identityPublic device identity
UIlast_tip_indexLoading screen tips

How It Works

Storage initialisation Flow

App Launch

├── Storage initialisation triggered

├── IndexedDB connection opened
│ │
│ └── Wait for connection ready

├── storageReady signal dispatched

├── App version check
│ └── Clear cache if version changed

└── Core services begin loading
├── Settings loaded from storage
├── Download queues restored
└── Network listeners activated

Data Access Pattern

Every storage operation follows this pattern:

  1. Check Ready: Is the storage instance initialised?
  2. Wait if Needed: If not ready, wait (blocks until ready)
  3. Execute Operation: Read or write the data
  4. Return Result: Resolve with the value or error

This ensures that even if data is requested immediately after app launch, it will wait for storage to be ready rather than fail.

Persistence Strategy

OperationBehaviour
WriteData is immediately written to IndexedDB
ReadData is read directly from IndexedDB
DeleteKey is removed from IndexedDB
ClearAll keys are wiped from IndexedDB

There is no caching layer between the app and IndexedDB — every read/write goes directly to the database.

Storage Lifecycle

┌─────────────────────────────────────────────────────┐
│ Storage Lifecycle │
│ │
│ App Start │
│ ↓ │
│ ┌──────────────────────────┐ │
│ │ Initialise IndexedDB │ │
│ └──────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────┐ │
│ │ storageReady signal │ │
│ └──────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────┐ │
│ │ Load settings, downloads │ │
│ │ network from storage │ │
│ └──────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────┐ │
│ │ App is running │ │
│ │ - Reads/writes on demand │ │
│ │ - No caching layer │ │
│ └──────────────────────────┘ │
│ ↓ │
│ App Close (no cleanup needed) │
│ Data persists in IndexedDB │
└─────────────────────────────────────────────────────┘

Integration with Other Features

Authentication

Auth data is stored on login and restored on startup:

KeyContentPurpose
authFull authentication objectSession restoration
user_idUser identifierQuick user identification
hashed_user_idSHA-256 hash of user IDAnonymous identification

Settings System

All user preferences are persisted under the settings key. This includes:

  • Account preferences
  • Server configuration
  • Language settings
  • Appearance (themes, fonts, visualizers)
  • Playback settings (EQ, audio effects)
  • Music quality preferences
  • Download settings
  • Notification preferences
  • Advanced settings
  • Scheduler configuration

Downloads System

Download queues and metadata are persisted:

KeyContent
downloadsActive download queue
llamafin_downloads_db_versionSchema version for migration

Job Scheduler

Background jobs are saved and restored on restart:

KeyContent
schedulerAll jobs (pending, running, completed)

This means interrupted downloads or analysis jobs resume when the app restarts.

Player State

Playback state is persisted for seamless restoration:

KeyContent
current_itemCurrently playing track
shuffle_statusShuffle enabled/disabled
repeat_statusRepeat mode (off, all, one)
playback_targetremote device control target

Device Metrics

Historical performance data is stored for trend analysis:

KeyContent
device-metricsHistorical CPU, RAM, battery, network data

Network Management

Network speed history is persisted:

KeyContent
networkHistorical speed measurements (up to 10,000 entries)

CryptoService (Security)

Device identity for P2P connections uses secure storage:

KeyContentStorage Location
llamafin_device_identityPublic key + device IDStandard IndexedDB
Private keyECDH/RSA key pairNative secure keystore (iOS Keychain / Android Keystore)

The private key is never stored in IndexedDB — it stays in the platform's secure keystore.


Error Handling & Reliability

Self-Healing Mechanisms

ScenarioBehaviour
Storage not readyOperations wait (block) until storage is initialised
Storage initialisation failureApp startup times out after 10 seconds with error
Corrupted dataFeatures handle errors individually with fallbacks
Storage fullErrors propagate to individual features for handling
IndexedDB unavailableFalls back to localStorage automatically

Known Limitations

LimitationImpact
No encryption at restAuth tokens and settings are stored as plain JSON in IndexedDB
No built-in versioningSchema migrations handled individually by each feature
No centralized key managementStorage keys are scattered as magic strings across the codebase
No storage quota monitoringApp does not track or warn about approaching storage limits
No data export/importUsers cannot backup or restore settings manually

Startup Timeout

If storage fails to initialise within 10 seconds, the app loading sequence times out. This prevents the app from hanging indefinitely on a broken storage system.


Technical Specifications

SpecificationValue
Storage engineIndexedDB (via Ionic Storage + localForage)
Fallback chainIndexedDB → WebSQL → localStorage
Data formatJSON (key-value pairs)
Access patternAsync (Promise-based)
initialisationLazy (on first access)
Startup timeout10 seconds
Total storage keys29 distinct keys
Consuming features18+ features/services
Package version@ionic/storage-angular v4.0.0-next.4
LocalForage versionBundled with Ionic Storage
EncryptionNone at rest (except native secure keystore for private keys)
MigrationPer-feature version keys (settings v49, downloads v49)

Security Considerations

What is Stored

Data TypeEncrypted?Location
Auth tokensNoIndexedDB (plain JSON)
User IDNoIndexedDB (plain text)
SettingsNoIndexedDB (plain JSON)
Playback stateNoIndexedDB (plain JSON)
Download queuesNoIndexedDB (plain JSON)
Private crypto keysYesNative secure keystore only
Public crypto identityNoIndexedDB (plain JSON)

What is Protected

  • Private keys for P2P encryption are stored in the platform's secure keystore (iOS Keychain or Android Keystore) — never in IndexedDB
  • User ID hashing uses SHA-256 to create an anonymous identifier for tracking purposes

Recommendations

For users concerned about data security:

  • Use device-level encryption (FileVault on macOS, BitLocker on Windows)
  • Enable screen lock on mobile devices
  • Clear app data before selling or disposing of a device
  • Log out when using shared devices


Last Updated: 2026-04-05 Version: 2.0.0