Un coffre-fort numérique privé pour les documents administratifs qu'on ne peut pas se permettre de perdre. Une application full-stack où la sécurité est le sujet, pas une feature qu'on coche.
Les documents qui comptent vraiment sont toujours ceux qu'on ne retrouve pas le jour où on en a besoin.
Pièces d'identité, contrats, fiches de paie, avis d'imposition : ces documents sont dispersés entre des tiroirs physiques, des dossiers email et des clouds génériques. Le jour d'un dossier de location ou d'une déclaration, on perd du temps à les chercher, sans garantie de tomber sur la bonne version. Le problème n'est pas le stockage : c'est l'organisation, la récupération, et la confiance qu'on peut placer dans un endroit où vivent des données privées.
Un espace privé, organisé par dossiers thématiques, où chaque document appartient à une seule personne : la sienne.
PaperVault centralise tout dans cinq dossiers (Identité, Finances, Logement, Santé, Emploi) et par type de document. On uploade ses PDF et images, on les classe, on les retrouve par recherche ou par filtre, on les télécharge via des liens signés temporaires. Chaque document est lié à son propriétaire et invisible aux autres, vérifié à chaque requête.
Quatre décisions d'ingénierie. Chaque couche est implémentée, pas seulement consommée.
folders, documents, documentFiles : contraintes d'unicité, cascades et indexes. Un document peut porter plusieurs fichiers ; chaque ligne connaît son propriétaire. Le schéma Drizzle est la source de vérité, partagé en types end-to-end avec le reste du monorepo.
Validation MIME par magic bytes (pas par l'extension), noms de fichiers sanitisés, liens signés à durée de vie courte (5 min), et nettoyage R2 avant toute suppression en base. Un fichier ne traîne jamais sans owner, ni un objet orphelin après suppression.
12+ endpoints derrière le middleware Clerk, avec vérification de propriété par ligne sur chaque query, rate limiting par utilisateur et par action, validation Zod côté serveur. En profondeur : CSP, HSTS, X-Frame-Options DENY, Permissions-Policy restrictive. Aucune donnée n'existe sans session authentifiée.
Un flag d'environnement bloque toutes les mutations, côté API (403) comme côté UI, sur 15 documents fictifs seedés. Un test Playwright rejoue le parcours utilisateur, intercepte POST/PUT/PATCH/DELETE, échoue si une mutation est tentée, et capture six images cohérentes à chaque run. Le recruteur voit des captures de l'app réelle, sans risque d'état cassé.
Les règles que je me suis imposées pour que le mode démo reste sûr.
Toutes les routes /dashboard et /api passent par le middleware Clerk. Aucune donnée accessible sans session.
Upload, suppression et modification renvoient 403. L'UI désactive les boutons correspondants.
15 documents fictifs sous un user dédié. Le seed est destructif et interdit en production.
Playwright bloque POST/PUT/PATCH/DELETE. Le test échoue si une mutation est tentée : screenshots strictement read-only.
Des documents éparpillés entre tiroirs, mails et clouds. La bonne version, jamais là au bon moment.
Un coffre privé, organisé par dossier, où chaque pièce se retrouve en deux clics, avec une sécurité qui tient sans qu'on y pense.
Déployé sur Vercel, monitoré avec Sentry, testé sur chaque push via GitHub Actions (lint, types, tests, build). Le package mobile Expo/React Native partage le même monorepo et les mêmes schémas Zod. Ce que le projet démontre, ce n'est pas une feature : c'est une manière de construire, avec un projet full-stack tenu jusqu'au détail, une sécurité par défaut, et un workflow QA qui garantit qu'on voit toujours l'app telle qu'elle est vraiment.