# bare-pipe

> Native I/O pipes for JavaScript

Latest version **4.4.0** (published 2026-10-14) · Apache-2.0 license · 0 weekly downloads

## Install

```sh
npm install bare-pipe
pnpm add bare-pipe
yarn add bare-pipe
bun add bare-pipe
```

## Health

**Score 75/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 4.4.0 |
| Published | 2026-10-14 |
| First published | 2023-05-16 |
| Weekly downloads | 0 |
| License | Apache-2.0 |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 2 |
| Unpacked size | 768.3 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 6 |
| Author | Holepunch |
| Maintainers | mafintosh |

## Links

- npm: https://www.npmjs.com/package/bare-pipe
- Repository: https://github.com/holepunchto/bare-pipe
- Homepage: https://github.com/holepunchto/bare-pipe#readme
- Issues: https://github.com/holepunchto/bare-pipe/issues
- npm.io page: https://npm.io/package/bare-pipe

## Dependencies (2)

- [bare-events](https://npm.io/package/bare-events.md) ^2.5.4
- [bare-stream](https://npm.io/package/bare-stream.md) ^2.6.4

## Recent versions

- 4.4.0 (latest) — 2026-10-14
- 4.3.1 — 2026-08-28
- 4.3.0 — 2026-08-28
- 4.2.3 — 2026-07-23
- 4.2.2 — 2026-06-11
- 4.2.1 — 2026-06-08
- 4.2.0 — 2026-06-03
- 4.1.5 — 2026-03-16
- 4.1.4 — 2026-03-13
- 4.1.3 — 2026-02-06
- 4.1.2 — 2025-12-16
- 4.1.1 — 2025-12-16
- 4.1.0 — 2025-10-20
- 4.0.7 — 2025-08-21
- 4.0.6 — 2025-04-22
- … 37 more at https://npm.io/package/bare-pipe/versions

## README

# bare-pipe

Native I/O pipes for JavaScript.

```
npm i bare-pipe
```

## Usage

```js
const Pipe = require('bare-pipe')

const [read, write] = Pipe.pipe()

const reader = new Pipe(read)
const writer = new Pipe(write)

reader.on('data', (data) => console.log(data.toString()))

writer.end('Hello world!\n')
```

## API

See the [`bare-pipe` reference](https://docs.pears.com/reference/bare/modules/bare-pipe).

## IPC handle passing

Pipes created with `ipc: true` can transfer libuv handles (named pipes, TCP sockets, UDP sockets) to a peer alongside the byte stream. The peer receives a `'handle'` event for each transferred handle, in arrival order, before the corresponding `'data'` event.

Handle passing needs a bidirectional socket on both ends, so the descriptors must come from a socket pair, such as `bare-tcp`'s `socketpair()`. The descriptors from `Pipe.pipe()` are a unidirectional pipe and cannot carry handles.

Sender:

```js
const left = new Pipe(fd, { ipc: true })
const socket = tcp.createConnection(port)

socket.on('connect', () => {
  left.write(Buffer.from('here'), socket)
})
```

Receiver:

```js
const right = new Pipe(fd, { ipc: true })

right.on('handle', (type) => {
  if (type === Pipe.constants.handle.TCP) {
    const socket = new tcp.Socket()
    right.accept(socket)
    socket.on('data', console.log)
  }
})
```

### `IPCAcceptable` protocol

Any object passed to `pipe.accept(target)` or `pipe.write(chunk, handle, ...)` must implement two well-known symbols:

```js
const ipcHandle = Symbol.for('bare.ipc.handle')
const ipcAccept = Symbol.for('bare.ipc.accept')

class MyTarget {
  get [ipcHandle]() {
    return this._handle // An ArrayBuffer backing a libuv `uv_*_t` struct
  }

  [ipcAccept]() {
    // Optional: Called synchronously after the handle has been transferred
  }
}
```

- `Symbol.for('bare.ipc.handle')` (required): A getter returning the underlying libuv handle (typically an `ArrayBuffer` whose first bytes are a `uv_stream_t` / `uv_udp_t`).
- `Symbol.for('bare.ipc.accept')` (optional): A method invoked synchronously after the handle has been transferred. Use it to initialize per-handle state (e.g. address lookup).

`Pipe`, `bare-tcp`'s `Socket`, `bare-dgram`'s `Socket`, and any compatible package implement this protocol natively, so a `bare-tcp` socket can be passed and received via `bare-pipe` IPC without any glue code.

TypeScript users can import the `IPCAcceptable` interface from `bare-pipe` to type the protocol.

## Local access control

A server can restrict its pipe to the current user, and either end can ask the operating system who is on the other end.

```js
const server = Pipe.createServer((pipe) => {
  const credentials = pipe.remoteCredentials()

  if (/* not valid credentials */) return pipe.destroy()

  pipe.end('hello\n')
})

server.listen({ path, ownerOnly: true })
```

What counts as valid credentials is up to the application, such as a `uid` matching the current user on Unix or a `sid` matching it on Windows.

`ownerOnly` applies the restriction before the server listens, and disconnects some clients as soon as they are accepted, without a `connection` event.

On Unix the socket file is made `0600`, without following a symbolic link that replaced it, which on Linux requires Linux 6.6 or later or a mounted `/proc`. A client whose user is neither that of the server nor root is disconnected, which the mode alone admits when the client may bypass it, such as a process with `CAP_DAC_OVERRIDE` on Linux.

On Windows the pipe gets a protected DACL that allows only the current user and `SYSTEM`, any client that opened the pipe before the DACL was applied is disconnected, and so is:

- A remote client.
- A client whose process runs as a user other than that of the server or `SYSTEM`. The DACL alone admits these when they connect while impersonating the current user.
- A client running at a lower integrity level than the server, such as an unelevated process seen from an elevated server or a sandboxed low integrity process. The DACL alone admits these, as they run as the same user.
- A client whose process the server may not query, such as one running as `SYSTEM` seen from an unelevated server, or one that has exited.

An elevated process of the current user is admitted by an unelevated server, as it runs as the same user at a higher integrity level.

Keep the socket in a directory only the current user can write to, so that its path cannot be replaced. An abstract socket has no file to restrict, so `ownerOnly` is rejected for one.

`pipe.remoteCredentials()` returns `{ pid, uid, gid, sid }`, with `null` for anything the platform cannot report. On Linux it reads `SO_PEERCRED`; on macOS and the BSDs `getpeereid()`, plus `LOCAL_PEERPID` on macOS. Read the credentials while the peer is connected: on macOS the process ID is no longer available once the peer has closed its end.

On Windows the process ID comes from the pipe. A remote peer reports no process ID or SID, since its process ID names a process on another machine.

On the server end, the SID is the user of the client process with that ID: the user the process runs as, not one it impersonated while connecting. It does not tell an elevated process from an unelevated one of the same user. The SID is `null`:

- For a client that has exited, once nothing holds its process open. Read the credentials while the client is alive.
- For a process created after the server accepted the connection, as it has reused the ID of a client that exited.
- For a client process the server may not query, such as one running as `SYSTEM` seen from an unelevated server.
- For a server end opened from a handle or received over IPC, as it may have connected at any time.

The check for a reused ID, which `ownerOnly` also relies on, has two gaps. A client that exits and has its ID reused while its connection waits to be accepted goes unnoticed, and so does a reuse after the system clock is set back, as process creation times are read from it.

On the client end, the process ID names the process that created the pipe, which may since have exited and had its ID reused. The SID is instead the owner of the pipe, which the kernel records when the server creates it, and which is the user of the server process, except for an elevated administrator, whose pipes are owned by the `Administrators` group (`S-1-5-32-544`). Only a process holding `SeRestorePrivilege`, such as an elevated administrator, can make another user the owner.

## License

Apache-2.0

---
_Source: https://npm.io/package/bare-pipe · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
