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

@dynamia-tools/buckie

v1.0.0

Published

Standalone filesystem-native file server with a minimal S3-style API for secure server-to-server file operations

Downloads

125

Readme

Release npm

Buckie Node.js

Node.js 24 implementation of Buckie — a filesystem-native file server with a minimal S3-style REST API optimized for secure server-to-server file operations.


Features

  • Zero database — no database, no message broker, no cloud account required; just a process and a disk
  • Single config file — the entire server state (buckets, identities, grants) lives in one config.json, making it trivial to version-control, back up or inject via Docker
  • Streaming by design — files are piped directly from disk to the HTTP response with constant memory usage regardless of file size
  • Multi-tenant ready — prefix-based grants let multiple tenants share a single server while staying fully isolated
  • On-the-fly thumbnails — images are resized and cached automatically via Sharp; no extra service needed
  • SFTP storage backend — store files on a remote SFTP server with local-staging atomic commits
  • Programmatic API — full TypeScript SDK for embedding Buckie into any Node.js application
  • Docker-friendly — mount a single config.json and the server is fully configured at startup

Requirements

| Requirement | Minimum | |---|---| | Node.js | 24.0.0 | | npm | 9+ (or pnpm 8+) |


Installation

npm install -g @dynamia-tools/buckie

Or use without installing:

npx @dynamia-tools/buckie serve

Quick Start

# Create a local bucket
buckie create bucket documents /mnt/storage/documents

# Create an SFTP bucket
buckie create bucket remote-docs /uploads \
  --storage sftp \
  --sftp-host storage.example.com \
  --sftp-username deploy \
  --sftp-private-key "$(cat ~/.ssh/id_rsa)"

# Create an identity
buckie create identity erp-prod my-secret-password

# Grant permissions
buckie grant erp-prod documents --read --write --delete --prefix /tenant-a/

# Start the server
buckie serve --host 0.0.0.0 --port 8080

CLI Reference

Tab Completion

# Bash — add to ~/.bashrc
eval "$(buckie completion bash)"

# Zsh — add to ~/.zshrc  (compinit must already be loaded)
eval "$(buckie completion zsh)"

# Fish — save to completions directory
buckie completion fish > ~/.config/fish/completions/buckie.fish

Commands

# Server
buckie serve [--host <host>] [--port <port>] [--data-dir <dir>] [--log-level <level>]

# Buckets
buckie create bucket <name> <absolutePath> [--storage local|sftp] [--sftp-host <host>] \
       [--sftp-port <port>] [--sftp-username <user>] [--sftp-password <pass>] \
       [--sftp-private-key <pem>] [--sftp-passphrase <phrase>]
buckie list buckets
buckie remove bucket <name>

# Identities
buckie create identity <identity> <secret>
buckie list identities
buckie remove identity <identity>

# Permissions
buckie grant <identity> <bucket> --read --write --delete [--prefix <path>]
buckie revoke <identity> <bucket>

# Files
buckie list files <bucket> [path]
buckie upload <bucket> <key> <localFile>
buckie copy <srcBucket> <srcKey> <dstBucket> <dstKey>

# Provisioning (create identity + secret + grant in one step)
buckie provision <bucket> [--identity <name>] [--prefix <path>] [--read] [--write] [--delete]

REST API

All requests (except GET /health) require authentication via X-Buckie-Identity + X-Buckie-Secret headers, or HTTP Basic Auth.

Health check

GET /health

Download a file

GET /:bucket/:key
X-Buckie-Identity: erp-prod
X-Buckie-Secret: my-secret-password

Download a thumbnail (on-the-fly resize)

GET /:bucket/:key?w=300&h=300&fit=cover&format=webp

Supported query parameters: w (width), h (height), fit (cover | contain | fill), format (webp | jpeg | png).

List a directory

GET /:bucket/:path/

List bucket contents (paginated)

GET /:bucket?limit=100&cursor=...

Upload a file

PUT /:bucket/:key
Content-Type: application/octet-stream
[body: file stream]

Delete a file

DELETE /:bucket/:key

curl Examples

# Health check
curl http://localhost:8080/health

# Upload a file
curl -X PUT http://localhost:8080/documents/tenant-a/invoice.pdf \
  -H "X-Buckie-Identity: erp-prod" \
  -H "X-Buckie-Secret: my-secret-password" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @invoice.pdf

# Upload using HTTP Basic Auth
curl -X PUT http://localhost:8080/documents/tenant-a/report.xlsx \
  -u "erp-prod:my-secret-password" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @report.xlsx

# Download a file
curl http://localhost:8080/documents/tenant-a/invoice.pdf \
  -H "X-Buckie-Identity: erp-prod" \
  -H "X-Buckie-Secret: my-secret-password" \
  -o invoice.pdf

# Download a thumbnail
curl "http://localhost:8080/documents/tenant-a/photo.jpg?w=300&h=300&fit=cover&format=webp" \
  -H "X-Buckie-Identity: erp-prod" \
  -H "X-Buckie-Secret: my-secret-password" \
  -o thumbnail.webp

# List a directory
curl http://localhost:8080/documents/tenant-a/ \
  -H "X-Buckie-Identity: erp-prod" \
  -H "X-Buckie-Secret: my-secret-password"

# List bucket contents (paginated)
curl "http://localhost:8080/documents?limit=50" \
  -H "X-Buckie-Identity: erp-prod" \
  -H "X-Buckie-Secret: my-secret-password"

# Next page using the cursor returned from the previous response
curl "http://localhost:8080/documents?limit=50&cursor=<cursor-value>" \
  -H "X-Buckie-Identity: erp-prod" \
  -H "X-Buckie-Secret: my-secret-password"

# Delete a file
curl -X DELETE http://localhost:8080/documents/tenant-a/invoice.pdf \
  -H "X-Buckie-Identity: erp-prod" \
  -H "X-Buckie-Secret: my-secret-password"

Embedded Usage

Minimal example

// index.js  (type: "module")
import { startServer } from '@dynamia-tools/buckie'

startServer({
  host: process.env.BUCKIE_HOST ?? '0.0.0.0',
  port: parseInt(process.env.BUCKIE_PORT ?? '8080', 10),
}).catch((err) => {
  console.error('Fatal error during startup:', err)
  process.exit(1)
})

Full programmatic setup

import path from 'node:path'
import { createRuntime, createServer } from '@dynamia-tools/buckie'

const runtime = await createRuntime({
  dataDir:  path.join(process.cwd(), '.buckie'),
  host:     '0.0.0.0',
  port:     8080,
  logLevel: 'info',
})

const { config, bucketService, storageService, thumbnailService,
        identityService, operationalLogger } = runtime

// Create a local bucket (idempotent pattern)
if (!await bucketService.find('documents')) {
  await bucketService.create('documents', '/mnt/storage/documents')
}

// Create an SFTP bucket
if (!await bucketService.find('remote')) {
  await bucketService.create('remote', '/uploads', 'sftp', {
    host: 'storage.example.com',
    port: 22,
    username: 'deploy',
    privateKey: process.env.SFTP_PRIVATE_KEY,
  })
}

// Create an identity (idempotent pattern)
if (!await identityService.find('app-user')) {
  await identityService.create('app-user', process.env.APP_SECRET ?? 'changeme')
}

// Grant permissions
await identityService.grant('app-user', {
  bucket:      'documents',
  prefixes:    ['/'],
  permissions: ['read', 'write', 'delete'],
})

await bucketService.validateStartup()

const server = await createServer({
  config, bucketService, storageService,
  thumbnailService, identityService, operationalLogger,
})

process.on('SIGTERM', async () => { await server.close(); process.exit(0) })
process.on('SIGINT',  async () => { await server.close(); process.exit(0) })

await server.listen({ host: config.host, port: config.port })

Available Exports

| Export | Description | |---|---| | startServer(overrides?) | One-call bootstrap: creates runtime + server + starts listening | | createRuntime(overrides?) | Initialises services and data dirs, returns the runtime object | | createServer(runtime) | Builds and returns the Fastify instance (does not call .listen) | | BucketService | Manage bucket definitions | | IdentityService | Manage identities and grants | | StorageService | Low-level file upload / download / list | | LocalStorageProvider | Local filesystem storage provider | | SftpStorageProvider | SFTP storage provider | | StorageProviderRegistry | Register custom storage backends | | ThumbnailService | On-the-fly image thumbnails via Sharp | | OperationalLogger | Structured JSONL access + error logs |


Storage Backends

| Backend | Target key | Description | |---|---|---| | Local FS | local (default) | Files stored directly on the local filesystem. | | SFTP | sftp | Files stored on a remote SFTP server. Uploads use a local staging area before being committed atomically to the remote path. |


Environment Variables

| Variable | Default | Description | |---|---|---| | BUCKIE_DATA_DIR | <cwd>/.buckie | Where Buckie stores config.json, logs and cache | | BUCKIE_HOST | 0.0.0.0 | Bind host | | BUCKIE_PORT | 8080 | Bind port | | BUCKIE_LOG_LEVEL | info | Pino log level (trace debug info warn error) |


Data Directory Structure

.buckie/
 ├── config.json    # Single source of truth: buckets, identities and grants
 ├── logs/
 │   ├── access.log.jsonl
 │   └── error.log.jsonl
 └── cache/         # Thumbnail cache + SFTP staging area

config.json structure

{
  "buckets": [
    { "name": "documents", "path": "/mnt/storage/documents", "storage": "local" },
    { "name": "remote",    "path": "/uploads",               "storage": "sftp",
      "sftp": { "host": "storage.example.com", "port": 22, "username": "deploy" } }
  ],
  "identities": [
    {
      "identity": "erp-prod",
      "hashedSecret": "<bcrypt hash>",
      "grants": [
        { "bucket": "documents", "prefixes": ["/tenant-a/"], "permissions": ["read","write","delete"] }
      ]
    }
  ]
}

Docker tip: mount or inject config.json at startup and Buckie will be fully configured with no CLI bootstrap needed.

volumes:
  - ./buckie-config.json:/app/.buckie/config.json:ro

Inside each bucket (local storage)

/path/to/bucket/
 ├── data/
 │    ├── my-photo.jpg          # Original file
 │    ├── 200x200/
 │    │    └── my-photo.jpg     # Thumbnail cached at 200×200
 │    └── 800x600/
 │         └── my-photo.jpg     # Thumbnail cached at 800×600
 └── .buckie/
      └── staging/              # Upload staging area (atomic commits)

Technology Stack

  • Node.js 24 with TypeScript (strict mode)
  • Fastify for high-throughput HTTP
  • Pino for structured JSON logging
  • Sharp for efficient thumbnail generation
  • bcrypt for password hashing (cross-compatible with the PHP implementation)
  • ssh2-sftp-client for SFTP backend
  • file-type for MIME detection via magic bytes
  • Zod for runtime schema validation

Development

# Install dependencies
npm install

# Build
npm run build

# Run tests
npm test

# Type-check only
npm run typecheck

Security

  • Private by default — every request requires authentication; there is no anonymous or public bucket support
  • bcrypt password hashing — secrets are never stored in plain text; hashes are cross-compatible with the PHP implementation
  • Prefix-based authorization — access can be restricted by bucket and path prefix
  • Path traversal protection — canonical path validation is enforced on every request
  • Staging + atomic commits — uploads are written to a staging area first to prevent partial reads

Related Implementations

| Implementation | Repository | Best for | |---|---|---| | Buckie PHP | github.com/dynamiatools/buckie-php | Shared hosting and standard PHP environments (Local FS, Apache, Nginx, FrankenPHP) |


License

MIT © Dynamia Soluciones IT SAS