Snapshot Filesystem
Understanding the virtual /snapshot/ filesystem in packaged Hakobu binaries.
When Hakobu packages your application, all source files, dependencies, and assets are embedded into a virtual snapshot filesystem inside the binary. At runtime, this filesystem is mounted at the /snapshot/ prefix and is transparently accessible through Node.js APIs.
The /snapshot/ Prefix
All packaged files live under /snapshot/ at runtime. For example, if your project is called my-app, the entry point might be located at:
/snapshot/my-app/index.jsYour node_modules and assets are also under this prefix:
/snapshot/my-app/node_modules/express/index.js
/snapshot/my-app/templates/email.html
/snapshot/my-app/config.yamlHow require() Resolves Paths
Node's require() works transparently with snapshot paths. When your code does:
const express = require('express');The module resolution follows the normal Node.js algorithm, but resolves into /snapshot/... paths. This is handled by the patched Node binary -- no code changes are needed in your application.
__dirname and __filename
In CJS modules, __dirname and __filename point to snapshot filesystem locations:
console.log(__dirname);
// => /snapshot/my-app/src
console.log(__filename);
// => /snapshot/my-app/src/index.jsIn ESM modules, use import.meta.url instead:
console.log(import.meta.url);
// => file:///snapshot/my-app/src/index.mjsYou can derive __dirname and __filename from import.meta.url:
import { dirname } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);process.cwd() vs Snapshot Paths
process.cwd() returns the real working directory on the host filesystem -- not a snapshot path. This is an important distinction:
| API | Returns | Example |
|---|---|---|
__dirname | Snapshot path (virtual) | /snapshot/my-app/src |
__filename | Snapshot path (virtual) | /snapshot/my-app/src/index.js |
process.cwd() | Real filesystem path | /home/user |
process.execPath | Real path to the binary | /usr/local/bin/my-app |
Use process.cwd() or process.execPath when you need to read/write files on the real filesystem (logs, user data, config files created at runtime). Use __dirname or import.meta.url when you need to access files embedded in the snapshot (templates, bundled assets).
fs API Behavior on Snapshot Paths
The fs module is patched in Hakobu binaries to handle snapshot paths transparently:
Reading Works
const fs = require('fs');
const path = require('path');
// Reading from snapshot -- works
const template = fs.readFileSync(path.join(__dirname, 'template.html'), 'utf-8');
const data = fs.readFileSync(path.join(__dirname, 'data.json'), 'utf-8');Both synchronous (readFileSync) and asynchronous (readFile) reads work on snapshot paths.
Writing Does Not Work
The snapshot filesystem is read-only. Attempting to write to a snapshot path will throw an error:
// This will throw -- snapshot is read-only
fs.writeFileSync(path.join(__dirname, 'output.txt'), 'data');To write files, use a real filesystem path:
const os = require('os');
const outputDir = path.join(os.homedir(), '.my-app');
fs.mkdirSync(outputDir, { recursive: true });
fs.writeFileSync(path.join(outputDir, 'output.txt'), 'data');Directory Listing Works
// Lists files embedded in the snapshot
const files = fs.readdirSync(path.join(__dirname, 'templates'));fs.existsSync Works
// Checks existence within the snapshot
if (fs.existsSync(path.join(__dirname, 'config.yaml'))) {
// load config
}Common Patterns
Writing logs or data to the real filesystem
const path = require('path');
const fs = require('fs');
// Use the directory where the binary lives
const execDir = path.dirname(process.execPath);
const logPath = path.join(execDir, 'app.log');
fs.appendFileSync(logPath, `${new Date().toISOString()} started\n`);Serving static files with a web framework
const express = require('express');
const path = require('path');
const app = express();
// Serves files from the snapshot -- works for reads
app.use('/static', express.static(path.join(__dirname, 'public')));Remember that snapshot paths like /snapshot/... only exist inside the packaged binary's runtime. They are not real paths on the host filesystem. If you pass a snapshot path to an external process or tool, it will not be able to access it.