Skip to main content
ClaudeWave

User files for SaaS. Ownership, access, sharing, expiry, revocation and an audit trail for your application's files — on your own Postgres and your own bucket. Apache-2.0.

ToolsRegistry oficial0 estrellas0 forks● TypeScriptApache-2.0Actualizado today
ClaudeWave Trust Score
95/100
✓ Verified
Passed
  • ✓Open-source license (Apache-2.0)
  • ✓Actively maintained (<30d)
  • ✓Clear description
  • ✓Topics declared
  • ✓Documented (README)
Last scanned: 10/13/2026
Get started
Method: Clone
Terminal
git clone https://github.com/filelayer/filelayer
1. Clone the repository.
2. Follow the README for installation and usage instructions.
Casos de uso

Resumen de Tools

# Filelayer

[![CI](https://github.com/filelayer/filelayer/actions/workflows/ci.yml/badge.svg)](https://github.com/filelayer/filelayer/actions/workflows/ci.yml)
[![npm](https://img.shields.io/npm/v/@filelayer/core.svg)](https://www.npmjs.com/package/@filelayer/core)
[![node](https://img.shields.io/node/v/@filelayer/core.svg)](https://nodejs.org)
[![license](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/filelayer/filelayer/blob/main/LICENSE)

<!-- Every URL on this page is rooted at github.com/filelayer/filelayer and
     npmjs.com/package/@filelayer/core, so the badges resolve on their own with
     no edit here. -->

> **Alpha — developer preview, not production software.** Nobody is running it
> in production, including us. [**Should you depend on this?**](https://github.com/filelayer/filelayer/blob/main/TRUST.md)
> has the real numbers, including the ones that are zero, and exactly what would
> change them. [Limitations](https://github.com/filelayer/filelayer/blob/main/LIMITATIONS.md) is the complete list of what it
> does not do.
>
> **We are looking for three to five design partners.** If you are building
> something with private user files, we will help you integrate this and stay on
> hand while you do, in exchange for telling us where it breaks. That is the only
> thing on our [roadmap to 1.0](https://github.com/filelayer/filelayer/blob/main/ROADMAP.md) we cannot build ourselves.
> [Open an issue](https://github.com/filelayer/filelayer/issues) or say hello.

**The file layer for SaaS applications.** Public files and private files, with
one authorization model behind both.

You tell Filelayer who the caller is. Filelayer decides what they may do with a
file, serves the bytes with the right headers, and writes the audit event. For
file access that goes through Filelayer, that decision is made in one place,
`packages/core/src/authz.ts`, so you write no ownership checks in route
handlers, no presigned-URL expiry logic, and no per-route access rules of your
own.

**Filelayer is authorization middleware, not row-level security.** It runs in
your application process, in front of Postgres and your bucket. Code that
queries these tables directly does not go through it. The schema does enforce a
set of invariants against *every* writer: cross-tenant grants, cross-project
identities, and delegation that amplifies authority or subject breadth are
refused by constraints and triggers, so a migration or a `psql` session cannot
write them. But there is no RLS policy in `schema.sql`, and a direct `SELECT` is
not filtered by anything. If you need enforcement that survives arbitrary
database clients, use database-level enforcement; Filelayer is the layer above
it, and the two compose.

<!-- doccheck-setup
import { Filelayer } from '@filelayer/core';
const fl = await Filelayer.quickstart({ baseUrl: 'http://localhost:3000' });
const bytes = new TextEncoder().encode('hello');
-->

```ts
// Public avatar
const { url } = await fl.files.put(bytes, { public: true });

// Private, user-owned
const { id } = await fl.files.put(bytes, { owner: 'user_123' });
const file  = await fl.files.get(id, { as: 'user_123' });

// Multi-tenant, role-controlled
await fl.files.put(bytes, { org: 'acme', owner: 'user_123' });

// Shared with an expiry, a password and a download cap, and revocable
const share = await fl.shares.create(id, {
  as: 'user_123', expiresIn: 3600, maxDownloads: 3, password: 'hunter2',
});
await fl.shares.revoke(share.grantId, { as: 'user_123' });   // this link, now

// Shared with a named user, and taken back. `unshare` is the one you want for
// a person: `create()` is not idempotent, so revoking a single grant id you
// happen to be holding can leave an earlier one live.
await fl.shares.create(id, { as: 'user_123', withUser: 'user_456' });
await fl.shares.unshare(id, { as: 'user_123', user: 'user_456' });   // all of it
```

**Start at whichever line matches your problem.** Complexity is incremental:
each tier adds one concept, and no tier makes you pay for a concept you are not
using. → [`docs/QUICKSTART.md`](https://github.com/filelayer/filelayer/blob/main/docs/QUICKSTART.md)

---

## The three layers

```
your application          fl.files.put / get / list, fl.shares, fl.orgs
                          you pass YOUR user id; nothing here knows a uuid
   ───────────────────────────────────────────────────────────────────────
Filelayer                 ONE authorization function answers every request
                          grants, roles, expiry, download caps, revocation
                          the audit event, written in the decision's own
                          transaction so the log cannot disagree with it
   ───────────────────────────────────────────────────────────────────────
your Postgres             9 tables. Filelayer owns the schema, you own the
your bucket               database. Bytes go to S3, R2, B2 or a directory
                          behind one adapter; storage is replaceable
```

Filelayer sits between the two things you already run. It brings no database and
no storage of its own, and **zero runtime dependencies**.

**It is authorization middleware, not row-level security.** It decides for calls
made through it, in your process. The schema additionally refuses cross-tenant
grants, cross-project identities and delegation that amplifies authority from
*every* writer, including a `psql` session — those are constraints and triggers.
But there is no RLS policy in `schema.sql`, and a direct `SELECT` against these
tables is filtered by nothing. Filelayer composes with database-level
enforcement; it does not replace it.

---

## What it is for, and what it is not for

**Worth the overhead when:** files are private, belong to specific people or
tenants, and their permissions change over time. Documents, contracts,
attachments, exports, anything with a share link you might later want back.

**Not worth the overhead when:**

| You need | Use instead | Why |
|---|---|---|
| Public images at CDN volume | a CDN-backed bucket | Default delivery proxies every byte. Redirect delivery (below) removes the proxy but is opt-in and narrow. |
| Video or audio at scale | a CDN / media service | Seeking works — the shipped routes answer `Range` with `206`. But default delivery proxies every byte through your server, and there is no CDN on that path. |
| Browser → storage upload with your server out of the data path, in development | a real bucket | `FsStorage` mints the credential so the client code is identical, but it receives the bytes itself and says so: `via: 'server'`. Only S3 and R2 keep your process out of the path. |
| Resumable or multipart direct upload | presigned S3 directly / tus | One signed PUT, one object. There is no resume. |
| Thumbnails, transforms, format negotiation | Cloudinary / imgix | We have none. |

### Against the two things you would otherwise do

| | Hand-rolled presigned URLs | Supabase Storage | Filelayer |
|---|---|---|---|
| Revoke a link somebody already holds | **No.** A signature is valid until its clock runs out; your options are a short TTL or proxying the bytes yourself | **No.** Signed URLs are signed with an internal key and cannot be revoked | **Yes**, on the next request, transitively through the delegation chain |
| "Everyone in this org may read" | your own join, recomputed on every join and leave | expressible as an RLS policy | one grant row; membership resolves at request time |
| Who accessed this, including refusals | whatever you wrote | not a product feature; Postgres logging is not an access log | hash-chained per tenant, written in the decision's transaction |
| Download cap | your own counter, and the race in it | none | atomic, reserved before the bytes move |
| CDN on the private path | yes, and that is why you cannot revoke | yes, same trade | **no** — the two facts are the same fact |
| Public images at volume | **yes** | **yes** | no. Use a bucket behind a CDN |
| Lines for a public avatar | ~10 | **10** | 16 |

The last two rows are losses and they are not close. We publish the full
comparison, including every case we lose, in
[`ARCHITECTURE-PROGRESSIVE.md`](https://github.com/filelayer/filelayer/blob/main/ARCHITECTURE-PROGRESSIVE.md)
§5. Short version: **for a public avatar, Supabase is 10 lines and Filelayer is
16.** If avatars are your whole problem, use Supabase.

---

## The five security properties

These are the product. Each is enforced in the schema or the authorization
engine, not by convention, and each has tests named after it.

| | Property | What it means in practice |
|---|---|---|
| **P1** | Deny by default | There is no `public` boolean anywhere in the schema. Public delivery is an explicit, revocable, auditable grant row — there is no flag to leave on by accident. **This is a property of the schema, not of your object store: Filelayer cannot make your bucket private, and a public bucket bypasses everything on this page.** That is the one configuration step it cannot do for you — [QUICKSTART §7](https://github.com/filelayer/filelayer/blob/main/docs/QUICKSTART.md). |
| **P2** | No ambient authority | Knowing an object key, a URL or a file id grants nothing. Storage location is never an input to a decision. |
| **P3** | Cross-tenant grants are structurally impossible to write | A grant's `org_id` must equal its file's `org_id`, enforced by the composite foreign key `FOREIGN KEY (file_id, org_id) REFERENCES file (id, org_id)`. No writer — including a migration or a `psql` session — can create a grant pointing at another tenant's file. This is a write-side integrity constraint, not a read filter: it makes the *row* unrepresentable. Reads are scoped by the engine, not by the database. |
| **P4** | A URL never outlives its permission | Every signed URL embeds a grant id and is re-validated on **every** request, transitively through the whole delegation chain. Revocation beats a live URL. |
| **P5** | Every 
audit-logauthorizationfile-sharingfile-uploadmulti-tenantnodejspostgress3saastypescript

Lo que la gente pregunta sobre filelayer

¿Qué es filelayer/filelayer?

+

filelayer/filelayer es tools para el ecosistema de Claude AI. User files for SaaS. Ownership, access, sharing, expiry, revocation and an audit trail for your application's files — on your own Postgres and your own bucket. Apache-2.0. Tiene 0 estrellas en GitHub y su última actualización registrada es del 2026-10-12.

¿Cómo se instala filelayer?

+

Puedes instalar filelayer clonando el repositorio (https://github.com/filelayer/filelayer) o siguiendo las instrucciones del README en GitHub. ClaudeWave también te ofrece bloques de instalación rápida en esta misma página.

¿Es seguro usar filelayer/filelayer?

+

Nuestro agente de seguridad ha analizado filelayer/filelayer y le ha asignado un Trust Score de 95/100 (tier: Verified). Revisa el desglose completo de comprobaciones superadas y flags en esta página.

¿Quién mantiene filelayer/filelayer?

+

filelayer/filelayer es mantenido por filelayer. La última actividad registrada en GitHub es del 2026-10-12, con 0 issues abiertos.

¿Hay alternativas a filelayer?

+

Sí. En ClaudeWave puedes explorar tools similares en /categories/tools, ordenados por popularidad o actividad reciente.

Despliega filelayer en tu cloud

Lleva este repo a producción en minutos. Cada plataforma genera su propio entorno con variables de entorno editables.

¿Mantienes este repo? Añade un badge a tu README

Pega el badge en tu README de GitHub para mostrar que está auditado por ClaudeWave. Cada badge enlaza de vuelta a esta página y muestra el Trust Score actual.

Featured on ClaudeWave: filelayer/filelayer
[![Featured on ClaudeWave](https://claudewave.com/api/badge/filelayer-filelayer)](https://claudewave.com/repo/filelayer-filelayer)
<a href="https://claudewave.com/repo/filelayer-filelayer"><img src="https://claudewave.com/api/badge/filelayer-filelayer" alt="Featured on ClaudeWave: filelayer/filelayer" width="320" height="64" /></a>

Más Tools

Alternativas a filelayer