# Stockage

Chaque projet doté d'une base a un stockage de fichiers, organisé en seaux, servi à l'adresse du projet au format de `storage-js`. Cette page dit comment s'en servir, comment l'accès aux fichiers se décide, ce que chaque palier permet, et ce qui n'est pas encore servi, à commencer par le point d'accès compatible S3.

## Ce qui est servi

Le stockage est servi sous `https://<identifiant>.donnees.bastide.app/storage/v1` (voir [Base de données](database.md) pour l'adresse). Votre application garde `supabase-js` : les chemins, les corps et les réponses sont ceux que `storage-js` appelle.

| Geste de `storage-js` | Servi |
| --- | --- |
| `createBucket`, `getBucket`, `listBuckets`, `updateBucket`, `deleteBucket`, `emptyBucket` | oui |
| `upload`, `update`, `download`, `list`, `remove`, `move`, `copy`, `info`, `exists` | oui |
| `createSignedUrl`, `createSignedUrls`, `createSignedUploadUrl`, `uploadToSignedUrl` | oui |
| `getPublicUrl` | oui |
| Téléversement repris (tus) | oui |
| Transformation d'images (`/storage/v1/render/image/…`) | oui, si l'installation porte le service de rendu ; sinon 501 |
| Point compatible S3 (`/storage/v1/s3`) | **non** : 404, code `chemin_inconnu` (LIM-07) |

> **Note.** Le point compatible S3 (pour le SDK AWS, `rclone` ou tout client S3) n'est pas encore servi. Il est prévu avec le lot L2 ; le servir à moitié ferait écrire une application contre une surface qui changera. Employez `storage-js`, ou l'API HTTP qu'il appelle.

Il n'y a pas d'écran de la console pour les seaux et les fichiers, ni d'outil MCP : le stockage se pilote depuis votre application. L'écran **Données** affiche le point d'entrée `/storage/v1` dans la carte « Base PostgreSQL ».

## Les seaux

Un seau se crée avec trois réglages :

- **public** : un seau public sert ses fichiers sans clé, par l'URL publique ;
- **taille maximale d'un fichier** : la plus petite de celle du seau et de celle du palier s'applique ;
- **types admis** : une liste de types MIME, `image/*` admet toute image ; la comparaison ignore la casse et les paramètres. Une liste vide admet tout.

```js
import { createClient } from "@supabase/supabase-js";

const supabase = createClient("https://<identifiant>.donnees.bastide.app", "<clé publiable>");

await supabase.storage.createBucket("avatars", {
  public: false,
  fileSizeLimit: "5MB",
  allowedMimeTypes: ["image/*"],
});
await supabase.storage.from("avatars").upload(`${userId}/portrait.png`, fichier);
```

Créer un seau demande en pratique la clé secrète, côté serveur, ou une politique qui l'autorise.

## Qui peut lire et écrire

Chaque requête s'exécute **sous le rôle de l'appelant** : le rôle anonyme avec la seule clé publiable, le rôle authentifié avec un jeton de session, le rôle de service avec une clé secrète. Ce sont les politiques de la table `storage.objects` de votre base qui décident ; la plateforme ne porte aucune règle d'accès aux fichiers. Une lecture refusée l'est par la politique, pas par l'application.

Une politique de stockage s'écrit comme une autre (voir [Sécurité des données](data-security.md)). Par exemple, chacun ne lit que son dossier :

```sql
create policy "lire son dossier" on storage.objects for select
  using (bucket_id = 'avatars' and (storage.foldername(name))[1] = (select auth.uid())::text);
```

Trois chemins se passent de clé, parce qu'un navigateur n'ajoute aucun en-tête à une balise `<img>` ou à un formulaire : l'URL publique d'un seau public, l'URL signée de lecture, et l'URL signée de téléversement. La permission d'une URL signée est jugée quand elle est signée.

- Une URL signée vit une heure par défaut, **sept jours au plus** ; au-delà, 400 `duree_refusee`.
- Un téléversement est d'abord éprouvé sous votre politique : un refus coûte une requête, pas un fichier envoyé.

## Les limites par palier

| Palier | Taille maximale d'un fichier | Stockage inclus |
| --- | --- | --- |
| Découverte | 50 Mo | 5 Go |
| Standard | 5 Go | 50 Go |
| Pro | 50 Go | 250 Go |
| Souverain | aucune borne de palier | 1 To |

Les tailles se comptent en puissances de 1 024. Aujourd'hui, rien n'est facturé au-delà de l'inclus : l'inclus est le quota.

- **À 80 % du quota**, le franchissement est consigné au journal d'audit du projet.
- **À 100 %**, toute nouvelle écriture est refusée (413, `quota_atteint`) avec un message qui nomme le quota ; les lectures continuent.

## Le nettoyage

Chaque jour, une passe par projet retire ce qui ne sert plus : un téléversement repris que personne n'a nourri depuis 24 heures, un téléversement en plusieurs parties abandonné depuis 24 heures, et des octets qu'aucun objet ne désigne depuis une heure. Chaque passe qui retire quelque chose se consigne.

## Sauvegarde des fichiers

Les fichiers de vos seaux ne sont pas restaurés avec la base : une restauration de base rend les lignes de `storage.objects` à l'instant du dump, pas les octets. Gardez ailleurs une copie de ce qui ne se reconstruit pas (voir [Sauvegardes](backups.md)).

## Erreurs fréquentes

| Code | Statut | Cause et remède |
| --- | --- | --- |
| `refuse_par_la_politique` | 403 | Une politique de `storage.objects` refuse ce geste à cet appelant. |
| `fichier_trop_grand` | 413 | Le fichier dépasse la borne du seau ou du palier ; le message dit laquelle. |
| `quota_atteint` | 413 | Le stockage du projet est plein ; supprimez des fichiers ou changez de palier. |
| `type_refuse` | 415 | Le seau n'admet pas ce type ; le message liste les types admis. |
| `seau_introuvable` | 404 | Le seau n'existe pas, ou la politique ne vous le laisse pas voir. |
| `existe_deja` | 409 | Un objet porte déjà ce nom ; téléversez avec `upsert: true` pour le remplacer. |
| `nom_refuse` | 400 | Le nom de l'objet n'est pas admis. |
| `duree_refusee` | 400 | L'URL signée demandée dépasse sept jours. |
| `chemin_inconnu` | 404 | Le chemin n'est pas servi, par exemple le point S3. |

## Voir aussi

- [Base de données](database.md), [Sécurité des données](data-security.md), [Authentification](auth.md), [Sauvegardes](backups.md)
- [Cache et images](cache.md), [Limites par palier](limits.md), [Migrer depuis Supabase](from-supabase.md)
