Skip to main content
L’API REST sous /admin/v1 est la surface opérationnelle : les points d’accès que les scripts et la supervision appellent. Vérification de déploiement, bannissements, file, surcharges de configuration. Les objets d’administration de données (organisations, journal d’audit, règles de journalisation, gestion des principals d’administration) vivent sur l’administration en JMAP (urn:oximail:params:jmap:v2:admin, ADR-075) : les nouveaux objets d’administration s’y ajoutent, pas ici.

Authentification

Chaque point d’accès exige le token bearer d’administration de [admin] token :
Liez la surface d’administration à un port local séparé avec [server] admin_bind si vous la préférez hors de l’écouteur public. GET /health est la seule route ci-dessous qui ne vit pas sous /admin/v1 : elle prend un token bearer de session ordinaire, pas le token d’administration.

Carte des points d’accès

Déploiement & santé

Comptes et organisations

Sécurité

Opérations courrier

Configuration

Ce qui vit sur admin-over-JMAP

Les objets de données auxquels une console d’administration se lie sont des méthodes JMAP sous urn:oximail:params:jmap:v2:admin (ADR-075), réservées à une session d’administrateur : Tenant/get|set, Principal/getAdmin|setAdmin|queryAdmin|changesAdmin, Principal/setPasswordAdmin, Principal/enableMail (partage), AccountStorage/get, DeviceSession/queryAdmin|getAdmin|setAdmin, AppPassword/queryAdmin|getAdmin|setAdmin, ErasureJob/queryAdmin|getAdmin (conformité), HealthReport/query|get ; le journal d’administration, AuditEvent/*, a sa propre capacité (conformité). Les faits à connaître avant de lier un formulaire :
  • Tenant/set écrit plan, offlinePersistenceAllowed, customEmojiEnabled, maxFileVersionsPerNode (UnsignedInt | null, refusé nommément au-delà du plafond d’instance de 100 ; fichiers), defaultLanguage (en, fr, de, it) et legalIdentity, le responsable que déclare l’organisation, écrit en entier (null l’efface ; une valeur hostile est refusée en nommant son champ, legalIdentity/legalName ; les pays de traitement de l’instance ne sont pas à l’organisation) ; Tenant/get les projette avec maxPrincipals, resourceLimitsOverride, defaultTimezone et effectiveLegalIdentity, en lecture seule, chaque valeur avec sa provenance (tenant, instance, derived), voir l’identité légale. Le locataire ne porte plus de quota de stockage depuis ADR-151 : un quota se règle par compte. Une création ou une destruction envoyée à Tenant/set (ou à Preferences/set) est refusée nommément au lieu d’être ignorée. Chaque mutation est journalisée, et un changement fait bouger sessionState, si bien qu’un client ouvert relit la session.
  • Un identifiant de principal et son identifiant de compte sont deux UUID différents. Principal/getAdmin sert des propriétés dérivées : accountId, la clé de jointure vers AccountStorage, et les deux quotas du compte, maxStorageBytes (courrier) et maxDriveBytes (Drive ; null = la valeur par défaut du déploiement, 0 = sans limite), si bien qu’une valeur écrite par Principal/setAdmin {maxStorageBytes} ou {maxDriveBytes} se relit sur le même objet, null compris. Les patchs de rôle et de statut d’un principal sont en deux phases (lectures et écritures du plan compte d’abord, puis les écritures de lignes sous une seule transaction), après un interblocage qui faisait mourir chaque patch de ce genre en sqlite busy sur toutes les box pendant que la suite de tests restait verte sur un plan simulé. Principal/getAdmin sert aussi isEntity (entités).
  • Le patch de Principal/setAdmin est un vocabulaire fermé. Une clé qui n’en fait pas partie est refusée invalidProperties, chaque clé fautive nommée avec la liste de ce qui se pose, au lieu d’être écartée pendant que la réponse disait updated ; une clé password renvoie vers Principal/setPasswordAdmin. Une liste permissions non vide est refusée à la mise à jour comme à la création (aucun code d’autorisation ne la lit) ; la liste vide que renvoient les anciens clients reste acceptée.
  • Un départ est un patch sur un principal suspendu. Dans le même patch que status: "suspended", ou sur un principal déjà suspendu : forwardTo (l’identifiant de Principal du collègue que l’adresse du partant atteint désormais, par un alias exact de cette adresse) avec un forwardUntil facultatif, et mailboxGrantTo (le collègue qui lit la boîte du partant, un octroi en lecture seule écrit par l’organisation) avec un grantUntil obligatoire, à un an au plus. null retire l’un ou l’autre, à tout moment et dans tout état. Chaque refus (un collègue qui n’est pas un Principal de l’organisation, une date sans sa cible, une cible déjà désactivée) est décidé avant le gel, si bien qu’un successeur refusé ne laisse jamais un compte suspendu sans sa redirection. Principal/getAdmin sert ce qui reste en place sous offboarding ({forwardTo, forwardUntil, mailboxGrantTo, grantUntil}, ou null). C’est le même moteur qu’oximail account offboard, soumis au même contrôle de second facteur qu’un changement de rôle.
  • Principal/setPasswordAdmin {id, password} pose le mot de passe de quelqu’un d’autre sans connaître l’ancien. C’est une méthode dédiée, jamais une clé de patch, derrière la garde de second facteur stricte : un administrateur sans passkey enrôlée est refusé, pas averti, et la session doit avoir été ouverte avec cette passkey. Le hash, la fermeture de tous les accès ouverts du compte et la ligne d’audit dans la chaîne du sujet sont validés ensemble ou pas du tout. Fermer tous les accès va au-delà des tokens : chaque mot de passe d’application et chaque passkey du compte sont supprimés, et chaque règle active qui transfère du courrier est désactivée, les copies transférées encore en file marquées en échec, car un transfert survit à un mot de passe volé (transferts). La réponse porte tokensRevoked, appPasswordsRevoked, passkeysRevoked, forwardsSuspended et forwardCopiesCancelled, jamais le mot de passe ; oximail account set-password et PUT /admin/v1/accounts/{id} font de même. Une personne qui change son propre mot de passe ne ferme rien de tout cela. Un compte partagé ne porte jamais de mot de passe (forbidden). Pas de changement forcé à la première connexion : le même hash sert IMAP, SMTP, CalDAV et SMB, qui ne savent pas afficher d’écran.
  • DeviceSession/*Admin et AppPassword/*Admin voient et coupent les sessions d’appareil et les mots de passe d’application d’un autre compte : queryAdmin {filter: {principalId}} (le seul filtre, obligatoire) liste les identifiants, getAdmin {ids} sert les métadonnées, setAdmin {destroy} coupe ; create et update n’existent pas, puisqu’une session s’ouvre par authentification et qu’un mot de passe d’application est frappé par son titulaire, seul à voir le secret. Une session porte deviceLabel, userAgent, authMethod, createdAt, lastSeenAt, expiresAt, et ce dont le support a besoin pour répondre à « mon téléphone ne reçoit plus de notifications » : hasPushSubscription (un abonnement Web Push vérifié et non expiré pour cet appareil), pushLastDeliveredAt (la dernière fois que le service de push a accepté une notification pour lui, notée au plus une fois toutes les dix minutes) et pushEndpointOrigin (le schéma et l’hôte du service de push, Apple, Google ou Mozilla, jamais le reste de l’URL, qui est un secret de remise ; un abonnement que le service a abandonné n’est plus actif mais garde son origine) ; un mot de passe d’application name, scope (une liste personnalisée est servie en scope: "custom" avec les URN dans capabilities), readOnly, createdAt, lastUsedAt, jamais le secret ni son hash. Couper un mot de passe d’application ferme aussi chaque token bearer qu’il a ouvert. Pas de garde de second facteur : ces méthodes ne font que retirer un accès. Chaque coupure est une ligne d’audit dans la chaîne de la personne à qui elle arrive, l’administrateur en acteur.
  • Un opérateur d’instance ne se modifie qu’à la console. Principal/setPasswordAdmin, une mise à jour Principal/setAdmin et un destroy de DeviceSession/setAdmin ou d’AppPassword/setAdmin qui visent le compte d’un opérateur d’instance sont refusés forbidden (les autres identifiants du lot passent) : réinitialiser ce mot de passe reviendrait à lire toute la box. On retire d’abord le privilège à la console.
  • HealthReport/query|get servent la ronde doctor de la box. Un timer (oximail-health.timer, toutes les 15 minutes, sous l’utilisateur du service ; oximail setup health-timer --apply le pose sur une box installée avant qu’il existe) enregistre oximail doctor --json --record, et les relevés de plus de 30 jours sont purgés. Un relevé porte id, takenAt, durationMs, exitCode, status (ok, degraded, failed), ageSeconds, stale (plus vieux que deux fois la cadence : un timer mort se lit « dernier contrôle il y a trois jours », jamais « tout va bien ») et report, le tableau --json exact : check, status, detail, plus family (mail, backup, storage, security, accounts, system, extension), facts avec les faits datés en UTCDate (observedAt de la dernière sauvegarde ou du dernier exercice de restauration, expiresAt du certificat) et, sur un warn ou un fail, remedy, le verbe à taper. query filtre sur after et before (UTCDate) et trie sur takenAt ; get {ids: null} répond requestTooLarge au-delà de maxObjectsInGet, donc le relevé le plus récent se lit par un query (takenAt décroissant, limit: 1) suivi d’un get. Un administrateur de locataire lit les relevés pris pendant que son organisation était seule sur la box, soit tous les relevés d’une box à une seule organisation ; un relevé pris quand la box en portait deux lui reste fermé pour toujours, et seul un opérateur d’instance le lit. Un membre est refusé accountNotSupportedByMethod à la porte de dispatch ; un compte qui porte la capacité d’administration par une surcharge sans être administrateur reçoit forbidden. detail recopie des valeurs qu’une organisation écrit (adresses, alias) : ne l’afficher qu’en texte brut.

Conventions

  • Les réponses sont en JSON ; les erreurs portent une raison lisible par machine et un statut HTTP qui dit ce qu’il veut dire.
  • Tout ce qui est ici est aussi accessible par la CLI : les deux sont des adaptateurs minces au-dessus de la même orchestration, le comportement est identique.
  • L’API est par organisation partout où les données le sont ; il n’existe volontairement pas de vue de données inter-organisations.