MemoryStorage, FileStorage, and Custom Storage in LINEJS: A Complete Guide

LINEJS provides three storage strategies—MemoryStorage for ephemeral in-memory caching, FileStorage for local JSON persistence, and custom storage implementations for external backends—all implementing the abstract BaseStorage interface defined in packages/linejs/base/storage/base.ts.

The LINEJS library (evex-dev/linejs) manages authentication tokens, encryption keys, and conversation caches through a pluggable storage layer. Understanding the difference between MemoryStorage, FileStorage, and custom storage options ensures your bot or client retains critical data across restarts or distributes state across multiple instances.

Understanding the Storage Architecture

The storage system in LINEJS revolves around a single abstract class that defines the contract between the client and its persistence layer.

The BaseStorage Contract

In packages/linejs/base/storage/base.ts, the BaseStorage abstract class defines five required methods that every implementation must support:

export abstract class BaseStorage {
  abstract set(key: Storage["Key"], value: Storage["Value"]): Promise<void>;
  abstract get(key: Storage["Key"]): Promise<Storage["Value"] | undefined>;
  abstract delete(key: Storage["Key"]): Promise<void>;
  abstract clear(): Promise<void>;
  abstract migrate(storage: BaseStorage): Promise<void>;
}

Any storage implementation—built-in or custom—must extend this class and handle these operations for the key-value pairs that LINEJS uses to maintain session state.

Built-in Storage Implementations

LINEJS ships with two concrete implementations that cover most development scenarios.

MemoryStorage (Volatile In-Memory)

Located in packages/linejs/base/storage/memory.ts, MemoryStorage stores data in a private Map object held in RAM. This implementation is the default when you create a client without specifying storage options, as noted in the source comments of packages/linejs/client/login.ts.

export class MemoryStorage extends BaseStorage {
  private data = new Map<Storage["Key"], Storage["Value"]>();
  // Methods operate directly on this.data
}

Data persists only for the lifetime of the Node.js or Deno process. When the process exits, authentication tokens and encryption keys disappear, forcing a fresh login on the next execution.

FileStorage (Persistent JSON)

For CLI tools and long-running bots that survive process restarts, FileStorage (in packages/linejs/base/storage/file.ts) serializes the storage state to a JSON file on disk using the Node.js fs module.

export class FileStorage extends BaseStorage {
  constructor(private path: string, extendData?: string) {
    // Initializes file path and optional extension data
  }
  // Reads/writes JSON on every get/set operation
}

This approach ensures your auth tokens and keys persist between runs without requiring re-authentication, though it is limited to single-instance deployments.

Implementing Custom Storage

When built-in options do not match your infrastructure requirements, you can implement custom storage by extending BaseStorage yourself.

When to Use Custom Storage

Choose a custom implementation when you need:

  • Distributed state: Share auth tokens across multiple instances using Redis or DynamoDB
  • Encryption at rest: Encrypt data before persisting to cloud storage
  • Browser compatibility: Use IndexedDB or localStorage in browser environments
  • Enterprise policies: Integrate with existing secret management systems

Example: Browser LocalStorage Implementation

The repository provides a working example in example/storage/local.ts that demonstrates how to wrap the browser's localStorage API:

import type { BaseStorage, Storage } from "@evex/linejs/storage";

export class LocalStorage implements BaseStorage {
  private readonly prefix = "linejs:";

  async set(key: Storage["Key"], value: Storage["Value"]) {
    localStorage.setItem(this.prefix + key, JSON.stringify(value));
  }

  async get(key: Storage["Key"]) {
    const raw = localStorage.getItem(this.prefix + key);
    return raw ? JSON.parse(raw) : undefined;
  }

  async delete(key: Storage["Key"]) {
    localStorage.removeItem(this.prefix + key);
  }

  async clear() {
    localStorage.clear();
  }

  async migrate(storage: BaseStorage) {
    // Copy existing entries to another storage if needed
  }
}

How to Configure Storage in Your Client

Pass your chosen storage instance through the storage property in client options, as documented in docs/docs/client-options.md.

Using MemoryStorage (Default)

import { loginWithPassword } from "@evex/linejs";

const client = await loginWithPassword({
  email: "you@example.com",
  password: "secure-password",
});
// Storage defaults to MemoryStorage; data is lost when the process exits

Using FileStorage for Persistence

import { loginWithAuthToken } from "@evex/linejs";
import { FileStorage } from "@evex/linejs/storage";

const client = await loginWithAuthToken("YOUR_AUTHTOKEN", {
  storage: new FileStorage("./linejs-storage.json"),
});
// Tokens persist in ./linejs-storage.json between restarts

Using Custom Storage

import { loginWithAuthToken } from "@evex/linejs";
import { LocalStorage } from "./path/to/local.ts";

const client = await loginWithAuthToken("YOUR_AUTHTOKEN", {
  storage: new LocalStorage(),
});

Summary

Frequently Asked Questions

What happens if I don't specify a storage option when creating a LINEJS client?

If you omit the storage parameter, the client defaults to MemoryStorage according to the implementation in packages/linejs/client/login.ts. Your authentication tokens and encryption keys remain in RAM only, disappearing completely when the Node.js or Deno process terminates, which forces re-authentication on the next run.

Can I switch from MemoryStorage to FileStorage without losing my current session?

No, because MemoryStorage does not persist data to disk. When you switch to FileStorage, you must provide a valid auth token to initialize the client. However, once running with FileStorage, the library automatically saves new tokens and keys to the specified JSON file path for future restarts.

Is FileStorage suitable for production environments with multiple server instances?

FileStorage is not recommended for horizontally scaled deployments. Because it reads from and writes to a single local filesystem path, concurrent access from multiple processes can cause race conditions or data corruption. For production clusters, implement a custom storage backend using Redis, PostgreSQL, or another distributed store that handles concurrent access safely.

What data does LINEJS actually store in these storage backends?

LINEJS stores authentication tokens, device registration information, encryption keys for E2EE conversations, and internal caching metadata. The specific keys and value types are defined by the Storage interface referenced in packages/linejs/base/storage/base.ts, ensuring type safety across all implementations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →