ReboFilesDocs
Start3
What is ReboFiles
Getting started
Demos to run
Put it in your app6
React component
Headless: your own UI
Without a framework
Styling and themes
Keyboard and accessibility
Languages
Files9
Uploads
Previews and the viewer
Search
Live changes and large folders
Comments
Starred and fields
Versions, trash and space
Download as .zip
Word documents
The server14
The route
Frameworks and sign-in
Who may do what
Storage: S3, GCS, Azure and disk
Checking uploads
Metadata: SQLite and Postgres
Database schema
Sharing and links
Activity and notifications
Hooks, logs and limits
Retention, holds and export
Desktop and other origins
Sync a folder
Going to production
Together1
AI assistants (MCP)
Reference5
Packages
Components and hooks
Client API
HTTP routes
Status and roadmap
↑↓ to move↵ to openEsc to close38 pages

The server Metadata: SQLite and Postgres

Metadata: SQLite and Postgres

A bucket has no folders, owners, permissions or trash. Those go in a metadata store beside it, which writes only what changed, one change at a time.
Pick

The stores

StoreFor
memoryMetadata()Tests and demos: gone when the process ends.
sqliteMetadata(path)An application on one server: one file, with Node’s own node:sqlite.
postgresMetadata(pool)More than one server, or a host without a disk: your driver, a lock of the database for each change.
Postgres

On Postgres

import pg from 'pg';
import { postgresMetadata } from '@rebofiles/server';

metadata: await postgresMetadata(new pg.Pool({ connectionString: process.env.DATABASE_URL })),

It takes anything with query(text, values) that answers { rows }: pg, Neon’s Pool, PGlite. Given a pool, each change holds a lock of the database while it reads and writes, so two servers never change the drive at once; without one, two folders made at the same moment could both be “(2)”. { table: 'acme_files' } for a table of another name.

Tables

What is kept

  • The entries, one row each, as JSON.
  • The settings of the drive and of each person, and the passwords of links as scrypt makes them.
  • The activity, a row a line, found by the entry and the folders it is about.
  • The text of each file: SQLite’s full-text search, or a tsvector in Postgres.
  • A revision that goes up with every change, so each process knows when to read the drive again.
Yours

A store of your own

MetadataStore is load() and save(changes); revision, lock, settings, record/activity and keepText/findText are optional, and without them the server keeps those in the process. Database schema has every table.

Scale

A piece at a time

SQLite, Postgres and the store in memory answer what each request needs, and no more: an entry and the folders above it, what is in a folder, what is under one, a search by name, the trash, what is shared with someone. The drive is never loaded whole, and a change writes only what it touched. What another process changed is read from the activity they keep.

A hundred people, two thousand files eachTook
The top of one’s drive13 ms
A folder of 2,000 files22 ms
A search by name35 ms
A file moved8 ms

The columns and tables it is asked by are added to a database made before them the first time it is opened, and the rows filled once. A store of your own does the same with scoped, and without it, it is held whole, as before.