Runnova VFS is an advanced, yet usable Origin Private File System (OPFS) based Virtual File System (VFS) for the web written in vanilla Javascript. Versions of this are used in my different projects.
vfs/
├── nodes/
│ └── <nodeId> # One OPFS file per node (JSON format)
├── content/
│ ├── <contentId> # Raw byte storage; contentId = SHA-256 hex (content-addressable)
│ └── <contentId>.rc # Reference count (plain integer text file)
├── idx/
│ └── <parentId>/ # Directory tracking the children of a specific folder
│ └── <key> # File mapped to a value containing the target nodeId
└── meta/
├── root # Root configuration JSON: { rootId }
├── version # Current layout version string
└── gen # Random token rewritten on every mutation (cache invalidation)- Short Names:
n + encodeURIComponent(name) - Long Names:
h + sha256(name)
Every file or folder in the nodes/ directory follows this data structure:
{
"id": "string",
"name": "string",
"parent": "string | null",
"type": "file | directory",
"contentId": "string (optional)",
"meta": {
"created": 1718841600000,
"modified": 1718841600000
}
}- Every public VFS operation runs under a unified Web Lock named "ozone-vfs".
- Shared Locks are acquired for read operations.
- Exclusive Locks are acquired for write/mutation operations.
- This model guarantees synchronization across multiple browser tabs, Web Workers, and the Service Worker.
- Fallback: In environments where the Web Locks API is unavailable, the system defaults to an in-process execution queue (limiting safety to a single execution context).
- The system maintains an in-memory node cache.
- At the start of every locked operation, the cache is validated against the value stored in meta/gen.
- If meta/gen has changed, the cache is discarded, ensuring no context ever reads stale data after a concurrent mutation.
Because OPFS does not natively support multi-file transactions, atomic mutations span multiple files. The VFS enforces a strict operational order to maintain system integrity:
- Safe Failures: Every multi-step operation is ordered so that an unexpected crash can only cause resource leaks (e.g., orphaned content blobs, stale reference counts, or dangling index entries).
- Corruption Prevention: The system guarantees that a crash will never leave a node pointing to missing or unwritten data.
- Self-Healing: An asynchronous file system check (fsck()) routine runs periodically or on boot to safely detect, clean up, and repair any leaked resources.
- Memory Constraints: Hashing requires buffering the entire file into memory because the Web Crypto API (SubtleCrypto) does not provide an incremental, streaming API.
- Buffer Stability: Callers must ensure that the input data buffer is never mutated while a writeFile() operation is still executing.