Partner Onboarding
Summary
Admins register new data providers (promoteurs, aggregators) via the admin API. Each partner receives a dedicated chroot-jailed Linux SFTP user under the sftp-feeds group, a cryptographically random password (AES-256-GCM encrypted in the database), and an IngestionSource record for the feed importer. The plaintext password is shown once at creation time for the admin to share with the partner. Subsequent operations allow metadata updates, SFTP username rename, password regeneration, and full deletion.
State Diagram
stateDiagram-v2 state "Validation des données" as Validating state "Demande rejetée" as Rejected state "Création utilisateur SFTP" as SftpUserCreating state "Erreur SFTP" as SftpFailed state "Transaction base de données" as DbTransacting state "Erreur base de données" as DbFailed state "Partenaire créé" as Active [*] --> Validating: admin creates partner Validating --> Rejected: name invalid or sftpUsername taken Validating --> SftpUserCreating: createSftpUser(username, password) SftpUserCreating --> SftpFailed: OS error SftpUserCreating --> DbTransacting: Partner + IngestionSource create DbTransacting --> DbFailed: DB error → deleteSftpUser (cleanup) DbTransacting --> Active: partner.status = pending_mapping Active --> [*]
Steps
1. Authenticate Admin (Actor: system)
authenticateAdmin preHandler validates the admin JWT (ADMIN_JWT_SECRET, 15 min HS256).
2. Derive and Validate SFTP Username (Actor: system)
sanitizeUsername(name) converts the partner display name to a safe Linux username (lowercase, alphanumeric + hyphens). If the result is empty, 400 INVALID_NAME is returned. Uniqueness is checked against partner.sftpUsername in the DB; conflict returns 409 USERNAME_EXISTS.
Outcome: Valid, unique SFTP username derived
3. Generate Password and Create Linux User (Actor: system)
randomBytes(18).toString("base64") generates a 24-character cryptographically random password. createSftpUser(username, password) from lib/sftp.ts creates a chroot-jailed Linux user at /home/{username}/feeds/. If this OS call fails, 500 SFTP_ERROR is returned immediately — no DB record is created.
Outcome: Linux SFTP user exists on the server
4. DB Transaction — Partner + IngestionSource (Actor: system)
Prisma $transaction:
partner.createwithsftpPassword: encrypt(password)(AES-256-GCM),feedPath: /home/{username}/feeds/,status: "pending_mapping".ingestionSource.createlinking to the partner withconfig: { feedPath, feedFormat }.
If the transaction fails: deleteSftpUser(username) is called as best-effort cleanup, then 500 is returned.
Outcome: Partner + IngestionSource in DB, partner status is pending_mapping
5. Return Credentials (Actor: admin)
Response includes { partner: { ...fields, sftpPassword: plaintextPassword } }. The plaintext password is only exposed at this moment — subsequent reads of the partner never include it. Admin must relay credentials to the partner out-of-band.
Outcome: Admin has SFTP credentials to share with partner
6. Update Partner Metadata (Actor: admin)
PUT /admin/partners/:id — updates name, contactEmail, website, feedFormat, photoBaseUrl, status. Does not touch the SFTP account. Returns partner without encrypted password.
7. Rename SFTP Username (Actor: admin)
POST /admin/partners/:id/rename-sftp:
- Validate new username format.
- Check uniqueness.
renameSftpUser(oldUsername, newUsername)— kills active sessions, moves home directory.- Update
partner.sftpUsername,partner.feedPath, andingestionSource.configin DB.
8. Regenerate Password (Actor: admin)
POST /admin/partners/:id/regenerate-password:
- Generate new
randomBytes(18)password. changeSftpPassword(username, newPassword).partner.update({ sftpPassword: encrypt(newPassword) }).- Return plaintext password once.
9. Delete Partner (Actor: admin)
DELETE /admin/partners/:id:
deleteSftpUser(username)— must succeed first.partner.delete— cascades toIngestionSourcevia FK.
Error: If SFTP deletion fails, DB record is not deleted to avoid orphaned Linux users.
Error States
- Name produces empty sanitized username → 400
INVALID_NAME - SFTP username already taken → 409
USERNAME_EXISTS - Linux user creation fails → 500
SFTP_ERROR(no DB record created) - DB transaction fails → 500
DB_ERROR, Linux user cleaned up best-effort - Partner not found → 404
NOT_FOUND
Related Processes
- partner-mapping-activation — next step after partner sends their first feed file
- file-deposit-monitoring — tracks deposits from this partner’s SFTP directory
- sftp-xml-ingestion — processes files from this partner’s feed path