Public preview · Registration and live billing are not open.
Anti-AIClonePILOT

LICENSED SOURCE · MIT

ASAR Archives

After creating an application distribution, the app's source code is usually bundled into an ASAR archive, which is a simple extensive archive format designed for Electron apps. By bundling the app we can mitigate issues around long path names on Windows, speed up require and conceal your source code from cursory inspection.

The bundled app runs in a virtual file system and most APIs would just work normally, but for some cases you might want to work on ASAR archives explicitly due to a few caveats.

Using ASAR Archives

In Electron there are two sets of APIs: Node APIs provided by Node.js and Web APIs provided by Chromium. Both APIs support reading files from ASAR archives.

Node API

With special patches in Electron, Node APIs like fs.readFile and require treat ASAR archives as virtual directories, and the files in it as normal files in the filesystem.

For example, suppose we have an example.asar archive under /path/to:

$ asar list /path/to/example.asar
/app.js
/file.txt
/dir/module.js
/static/index.html
/static/main.css
/static/jquery.min.js

Read a file in the ASAR archive:

const fs = require('node:fs')

fs.readFileSync('/path/to/example.asar/file.txt')

List all files under the root of the archive:

const fs = require('node:fs')

fs.readdirSync('/path/to/example.asar')

Use a module from the archive:

require('./path/to/example.asar/dir/module.js')

Directories in an archive can also be iterated with fs.opendir, and symbolic links stored in an archive can be inspected with fs.readlink, which returns the link's target relative to the link, like a real filesystem would.

You can also display a web page in an ASAR archive with BrowserWindow:

const { BrowserWindow } = require('electron')

const win = new BrowserWindow()

win.loadURL('file:///path/to/example.asar/static/index.html')

Web API

In a web page, files in an archive can be requested with the file: protocol. Like the Node API, ASAR archives are treated as directories.

For example, to get a file with $.get:

<script>
let $ = require('./jquery.min.js')
$.get('file:///path/to/example.asar/file.txt', (data) => {
  console.log(data)
})
</script>

Treating an ASAR archive as a Normal File

For some cases like verifying the ASAR archive's checksum, we need to read the content of an ASAR archive as a file. For this purpose you can use the built-in original-fs module which provides original fs APIs without asar support:

const originalFs = require('original-fs')

originalFs.readFileSync('/path/to/example.asar')

You can also set process.noAsar to true to disable the support for asar in the fs module:

const fs = require('node:fs')

process.noAsar = true
fs.readFileSync('/path/to/example.asar')

Limitations of the Node API

Even though we tried hard to make ASAR archives in the Node API work like directories as much as possible, there are still limitations due to the low-level nature of the Node API.

Archives Are Read-only

The archives can not be modified so all Node APIs that can modify files will not work with ASAR archives.

Working Directory Can Not Be Set to Directories in Archive

Though ASAR archives are treated as directories, there are no actual directories in the filesystem, so you can never set the working directory to directories in ASAR archives. Passing them as the cwd option of some APIs will also cause errors.

File Descriptors for Files in Archives

fs.open, fs.openSync and fs.promises.open return real file descriptors (and FileHandles) for files in ASAR archives, backed by the archive itself, so APIs built on top of them - fs.read, fs.readv, fs.fstat, fs.readFile(fd), fs.createReadStream, FileHandle#readFile, FileHandle#createReadStream, and so on - read directly out of the archive without any temporary copy. Likewise fs.copyFile, fs.cp and their sync and promise variants copy straight from the archive to the destination.

Because archives are read-only, opening a file inside an archive with any flag that allows writing (w, a, r+, ...) fails with EACCES, and fs.fchmod, fs.fchown and fs.futimes on such a descriptor fail with EACCES too. The descriptor only identifies the entry to Node's fs module; it is not backed by the file's contents, so passing it to code outside of fs (for example a native addon that reads from the raw descriptor, child_process stdio, net.Socket({ fd }) or http2stream.respondWithFile()) fails with EBADF and is not supported.

Extra Unpacking on Some APIs

For APIs that rely on passing the real file path to underlying system calls, Electron will extract the needed file into a temporary file and pass the path of the temporary file to the APIs to make them work. This adds a little overhead for those APIs.

APIs that require extra unpacking are:

  • child_process.execFile
  • child_process.execFileSync
  • process.dlopen - Used by require on native modules

Fake Stat Information of fs.stat

The Stats object returned by fs.stat and its friends on files in asar archives is generated by guessing, because those files do not exist on the filesystem. So you should not trust the Stats object except for getting file size and checking file type.

Executing Binaries Inside ASAR archive

There are Node APIs that can execute binaries like child_process.exec, child_process.spawn and child_process.execFile, but only execFile is supported to execute binaries inside ASAR archive.

This is because exec and spawn accept command instead of file as input, and commands are executed under shell. There is no reliable way to determine whether a command uses a file in asar archive, and even if we do, we can not be sure whether we can replace the path in command without side effects.

Adding Unpacked Files to ASAR archives

As stated above, some Node APIs will unpack the file to the filesystem when called. Apart from the performance issues, various anti-virus scanners might be triggered by this behavior.

As a workaround, you can leave various files unpacked using the --unpack option. In the following example, shared libraries of native Node.js modules will not be packed:

$ asar pack app app.asar --unpack *.node

After running the command, you will notice that a folder named app.asar.unpacked was created together with the app.asar file. It contains the unpacked files and should be shipped together with the app.asar archive.

Code license and snapshot

Text license: MIT · Code examples: MIT

Code copyright and full license notice
Copyright (c) Electron contributors
Copyright (c) 2013-2020 GitHub Inc.

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Original Markdown and integrity digest

SHA-256: 3e4ca507313870cf8e8d81fc93b3a7d07f93703ee42a0b7bcc4d80239cf61a02

---
title: ASAR Archives
description: What is ASAR archive and how does it affect the application.
slug: asar-archives
hide_title: false
---

After creating an [application distribution](application-distribution.md), the
app's source code is usually bundled into an [ASAR archive](https://github.com/electron/asar),
which is a simple extensive archive format designed for Electron apps. By bundling the app
we can mitigate issues around long path names on Windows, speed up `require` and conceal your source
code from cursory inspection.

The bundled app runs in a virtual file system and most APIs would just work
normally, but for some cases you might want to work on ASAR archives explicitly
due to a few caveats.

## Using ASAR Archives

In Electron there are two sets of APIs: Node APIs provided by Node.js and Web
APIs provided by Chromium. Both APIs support reading files from ASAR archives.

### Node API

With special patches in Electron, Node APIs like `fs.readFile` and `require`
treat ASAR archives as virtual directories, and the files in it as normal
files in the filesystem.

For example, suppose we have an `example.asar` archive under `/path/to`:

```sh
$ asar list /path/to/example.asar
/app.js
/file.txt
/dir/module.js
/static/index.html
/static/main.css
/static/jquery.min.js
```

Read a file in the ASAR archive:

```js
const fs = require('node:fs')

fs.readFileSync('/path/to/example.asar/file.txt')
```

List all files under the root of the archive:

```js
const fs = require('node:fs')

fs.readdirSync('/path/to/example.asar')
```

Use a module from the archive:

```js @ts-nocheck
require('./path/to/example.asar/dir/module.js')
```

Directories in an archive can also be iterated with `fs.opendir`, and
symbolic links stored in an archive can be inspected with `fs.readlink`, which
returns the link's target relative to the link, like a real filesystem would.

You can also display a web page in an ASAR archive with `BrowserWindow`:

```js
const { BrowserWindow } = require('electron')

const win = new BrowserWindow()

win.loadURL('file:///path/to/example.asar/static/index.html')
```

### Web API

In a web page, files in an archive can be requested with the `file:` protocol.
Like the Node API, ASAR archives are treated as directories.

For example, to get a file with `$.get`:

```html
<script>
let $ = require('./jquery.min.js')
$.get('file:///path/to/example.asar/file.txt', (data) => {
  console.log(data)
})
</script>
```

### Treating an ASAR archive as a Normal File

For some cases like verifying the ASAR archive's checksum, we need to read the
content of an ASAR archive as a file. For this purpose you can use the built-in
`original-fs` module which provides original `fs` APIs without `asar` support:

```js
const originalFs = require('original-fs')

originalFs.readFileSync('/path/to/example.asar')
```

You can also set `process.noAsar` to `true` to disable the support for `asar` in
the `fs` module:

```js
const fs = require('node:fs')

process.noAsar = true
fs.readFileSync('/path/to/example.asar')
```

## Limitations of the Node API

Even though we tried hard to make ASAR archives in the Node API work like
directories as much as possible, there are still limitations due to the
low-level nature of the Node API.

### Archives Are Read-only

The archives can not be modified so all Node APIs that can modify files will not
work with ASAR archives.

### Working Directory Can Not Be Set to Directories in Archive

Though ASAR archives are treated as directories, there are no actual
directories in the filesystem, so you can never set the working directory to
directories in ASAR archives. Passing them as the `cwd` option of some APIs
will also cause errors.

### File Descriptors for Files in Archives

`fs.open`, `fs.openSync` and `fs.promises.open` return real file descriptors
(and `FileHandle`s) for files in ASAR archives, backed by the archive itself,
so APIs built on top of them - `fs.read`, `fs.readv`, `fs.fstat`,
`fs.readFile(fd)`, `fs.createReadStream`, `FileHandle#readFile`,
`FileHandle#createReadStream`, and so on - read directly out of the archive
without any temporary copy. Likewise `fs.copyFile`, `fs.cp` and their sync
and promise variants copy straight from the archive to the destination.

Because archives are read-only, opening a file inside an archive with any
flag that allows writing (`w`, `a`, `r+`, ...) fails with `EACCES`, and
`fs.fchmod`, `fs.fchown` and `fs.futimes` on such a descriptor fail with
`EACCES` too. The descriptor only identifies the entry to Node's `fs` module;
it is not backed by the file's contents, so passing it to code outside of
`fs` (for example a native addon that reads from the raw descriptor,
`child_process` `stdio`, `net.Socket({ fd })` or
`http2stream.respondWithFile()`) fails with `EBADF` and is not supported.

### Extra Unpacking on Some APIs

For APIs that rely on passing the real file path to underlying system calls,
Electron will extract the needed file into a temporary file and pass the path
of the temporary file to the APIs to make them work. This adds a little
overhead for those APIs.

APIs that require extra unpacking are:

* `child_process.execFile`
* `child_process.execFileSync`
* `process.dlopen` - Used by `require` on native modules

### Fake Stat Information of `fs.stat`

The `Stats` object returned by `fs.stat` and its friends on files in `asar`
archives is generated by guessing, because those files do not exist on the
filesystem. So you should not trust the `Stats` object except for getting file
size and checking file type.

### Executing Binaries Inside ASAR archive

There are Node APIs that can execute binaries like `child_process.exec`,
`child_process.spawn` and `child_process.execFile`, but only `execFile` is
supported to execute binaries inside ASAR archive.

This is because `exec` and `spawn` accept `command` instead of `file` as input,
and `command`s are executed under shell. There is no reliable way to determine
whether a command uses a file in asar archive, and even if we do, we can not be
sure whether we can replace the path in command without side effects.

## Adding Unpacked Files to ASAR archives

As stated above, some Node APIs will unpack the file to the filesystem when
called. Apart from the performance issues, various anti-virus scanners might
be triggered by this behavior.

As a workaround, you can leave various files unpacked using the `--unpack` option.
In the following example, shared libraries of native Node.js modules will not be
packed:

```sh
$ asar pack app app.asar --unpack *.node
```

After running the command, you will notice that a folder named `app.asar.unpacked`
was created together with the `app.asar` file. It contains the unpacked files
and should be shipped together with the `app.asar` archive.

Our original analysis