[{"data":1,"prerenderedAt":628},["ShallowReactive",2],{"docs-nav":3,"/docs/getting-started/architecture":60},[4],{"title":5,"path":6,"stem":7,"children":8,"page":59},"Docs","/docs","docs",[9,27,45],{"title":10,"path":11,"stem":12,"children":13,"page":-1},"Premiers pas","/docs/getting-started","docs/1.getting-started/index",[14,15,19,23],{"title":10,"path":11,"stem":12},{"title":16,"path":17,"stem":18},"Introduction","/docs/getting-started/introduction","docs/1.getting-started/1.introduction",{"title":20,"path":21,"stem":22},"Claim, Adopt, Fork","/docs/getting-started/concepts","docs/1.getting-started/2.concepts",{"title":24,"path":25,"stem":26},"Architecture","/docs/getting-started/architecture","docs/1.getting-started/3.architecture",{"title":28,"path":29,"stem":30,"children":31,"page":-1},"Installation","/docs/installation","docs/2.installation/index",[32,33,37,41],{"title":28,"path":29,"stem":30},{"title":34,"path":35,"stem":36},"Déploiement Docker","/docs/installation/docker","docs/2.installation/1.docker",{"title":38,"path":39,"stem":40},"Déploiement VPS","/docs/installation/vps","docs/2.installation/2.vps",{"title":42,"path":43,"stem":44},"Déploiement Alwaysdata","/docs/installation/alwaysdata","docs/2.installation/3.alwaysdata",{"title":46,"path":47,"stem":48,"children":49,"page":-1},"Administration","/docs/administration","docs/3.administration/index",[50,51,55],{"title":46,"path":47,"stem":48},{"title":52,"path":53,"stem":54},"Configuration","/docs/administration/configuration","docs/3.administration/1.configuration",{"title":56,"path":57,"stem":58},"Maintenance","/docs/administration/maintenance","docs/3.administration/2.maintenance",false,{"id":61,"title":24,"body":62,"description":622,"extension":623,"meta":624,"navigation":625,"path":25,"seo":626,"stem":26,"__hash__":627},"docs/docs/1.getting-started/3.architecture.md",{"type":63,"value":64,"toc":606},"minimark",[65,69,73,78,200,204,215,219,286,291,314,318,322,390,394,401,404,434,438,508,515,519,525,543,547,579,582,602],[66,67,24],"h1",{"id":68},"architecture",[70,71,72],"p",{},"Suddenly est une application Django standard qui tourne partout où Python et PostgreSQL sont disponibles. Docker est optionnel.",[74,75,77],"h2",{"id":76},"stack-technique","Stack technique",[79,80,81,97],"table",{},[82,83,84],"thead",{},[85,86,87,91,94],"tr",{},[88,89,90],"th",{},"Composant",[88,92,93],{},"Technologie",[88,95,96],{},"Rôle",[98,99,100,112,123,134,145,156,167,178,189],"tbody",{},[85,101,102,106,109],{},[103,104,105],"td",{},"Backend",[103,107,108],{},"Python 3.11+ + Django 5.0",[103,110,111],{},"Application principale (SSR)",[85,113,114,117,120],{},[103,115,116],{},"API REST",[103,118,119],{},"Django REST Framework",[103,121,122],{},"API + OpenAPI (drf-spectacular)",[85,124,125,128,131],{},[103,126,127],{},"Auth",[103,129,130],{},"django-allauth",[103,132,133],{},"Authentification",[85,135,136,139,142],{},[103,137,138],{},"Frontend",[103,140,141],{},"HTMX + Alpine.js + UnoCSS",[103,143,144],{},"UI sans SPA (~32KB total)",[85,146,147,150,153],{},[103,148,149],{},"Fédération",[103,151,152],{},"ActivityPub custom",[103,154,155],{},"Protocole de fédération",[85,157,158,161,164],{},[103,159,160],{},"Signatures",[103,162,163],{},"HTTP Signatures (RSA-SHA256)",[103,165,166],{},"Authentification des requêtes AP",[85,168,169,172,175],{},[103,170,171],{},"Base de données",[103,173,174],{},"PostgreSQL 16",[103,176,177],{},"Datastore principal",[85,179,180,183,186],{},[103,181,182],{},"Cache / Queue",[103,184,185],{},"Redis 7 (optionnel)",[103,187,188],{},"Broker Celery + cache",[85,190,191,194,197],{},[103,192,193],{},"Workers",[103,195,196],{},"Celery + django-celery-beat",[103,198,199],{},"Tâches asynchrones",[74,201,203],{"id":202},"flux-de-services","Flux de services",[205,206,211],"pre",{"className":207,"code":209,"language":210},[208],"language-text","Browser ──HTMX/JSON──► Django App ──ORM──► PostgreSQL\n                           │\nRemote Instance ──AP──►    │ ──enqueue──► Celery Worker\n                           │                  │\n                           └──────────────► Redis (optionnel)\n                                        ou DB cache (fallback)\n","text",[212,213,209],"code",{"__ignoreMap":214},"",[74,216,218],{"id":217},"matrice-de-dépendances","Matrice de dépendances",[79,220,221,236],{},[82,222,223],{},[85,224,225,227,230,233],{},[88,226,90],{},[88,228,229],{},"Requis",[88,231,232],{},"Optionnel",[88,234,235],{},"Fallback",[98,237,238,251,262,274],{},[85,239,240,243,246,249],{},[103,241,242],{},"Python 3.11+",[103,244,245],{},"✓",[103,247,248],{},"—",[103,250,248],{},[85,252,253,256,258,260],{},[103,254,255],{},"PostgreSQL",[103,257,245],{},[103,259,248],{},[103,261,248],{},[85,263,264,267,269,271],{},[103,265,266],{},"Redis",[103,268,248],{},[103,270,245],{},[103,272,273],{},"DB cache",[85,275,276,279,281,283],{},[103,277,278],{},"Celery",[103,280,248],{},[103,282,245],{},[103,284,285],{},"Tâches synchrones",[287,288,290],"h3",{"id":289},"profils-de-déploiement","Profils de déploiement",[292,293,294,302,308],"ul",{},[295,296,297,301],"li",{},[298,299,300],"strong",{},"Petite instance (\u003C 50 utilisateurs)"," — Django + PostgreSQL uniquement",[295,303,304,307],{},[298,305,306],{},"Instance moyenne (50–500)"," — + Redis recommandé",[295,309,310,313],{},[298,311,312],{},"Grande instance (500+)"," — + Celery pour la livraison fédérée asynchrone",[74,315,317],{"id":316},"fédération-activitypub","Fédération ActivityPub",[287,319,321],{"id":320},"endpoints-exposés","Endpoints exposés",[79,323,324,337],{},[82,325,326],{},[85,327,328,331,334],{},[88,329,330],{},"Endpoint",[88,332,333],{},"Protocole",[88,335,336],{},"Description",[98,338,339,352,365,378],{},[85,340,341,346,349],{},[103,342,343],{},[212,344,345],{},"/.well-known/webfinger",[103,347,348],{},"WebFinger",[103,350,351],{},"Découverte des acteurs",[85,353,354,359,362],{},[103,355,356],{},[212,357,358],{},"/.well-known/nodeinfo",[103,360,361],{},"NodeInfo",[103,363,364],{},"Métadonnées de l'instance",[85,366,367,372,375],{},[103,368,369],{},[212,370,371],{},"/users/{id}/inbox",[103,373,374],{},"ActivityPub",[103,376,377],{},"Réception des activités",[85,379,380,385,387],{},[103,381,382],{},[212,383,384],{},"/users/{id}/outbox",[103,386,374],{},[103,388,389],{},"Publication des activités",[287,391,393],{"id":392},"sécurité-des-requêtes","Sécurité des requêtes",[70,395,396,397,400],{},"Toutes les requêtes ActivityPub sortantes sont signées avec RSA-SHA256. Les en-têtes signés sont : ",[212,398,399],{},"(request-target) host date digest",".",[70,402,403],{},"Les clés ne se génèrent pas à la main : Suddenly les crée lui-même.",[292,405,406,428],{},[295,407,408,411,412,415,416,419,420,423,424,427],{},[298,409,410],{},"Clés d'instance"," — ",[212,413,414],{},"ensure_instance_keys()"," écrit ",[212,417,418],{},"keys/private.pem"," (chmod 0600) et ",[212,421,422],{},"keys/public.pem"," s'ils sont absents. C'est ce que fait ",[212,425,426],{},"./scripts/init-dev.sh"," en local.",[295,429,430,433],{},[298,431,432],{},"Clés d'acteur"," — chaque utilisateur, personnage et partie reçoit sa propre paire RSA à la création, via signal Django. Rien à faire.",[74,435,437],{"id":436},"rate-limiting","Rate limiting",[79,439,440,453],{},[82,441,442],{},[85,443,444,447,450],{},[88,445,446],{},"Surface",[88,448,449],{},"Limite",[88,451,452],{},"Où",[98,454,455,468,480,495],{},[85,456,457,460,463],{},[103,458,459],{},"Inbox AP — instance connue",[103,461,462],{},"100 req/min par domaine",[103,464,465],{},[212,466,467],{},"django-ratelimit",[85,469,470,473,476],{},[103,471,472],{},"Inbox AP — instance inconnue",[103,474,475],{},"10 req/min par domaine",[103,477,478],{},[212,479,467],{},[85,481,482,489,492],{},[103,483,484,485,488],{},"Connexion (",[212,486,487],{},"POST /accounts/login/",")",[103,490,491],{},"10 tentatives/min par IP",[103,493,494],{},"middleware Django",[85,496,497,503,506],{},[103,498,499,500,488],{},"Inscription (",[212,501,502],{},"POST /accounts/signup/",[103,504,505],{},"5 tentatives/min par IP",[103,507,494],{},[70,509,510,511,514],{},"L'API REST n'est ",[298,512,513],{},"pas"," limitée à ce jour : aucune classe de throttling DRF n'est configurée. Si vous exposez l'API publiquement, prévoyez une limitation en amont (nginx, WAF).",[74,516,518],{"id":517},"structure-du-projet","Structure du projet",[205,520,523],{"className":521,"code":522,"language":210},[208],"suddenly/\n├── config/settings/\n│   ├── base.py          # Paramètres partagés\n│   ├── development.py   # Overrides dev\n│   └── production.py    # Production (sécurité renforcée, env requis)\n├── config/asgi.py\n├── suddenly/wsgi.py     # Point d'entrée WSGI (pas config/wsgi.py)\n├── manage.py\n├── pyproject.toml       # Dépendances (pip install -e \".[federation]\")\n├── docker-compose.yml\n├── docker-compose.dev.yml\n├── scripts/             # install.sh, entrypoint.sh, backup.sh, restore.sh…\n├── staticfiles/         # Static collectés (whitenoise)\n└── media/               # Uploads utilisateurs\n",[212,524,522],{"__ignoreMap":214},[526,527,528],"blockquote",{},[70,529,530,531,534,535,538,539,542],{},"Le module WSGI est ",[212,532,533],{},"suddenly/wsgi.py"," — le paquet ",[212,536,537],{},"config/"," ne contient que les settings et l'ASGI. Une commande ",[212,540,541],{},"gunicorn config.wsgi:application"," échouera.",[74,544,546],{"id":545},"qualité-et-ci","Qualité et CI",[292,548,549,555,561,567,573],{},[295,550,551,554],{},[298,552,553],{},"Lint"," : Ruff (règles E, F, I, N, W, UP)",[295,556,557,560],{},[298,558,559],{},"Type check"," : mypy strict + django-stubs",[295,562,563,566],{},[298,564,565],{},"Tests"," : pytest + pytest-cov, seuil de couverture 50%",[295,568,569,572],{},[298,570,571],{},"CI"," : GitHub Actions — ruff + mypy + pytest --cov, bloque les merges en cas d'échec",[295,574,575,578],{},[298,576,577],{},"Pre-commit"," : ruff --fix + mypy",[70,580,581],{},"Lancer tous les checks en local :",[205,583,587],{"className":584,"code":585,"language":586,"meta":214,"style":214},"language-bash shiki shiki-themes github-light github-dark","make check\n","bash",[212,588,589],{"__ignoreMap":214},[590,591,594,598],"span",{"class":592,"line":593},"line",1,[590,595,597],{"class":596},"sScJk","make",[590,599,601],{"class":600},"sZZnC"," check\n",[603,604,605],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":214,"searchDepth":607,"depth":607,"links":608},2,[609,610,611,615,619,620,621],{"id":76,"depth":607,"text":77},{"id":202,"depth":607,"text":203},{"id":217,"depth":607,"text":218,"children":612},[613],{"id":289,"depth":614,"text":290},3,{"id":316,"depth":607,"text":317,"children":616},[617,618],{"id":320,"depth":614,"text":321},{"id":392,"depth":614,"text":393},{"id":436,"depth":607,"text":437},{"id":517,"depth":607,"text":518},{"id":545,"depth":607,"text":546},"Stack technique de Suddenly, flux de services et dépendances.","md",{},true,{"title":24,"description":622},"Fq6oaUbf7yJdZBNqt4w1N1fbZpe0yCyu3LqepeX7b3s",1784021864437]