Product — FluxFiles

Features

Everything a modern file manager needs

Built for developers who need flexibility.

Multi-storage

Local, AWS S3, Cloudflare R2, SFTP via Flysystem v3 — swap with config, no code change.

Embed anywhere

iframe + postMessage SDK. Standalone UI is Alpine.js (zero build). Works with any framework.

JWT auth + scoping

Per-user path prefix, permission claims, disk whitelist, owner-only mode — or start from a viewer/editor/admin role preset.

ACL role presets

Mint tokens from viewer / editor / admin / superadmin presets — one parameter expands to the right permission, disk, and scope claims instead of hand-assembling them.

AI auto-tag

Claude / OpenAI vision — auto alt text, title, tags.

Image crop

Inline crop tool with aspect ratio presets.

Image optimization

Auto WebP variants — thumb, medium, large.

Presigned uploads

Browser uploads directly to S3/R2 — zero bandwidth on your server.

Chunk upload

S3 multipart for files > 10MB.

Full-text search

Indexed search across names, titles, tags — file-based by default, or your own SQL database as an opt-in backend.

DB-backed metadata

Opt-in FLUXFILES_STORAGE_BACKEND=db mode — metadata, search/folder index, and audit log live in your own MySQL/PostgreSQL/SQLite instead of JSON sidecars. File bytes stay storage-resident either way.

Bulk operations

Multi-select move, copy, delete, download.

Cross-disk transfer

Copy/move between Local ↔ S3 ↔ R2.

Trash / soft delete

Recoverable deletes with auto-purge.

SEO metadata

Title, alt, caption, tags — S3 object metadata (cloud) or sidecar JSON (local). Full-text search.

Dark mode

Light / dark / auto in UI and SDK.

16 languages

Default English. Set locale via SDK, URL (?locale=), or FLUXFILES_LOCALE. RTL for Arabic.

Audit log

All write actions logged for compliance.

Rate limiting

Token bucket per user for API protection.

Storage quota

Per-user storage limits via JWT claims.

BYOB buckets

Users bring their own S3/R2 — credentials encrypted in JWT (AES-256-GCM).

Duplicate detection

SHA-256 match skips redundant uploads unless you force overwrite.

SDK token refresh

onTokenRefresh on 401 — coalesced refresh, retry, and updateToken() for proactive rotation.

Bucket Doctor

Diagnose any S3/R2 disk — credentials, read/write/delete, presign, CORS, multipart — with ready-to-paste IAM & CORS fixes. Onboard BYOB buckets in minutes.

SFTP disk

A 3rd driver after S3/R2 — turn any VPS or shared host into a managed file manager.

Code / config editor

Edit text and config files (wp-config.php, .env, nginx.conf) in-browser, cPanel-style.

Zip & Extract

Download a selection as a zip, or extract an archive in place — zip-slip & bomb guarded.

Watermark

Apply a text or logo watermark on the fly for preview protection — the source is never touched.

On-demand WebP + AVIF

Serve any image size as cached AVIF/WebP via one URL (Accept-negotiated), with ready responsive srcset.

SFTP permissions

Change Unix file permissions (chmod) on SFTP disks with a cPanel-style dialog.

SSH terminal

A stateless command-runner terminal on SFTP disks — cPanel-style admin tooling, built in and free (or embed your own ttyd for a full PTY).

One-click Git deploy

Pull and reset a Git repo on your SFTP server straight from the file manager — fixed-shape, no arbitrary shell, hooks off by default.

Usage dashboard

See storage at a glance — quota plus a per-type and per-folder breakdown.

Media preview

Inline image, video, audio & PDF preview; private local media streams securely (Range-capable).

Quick install

Up and running in 2 minutes

composer require fluxfiles/fluxfiles
cp .env.example .env

# .env
FLUXFILES_SECRET=your-random-32-char-secret
FLUXFILES_ALLOWED_ORIGINS=https://yourapp.com

require_once 'vendor/autoload.php';

$token = fluxfiles_token(
    userId: 'user-123',
    perms: ['read', 'write', 'delete'],
    disks: ['local', 's3', 'r2'],
    prefix: 'user-123/',
    maxUploadMb: 10,
    allowedExt: null,
    ttl: 3600
);
composer require fluxfiles/laravel
php artisan vendor:publish --tag=fluxfiles-config

# .env — point the adapter at your FluxFiles server
FLUXFILES_ENDPOINT=https://fm.yourdomain.com
FLUXFILES_SECRET=your-secret-min-32-chars

# Optional: match CORS on the FluxFiles host
# FLUXFILES_ALLOWED_ORIGINS=https://yourapp.com

# Config: config/fluxfiles.php
npm install @fluxfiles/node

// Express / Next.js route — mint a token from your JS backend (no PHP)
import { createToken } from '@fluxfiles/node';

app.get('/fluxfiles-token', (req, res) => {
    const token = createToken({
        secret: process.env.FLUXFILES_SECRET, // >= 32 bytes
        userId: req.user.id,
        perms: ['read', 'write', 'delete'],
        prefix: `users/${req.user.id}`,
        ttl: 3600,
    });
    res.json({ token });
});
pip install fluxfiles-token

# FastAPI / Django / Flask route — mint a token from your Python backend (no PHP)
from fluxfiles_token import create_token

@app.get("/fluxfiles-token")
def fluxfiles_token(user=Depends(current_user)):
    token = create_token(
        secret=os.environ["FLUXFILES_SECRET"],  # >= 32 bytes
        user_id=user.id,
        perms=["read", "write", "delete"],
        prefix=f"users/{user.id}",
        ttl=3600,
    )
    return {"token": token}
<!-- Demo: unpkg. In production, serve fluxfiles.js from your FluxFiles host -->
<script src="https://unpkg.com/fluxfiles@latest/fluxfiles.js"></script>

<script>
FluxFiles.open({
    endpoint: 'https://your-api.com',
    token: 'eyJhbGci...',
    disk: 'local',
    mode: 'picker',
    container: '#file-picker',
    onSelect: function(file) {
        console.log('Selected:', file.url);
    },
    async onTokenRefresh() {
        const r = await fetch('/api/auth/refresh-fluxfiles-token');
        const { token } = await r.json();
        return token;
    }
});
</script>
npm install @fluxfiles/react

import { FluxFilesModal } from '@fluxfiles/react';

<FluxFilesModal
    open={open}
    endpoint="https://your-api.com"
    token={token}
    onSelect={(file) => console.log(file)}
    onClose={() => setOpen(false)}
/>
npm install @fluxfiles/vue

<script setup>
import { FluxFilesModal } from '@fluxfiles/vue';
</script>

<FluxFilesModal
    v-model:open="open"
    endpoint="https://your-api.com"
    :token="token"
    @select="onSelect"
    @close="open = false"
/>
git clone https://github.com/thai-pc/fluxfiles.git
docker build -f fluxfiles/docker/Dockerfile.prod -t fluxfiles fluxfiles

docker run -p 8080:80 \\
  -e FLUXFILES_SECRET=your-random-32-char-secret \\
  fluxfiles

# UI: http://localhost:8080/public/index.html
git clone https://github.com/thai-pc/fluxfiles.git
cd fluxfiles
composer install -d packages/core
cp .env.example .env

cd packages/core
php -S localhost:8080 router.php

# UI:  http://localhost:8080/public/index.html
# API: http://localhost:8080/api/fm/list?disk=local&path=

Works with

Laravel WordPress React / Next.js Vue / Nuxt CKEditor 4 TinyMCE Summernote Any framework

Integrations

Drop-in adapters for your stack

Official adapters embed FluxFiles natively in the frameworks and editors you already use.

Laravel
PHP
WordPress
Plugin
React
Component
Vue / Nuxt
Component
CKEditor 4
Plugin
TinyMCE
Plugin
Summernote
Plugin

Embed

Embed it in a few lines

Load fluxfiles.js, pass a server-minted JWT, and drop a full file manager — or a file-picker modal — into any page.

<script src="https://fm.example.com/fluxfiles.js"></script>

FluxFiles.open({
  endpoint: 'https://fm.example.com',
  token: 'eyJhbGci...',   // JWT from your backend
  disk: 'local',
  mode: 'picker',         // select & close
  theme: 'auto',
  onSelect(file) {
    // { url, key, name, size, disk, meta, variants }
    document.querySelector("#img").src = file.url;
  },
});
<div id="fm" style="height:600px"></div>

FluxFiles.open({
  endpoint: 'https://fm.example.com',
  token: 'eyJhbGci...',
  mode: 'browser',          // full manager, stays open
  container: '#fm',         // omit for a modal overlay
  disks: ['local', 's3', 'r2'],
});

FluxFiles.on('FM_EVENT', (e) => console.log(e.event));
FluxFiles.navigate('/photos/2024');
import { FluxFilesModal } from '@fluxfiles/react';

function Picker({ open, setOpen, token }) {
  return (
    <FluxFilesModal
      open={open}
      endpoint="https://fm.example.com"
      token={token}
      onSelect={(file) => console.log(file.url)}
      onClose={() => setOpen(false)}
    />
  );
}

Tokens are minted server-side — never ship your secret to the browser.

Why FluxFiles

Built for what others forgot

Swipe to compare
Feature FluxFiles elFinder Laravel-FM RichFilemanager Responsive FM
S3 + R2 + Local + SFTP
Embed any framework
JWT auth + scoping
SEO metadata
AI auto-tag
Image crop
Chunk upload
Full-text search
Dark mode
16 languages
PHP ≥ 8.1
Actively maintained
Modern UI
Capability FluxFiles Filestack Uploadcare Cloudinary Supabase Storage
Self-hosted (runs in your infra)
BYOB — each customer’s own bucket
Embeddable file-manager UI
Open source (MIT)
No per-storage / per-upload SaaS fees
Multi-storage (S3 / R2 / local)
Per-tenant token scoping built in

Comparison reflects typical default offerings; each product excels in its own space. FluxFiles’ niche is the embeddable, self-hosted, bring-your-own-bucket combination.

Comparison as of September 2026, unaffiliated — see the GitHub repo to flag a correction.

FAQ

Frequently asked questions

How does authentication work?
Stateless JWT (HS256): you mint a token server-side with granular claims (permissions, disks, path scope, limits), so each user sees only what you allow.
Do my users need a FluxFiles account?
No. FluxFiles trusts the JWT from your own app, so it plugs into your existing auth — there's no separate user system.
What do I need to run it?
PHP 8.1+ with common extensions and Composer. Point it at a disk, set a secret and allowed origins, then serve — it runs on a small VPS.
Can different users have different permissions?
Yes. Mint a token with fine-grained claims (read/write/delete, disk, path prefix, upload limits, allowed extensions, owner-only), or use one of four built-in role presets — viewer, editor, admin, superadmin — to scope a token in one line instead of listing every claim by hand.
Do I need to run a database?
No. By default all metadata (file info, search index, audit log) lives next to your files as JSON sidecars — nothing extra to provision or back up. If you'd rather keep that bookkeeping in your own MySQL, PostgreSQL, or SQLite, an opt-in FLUXFILES_STORAGE_BACKEND=db mode moves it there; file bytes always stay in your storage either way.
Are there any usage limits?
The core itself has no artificial limits — no monthly-active-user caps, no forced upgrade prompts. You set your own limits via JWT claims: per-user storage quotas, upload size/extension restrictions, and a built-in rate limiter (60 reads / 10 writes per minute by default, configurable).