npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

bento-s3

v1.0.0

Published

A lightweight S3-compatible API server for local development, automated testing, and CI environments.

Downloads

86

Readme

BentoS3

BentoS3 is a lightweight, S3-compatible API server for local development, automated testing, and CI environments.

It is designed as both:

  • A standalone CLI-bootable S3-compatible server.
  • An embeddable Node.js library for Vitest, Jest, and framework-based test suites.

BentoS3 aims to provide the most commonly used S3 behavior without the operational weight of MinIO, LocalStack, or a full object-storage server.

BentoS3 is intended for local development, automated tests, and CI. It is not production object storage and does not attempt full S3 feature parity.

Installation

Install BentoS3 from npm:

npm install bento-s3

Then import it in your project:

import { BentoS3, MemoryAuthStore } from "bento-s3";

Goals

  • Support a practical subset of the S3 HTTP API.
  • Work with the official AWS SDK for JavaScript.
  • Persist buckets and objects to the local filesystem.
  • Provide a framework-neutral core that can be embedded in any Node.js HTTP stack.
  • Provide adapters for common frameworks such as Express, Koa, Fastify, and Node HTTP.
  • Include a lightweight server-rendered dashboard.
  • Keep dependencies lean for fast installs in CI.

Non-Goals

  • Production-grade object storage.
  • Full S3 feature parity.
  • Distributed storage.
  • Replication, lifecycle policies, object lock, ACLs, or bucket policies.

Usage

Managed Test Server

import { BentoS3, MemoryAuthStore } from "bento-s3";

const authStore = new MemoryAuthStore();
await authStore.createCredential({
  accessKeyId: "test",
  secretAccessKey: "test-secret",
});

const s3 = new BentoS3({
  port: 0,
  authStore,
});

await s3.start();

console.log(s3.endpoint);

await s3.stop();

AWS SDK Client

import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

const client = new S3Client({
  region: "us-east-1",
  endpoint: s3.endpoint,
  forcePathStyle: true,
  credentials: {
    accessKeyId: "test",
    secretAccessKey: "test-secret",
  },
});

await client.send(
  new PutObjectCommand({
    Bucket: "photos",
    Key: "cat.jpg",
    Body: Buffer.from("image-bytes"),
  }),
);

CLI

Start a local S3-compatible server with the dashboard enabled:

bentos3 serve
bentos3 serve --host 127.0.0.1 --port 9000 --root-dir ./.bentos3

Create a dashboard user:

bentos3 user create admin
bentos3 user create admin --password mypassword

List dashboard users and buckets:

bentos3 user list
bentos3 bucket list

Default server behavior:

| Option | Default | | ------------ | ------------ | | --host | 127.0.0.1 | | --port | 9000 | | --root-dir | ./.bentos3 | | Dashboard | Enabled | | Auth store | JSON-backed |

On first start, bentos3 serve bootstraps a default access key and prints the credentials.

Framework-Embedded Server

import express from "express";
import { MemoryAuthStore } from "bento-s3";
import { BentoS3Core } from "bento-s3/core";
import { expressAdapter } from "bento-s3/adapters/express";

const app = express();
const authStore = new MemoryAuthStore();
await authStore.createCredential({
  accessKeyId: "test",
  secretAccessKey: "test-secret",
});

const bento = new BentoS3Core({ authStore });

app.use("/s3", expressAdapter(bento));

The AWS SDK endpoint for this mounted route should include the mount path:

new S3Client({
  endpoint: "http://127.0.0.1:3000/s3",
  forcePathStyle: true,
  region: "us-east-1",
  credentials: {
    accessKeyId: "test",
    secretAccessKey: "test-secret",
  },
});

Framework Adapters

Express:

import { expressAdapter } from "bento-s3/adapters/express";

app.use("/s3", expressAdapter(bento));

Koa:

import { koaAdapter } from "bento-s3/adapters/koa";

app.use(koaAdapter(bento, { basePath: "/s3" }));

Fastify:

import { fastifyBentoS3 } from "bento-s3/adapters/fastify";

await app.register(fastifyBentoS3, {
  prefix: "/s3",
  bento,
});

Fetch:

import { BentoS3Core } from "bento-s3/core";
import { handleFetchRequest } from "bento-s3/adapters/fetch";

const bento = new BentoS3Core({
  auth: { enabled: false },
});

const response = await handleFetchRequest(bento, request, { basePath: "/s3" });

The Fetch adapter passes requests through the configured BentoS3Core auth behavior. Use signed requests with a configured auth store, or disable auth for unsigned local test requests.

Body Parser Ordering

BentoS3 adapters must receive the raw request stream. Mount BentoS3 before body parsers for the S3 route:

app.use("/s3", expressAdapter(bento));
app.use(express.json());

Avoid this ordering for S3 requests:

app.use(express.json());
app.use("/s3", expressAdapter(bento));

The same rule applies to Koa middleware that reads ctx.req. Fastify registration installs a raw content-type parser for the plugin scope so S3 object uploads are not pre-parsed before BentoS3 receives them.

Core Design

BentoS3 is protocol-engine first. The S3 implementation is not coupled to Express, Koa, Fastify, or any other framework.

The core accepts an internal request object and returns an internal response object:

export interface BentoRequest {
  method: string;
  url: string;
  path: string;
  canonicalPath?: string;
  query: URLSearchParams;
  headers: Record<string, string | string[] | undefined>;
  body?: AsyncIterable<Uint8Array> | NodeJS.ReadableStream;
  remoteAddress?: string;
}

export interface BentoResponse {
  statusCode: number;
  headers: Record<string, string | number | string[]>;
  body?: string | Uint8Array | AsyncIterable<Uint8Array> | NodeJS.ReadableStream;
}

Adapters translate framework-specific request and response types into this contract.

S3 Compatibility

The compatibility target is the official AWS SDK for JavaScript v3 using path-style addressing:

new S3Client({
  endpoint: "http://127.0.0.1:9000",
  forcePathStyle: true,
  region: "us-east-1",
  credentials,
});

Supported S3 operations:

  • ListBuckets
  • CreateBucket
  • DeleteBucket
  • HeadBucket
  • ListObjectsV2
  • PutObject
  • GetObject
  • HeadObject
  • DeleteObject
  • DeleteObjects
  • CopyObject

Current compatibility limits:

  • Requests must use path-style addressing.
  • SigV4 header authentication is supported; presigned URL query authentication is not supported.
  • ListObjectsV2 supports prefix filtering, but not delimiter grouping, continuation tokens, StartAfter, or custom MaxKeys pagination.
  • Multipart upload, range requests, ACLs, bucket policies, object tagging, lifecycle policies, replication, and object lock are not supported.
  • Bucket names are restricted to filesystem-safe path segments for local persistence.

Storage

BentoS3 uses the local filesystem as its primary persistence layer. Storage drivers write a .bentos3/ data directory inside the configured rootDir.

Example layout:

.bentos3/
  buckets/
    photos/
      .bentos3-bucket.json
      cats/leo.jpg
      cats/leo.jpg.meta.json
  auth/
    credentials.json
    dashboard-users.json
    sessions.json
  tmp/

Buckets map to directories. Objects map to files. Object metadata is stored in JSON sidecar files.

Auth

BentoS3 supports two credential store implementations:

  • MemoryAuthStore - in-memory, ideal for tests.
  • JsonAuthStore - JSON-backed, persists credentials to disk for local development.

SigV4 authentication is enabled by default. Configure the auth store through BentoS3 or BentoS3Core options:

import { BentoS3, JsonAuthStore } from "bento-s3";

const rootDir = "./.bentos3";

const s3 = new BentoS3({
  port: 9000,
  rootDir,
  authStore: new JsonAuthStore({ rootDir }),
});

Auth can be disabled for testing:

import { BentoS3Core } from "bento-s3/core";

const bento = new BentoS3Core({
  auth: { enabled: false },
});

Request body buffering is capped by default to keep local runs from accidentally exhausting memory. Configure the limit when larger test fixtures are needed:

const s3 = new BentoS3({
  maxRequestBodyBytes: 250 * 1024 * 1024,
});

Dashboard

The dashboard is a server-rendered UI for managing buckets, objects, and access keys. It is enabled by default when running bentos3 serve or creating a BentoS3 instance without dashboard.enabled: false.

Access the dashboard at http://127.0.0.1:9000/ui.

Dashboard Features

  • Browse and manage buckets and objects.
  • Upload and download objects from the browser.
  • Create and revoke S3 access keys.
  • Session-based authentication with HttpOnly cookies.

Dashboard Users

Create a dashboard user via the CLI:

bentos3 user create admin --password mypassword

Dashboard passwords are hashed with Node crypto.scrypt. Sessions store token hashes, not raw tokens.

Technology

  • EJS templates.
  • Inline CSS styled with a compact dashboard design system.
  • Turbo-compatible static script placeholder.
  • JSON files for user and session storage.

Package Exports

| Export path | Contents | | ----------------------------- | ----------------------------------------------------------------------- | | bento-s3 | BentoS3, BentoS3Core, adapters, auth stores, storage drivers, types | | bento-s3/core | BentoS3Core, BentoS3CoreOptions | | bento-s3/adapters/node-http | Node HTTP adapter utilities | | bento-s3/adapters/express | expressAdapter | | bento-s3/adapters/koa | koaAdapter | | bento-s3/adapters/fastify | fastifyBentoS3 | | bento-s3/adapters/fetch | handleFetchRequest |