Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.snakysec.com/llms.txt

Use this file to discover all available pages before exploring further.

OpenProject Setup — Runbook

Runbook opérationnel pour OpenProject Community Edition 14 self-hosted. Stack : platform/compose/openproject.yml (app + worker + Postgres dédié + Memcached). Source de vérité projet : docs/project/roadmap.yml (importé via seed-openproject.mjs).

TL;DR

# Déploiement initial pré-prod (depuis platform/)
make openproject-secrets   # provisionne secrets dans Vault (idempotent)
make openproject-up        # démarre stack + lance entrypoint Vault
                            # ATTENDRE ~5-7 min : asset compile + ~150 migrations DB

# IMPORTANT — db:seed DOIT être exécuté manuellement après le 1er boot
# (le rake task n'est pas chaîné automatiquement, et SANS lui aucun admin user
# n'existe → page login retourne "compte bloqué" ou login invalide).
DBPASS=$(docker exec mssp-vault vault kv get -field=db_password mssp/openproject)
ADMINPASS=$(docker exec mssp-vault vault kv get -field=admin_password mssp/openproject)
docker exec \
  -e RAILS_ENV=production \
  -e DATABASE_URL="postgres://openproject:${DBPASS}@openproject-db:5432/openproject" \
  -e OPENPROJECT_SEED_ADMIN_USER_PASSWORD="${ADMINPASS}" \
  -e OPENPROJECT_SEED_ADMIN_USER_NAME="admin" \
  -e OPENPROJECT_SEED_ADMIN_USER_MAIL="admin@snakysec.com" \
  -e OPENPROJECT_SEED_ADMIN_USER_PASSWORD_RESET="true" \
  -e OPENPROJECT_DEFAULT__LANGUAGE="fr" \
  openproject-app bundle exec rake db:seed

# Premier login (après seed)
# 1. Login : https://openproject.localhost
#    Login :    admin            (NB: c'est le LOGIN, pas l'email)
#    Password : <admin_password from Vault>
# 2. Force change password (OpenProject impose à la première connexion)

# 3. Générer API token : My Account → Access Tokens → Create new
#    Copier le token, l'ajouter au secret existant Vault mssp/openproject :
docker exec -e VAULT_TOKEN="<root>" -e VAULT_ADDR=http://127.0.0.1:8200 mssp-vault \
  vault kv patch mssp/openproject api_key="<token>"

# 4. Lancer le seed — les scripts trouvent le token automatiquement via Vault.
#    (mssp-app policy a déjà read sur mssp/data/openproject, pas de policy update.)
make openproject-seed
Note compose env : la variable OPENPROJECT_AVAILABLE__LANGUAGES ne doit pas être définie dans l’overlay — elle rend le setting available_languages non writable côté DB et casse le db:seed (Setting::NotWritableError). Configurer les langues disponibles via Admin → Languages.
Après le seed, OpenProject contient :
  • 1 projet SnakySec V1 Q2 2026
  • 7 versions (1 archive V1 + 4 sprints + Patterns transverses + Backlog Q3 2026)
  • 86 work packages (24 V1 + 4 arbitrages + 35 recos UX + 10 patterns + 6 bugs + 7 backlog)
URL du projet : https://openproject.localhost/projects/snakysec-v1/work_packages

Plan B — Custom fields + Types Epic/Bug (manuel UI, ~30 min)

L’API v3 d’OpenProject Community ne permet pas de créer custom fields ni de définir de nouveaux types — ces opérations sont réservées à l’admin UI. Une fois ce step fait, re-runner make openproject-seed enrichit automatiquement les WPs avec les valeurs custom du YAML.

Step 1 : Activer Epic + Feature + User Story + Bug + Documentation

  1. Login admin → /admin/types
  2. Pour chaque type non-existant (Epic, Feature, User Story, Bug, Documentation) :
    • Cliquer + Type
    • Nom : Epic (puis Feature, etc.)
    • Couleur : choisir distinctive (Epic violet, Bug rouge, Feature bleu, Documentation gris, User Story vert)
    • Save
  3. Vérifier que les 5 nouveaux types apparaissent à /admin/types

Step 2 : Activer ces types pour le projet snakysec-v1

  1. Project menu → Project settingsTypes
  2. Cocher : Epic, Feature, User Story, Bug, Documentation (en plus de Tâche déjà actif)
  3. Save

Step 3 : Créer les Custom Fields

/admin/custom_fields+ Custom field pour chacun :
NameTypeFormatRequiredPour les types
EffortListListNoAll work package types
Persona cibléListListNoFeature, User Story, Task
Problèmes adressésLong textLong textNoAll
ID UXStringStringNoAll
Axes DICTListList (multi-select)NoAll
FrameworkListList (multi-select)NoAll
Options pour Effort : S / M / L Options pour Persona ciblé : MSSP Operator / Client PME / Les deux Options pour Axes DICT : Disponibilité / Intégrité / Confidentialité / Traçabilité Options pour Framework : CIS / SCuBA / ISO27001 / NIS2 / RGPD / ANSSI / eIDAS

Step 4 : Activer les Custom Fields pour le projet

  1. Project menu → Project settingsCustom fields
  2. Cocher les 6 nouveaux + Save

Step 5 : Re-runner le seed

make openproject-seed
Le seed détectera les nouveaux types + custom fields et :
  • Re-mappe les types YAML (Epic, Feature, etc.) vers leurs nouveaux ids OpenProject
  • Pousse les valeurs custom.effort, custom.persona, custom.prob_addressed, etc. depuis le YAML

Step 6 : Configurer les vues sauvegardées

À faire manuellement dans l’UI (~10 min) :
  1. Vue Gantt exécutif :
    • /projects/snakysec-v1/work_packages → vue Gantt chart
    • Filtres : Type ∈ Epic, Feature, User Story
    • Colonnes : Subject, Type, Priority, Assignee, Start date, Due date, Estimated time
    • Save as new view → “V1 Gantt exécutif”
  2. Vue Kanban Sprint 1 :
    • Vue Board → Group by Status
    • Filtres : Version = “Sprint 1 — Bases produit”
    • Save as → “Sprint 1 Kanban”
  3. Vue Tableau complet :
    • Vue Table (default)
    • Colonnes : ID, Subject, Type, Status, Priority, Assignee, Estimated time, Version, Custom: Effort, ID UX
    • Save as → “V1 Tableau exécutif”

Architecture

┌─────────────────────────────────────────────────┐
│ Traefik (mssp-net)                              │
│   ↓ openproject.localhost (mkcert dev)          │
│   ↓ openproject.snakysec.com (LE prod)          │
└─────────┬───────────────────────────────────────┘


┌──────────────────────────────────────────────────┐
│ openproject-app (Rails web, port 8080)          │
│   - SECRET_KEY_BASE (Vault)                      │
│   - DATABASE_URL=postgres://openproject:***@db   │
│   - OPENPROJECT_HOST__NAME=openproject.localhost │
│   - OPENPROJECT_HTTPS=true (Traefik termine TLS) │
└─────────┬───────────────────────────┬────────────┘
          │ openproject-net           │ mssp-net (Traefik)
          ▼                           │
┌─────────────────────────────────┐   │
│ openproject-worker              │   │
│   (background jobs Rails)       │   │
└─────────┬───────────────────────┘   │
          │                           │
          ▼                           ▼
┌─────────────────────┐   ┌─────────────────────┐
│ openproject-db      │   │ openproject-cache   │
│ (Postgres 15 dédié) │   │ (Memcached 1.6)     │
│ /var/lib/postgresql │   │ in-memory only      │
└─────────────────────┘   └─────────────────────┘

Volumes Docker:
  - openproject-pgdata    — Postgres data
  - openproject-assets    — Files uploaded (attachments work packages)
  - openproject-pgdump    — Backup dumps (utilisé par make openproject-shell-db)
Réseaux :
  • openproject-net (interne, isolation BD/cache)
  • mssp-net (exposition Traefik, accès Vault)
Ressources : ~3 GB RAM total, ~3 GB disque (assets + DB).

Secrets Vault

Path : mssp/data/openproject
CléTypeRotationNotes
secret_key_baseHex 128Jamais sans purge sessionsRails session encryption
db_passwordRandom 32Jamais sans reset DBPostgres user openproject
admin_passwordRandom 24À la demande (--rotate-admin)Initial admin login uniquement
admin_emailStringÀ la demandeDefault admin@snakysec.com
Provisioning initial (idempotent) :
make openproject-secrets
# OU directement :
bash scripts/setup-openproject-secrets.sh
Rotation admin password (en cas de compromission) :
make openproject-rotate-admin
# Puis redémarrer l'app pour propager :
make openproject-restart
Rotation totale (DANGEREUSE) :
bash scripts/setup-openproject-secrets.sh --force
# Implique : reset DB password manuel + perte de toutes les sessions
Lecture admin password :
docker exec mssp-vault vault kv get -field=admin_password mssp/openproject

Workflow quotidien

1. Voir l’état du stack

make openproject-logs       # tail logs
docker ps | grep openproject

2. Updater la roadmap

Source de vérité : docs/project/roadmap.yml
  1. Éditer le YAML (ajout chantier, change status, dates)
  2. Re-seed (idempotent — n’écrase pas les modifs UI faites entre temps) :
    make openproject-seed
    
Le script crée seulement les work packages manquants. Modifs UI (status, assignee, due date) ne sont pas écrasées.

3. Backup ad-hoc DB

docker exec openproject-db pg_dump -U openproject -d openproject -F c \
  -f /var/openproject/pgdump/op-$(date +%Y%m%d-%H%M%S).dump

4. Restaurer un dump

docker exec -i openproject-db pg_restore -U openproject -d openproject -c < dump-file.dump

5. Shell Postgres

make openproject-shell-db
# Dans psql :
\dt                         # list tables
SELECT count(*) FROM work_packages;
\q

Vues et configuration UI

Après seed initial, configurer les vues globales (Admin → System → Work Package types).

Vue Gantt (timeline)

  1. Aller sur /projects/snakysec-v1/work_packages
  2. Cliquer “Gantt chart” dans le sélecteur de vue
  3. Filtres recommandés :
    • Type : Epic + Feature + User Story (cacher Tasks/Bugs/Doc pour vue exec)
    • Date colonnes : start_date / due_date
  4. Sauvegarder vue : “Save as new view” → “V1 Q2 2026 Gantt exécutif”

Vue Kanban

  1. Cliquer “Board” dans le sélecteur de vue
  2. Group by : Status
  3. Filtres : Version = “Sprint 1 — Bases produit” (ou autre sprint actif)
  4. Sauvegarder : “Sprint 1 Kanban”

Vue Tableau (spreadsheet-like, éditable inline)

  1. Cliquer “Table” dans le sélecteur de vue (par défaut)
  2. Colonnes recommandées :
    • ID, Subject, Type, Status, Priority, Assignee, Start date, Due date, Version, Custom: Effort, Custom: Persona, Custom: ux_id
  3. Inline edit : clic sur une cellule pour la modifier directement
  4. Sauvegarder : “V1 Tableau complet”

Custom fields

Si les custom fields (effort, persona, prob_addressed, ux_id, dict_axes, framework) ne sont pas créés automatiquement par le seed (ils dépendent de l’admin OpenProject), créer manuellement via : Admin → Custom fields → New custom field Pour chacun :
  • Type : List (effort, persona, dict_axes, framework) ou Text (prob_addressed) ou String (ux_id)
  • Cocher “For all projects” et “Required” = NON
  • Mappings dans docs/project/roadmap.yml section custom_fields
Le seed actuel ne pousse pas les custom fields (limitation API v3 OpenProject Community). À ajouter manuellement V1.

Backup et DR

Stratégie

OpenProject DB doit être incluse dans le plan de backup PG.
  • DB : openproject-db Postgres dédié → dump quotidien via Ofelia
  • Assets : volume openproject-assets → snapshot rsync
  • Vault secrets : déjà inclus dans le plan Vault DR (Shamir 5/3)

Ofelia config

À ajouter dans platform/docker/ofelia/config.ini :
[job-exec "openproject-pg-dump"]
schedule = 0 0 3 * * *   # 3am daily
container = openproject-db
command = pg_dump -U openproject -d openproject -F c -f /var/openproject/pgdump/openproject-$(date +%Y%m%d).dump

Restore en cas d’incident

  1. make openproject-down
  2. Identifier le dump cible : ls -lt artifacts/backup/openproject-pg/
  3. make openproject-up (DB vide)
  4. Attendre que openproject-db soit healthy
  5. docker exec -i openproject-db pg_restore -U openproject -d openproject -c < <dump>
  6. Restart : make openproject-restart

Troubleshooting

”OpenProject is starting up…” pendant >5 min

Premier boot = Rails asset compile + DB migrate. Normal jusqu’à ~5 min.
# Suivre la progression
make openproject-logs

# Indicateurs OK :
# - "Migrations executed"
# - "Worker started"
# - "Booted in X seconds"
Si >10 min : kill et inspecter logs.

”Vault is sealed” → entrypoint échoue

docker logs openproject-app 2>&1 | grep -i vault
# → "[openproject-entrypoint] Vault read failed" ou "Continuing without Vault injection"
Cause : Vault scellé (mode prod Shamir 3/2). Fix : unseal Vault d’abord
docker exec -it mssp-vault vault operator unseal
# Entrer 2 des 3 clés (DR Owners + notaire)
Puis redémarrer OpenProject :
make openproject-restart

“Connection refused” sur openproject.localhost

# Vérifier Traefik route
curl -k https://openproject.localhost/health_checks/default
# → 200 OK
Si pas accessible :
  1. Vérifier app.localhost fonctionne (Traefik OK ?)
  2. Vérifier label Traefik : docker inspect openproject-app | grep Host
  3. Vérifier que mkcert a généré le wildcard *.localhost (sinon make certs)

“Database password authentication failed”

Cause : db_password dans Vault ≠ password actuel Postgres. Si juste boot initial → vérifier setup-openproject-secrets.sh a tourné. Si après restore → resetter le password :
docker exec -it openproject-db psql -U postgres -c "ALTER USER openproject WITH PASSWORD '<password from vault>';"

Performance lente après plusieurs mois

# 1. Vacuum DB
docker exec openproject-db psql -U openproject -d openproject -c "VACUUM ANALYZE;"

# 2. Clear Memcached
docker restart openproject-cache

# 3. Restart app
make openproject-restart

Production deployment (VPS OVH)

À faire quand pré-prod stable et 1er client payant. Étapes :
  1. Adapter overlay : compose/openproject.yml → utiliser image avec tag pinned (openproject/community:14.x.y au lieu de :14)
  2. Reverse proxy : configurer LE wildcard pour openproject.snakysec.com dans Traefik prod
  3. Variable env : OPENPROJECT_HOST=openproject.snakysec.com
  4. Backup : confirmer Ofelia tourne et upload vers OVH Object Storage
  5. Vault : confirmer policy mssp-app accède bien à mssp/data/openproject en prod
  6. Smoke test : curl https://openproject.snakysec.com/health_checks/default

Références