# Applications

> Pousser une image et la mettre en service, depuis une CI ou ton poste.

Une application, c'est **ton image Docker** hébergée chez nous. Ces routes portent le
parcours que `ark deploy` suit : créer le service, pousser l'image dans le registre,
désigner la version qui tourne. Elles acceptent un jeton de portée `REGISTRY` comme un
jeton `FULL`.

## Vérifier le jeton

```http
GET /api/apps/whoami
```

```json
{ "email": "toi@exemple.fr" }
```

## Lister

```http
GET /api/apps
```

```json
{
  "items": [
    {
      "id": "cmu5xje1k0003itlhpkwwfq66",
      "slug": "crm",
      "name": "crm",
      "status": "ACTIVE",
      "imageRef": "registry.arkya.gg/c-7f3a91c04d2e/crm@sha256:6246f76d…",
      "address": "0.0.0.0:30016",
      "platform": "linux/arm64",
      "releasedAt": "2026-09-17T21:55:56.009Z"
    }
  ]
}
```

Le `slug` est unique chez Arkya, tous comptes confondus : c'est lui qui nomme le dépôt
dans le registre.

## Créer

```http
POST /api/apps
```

```json
{
  "slug": "crm",
  "hostId": null,
  "projectId": "cmu5xje0s0001itlh3jg2pptf",
  "dimensions": { "cpuCores": 1, "ramGb": 2, "storageGb": 10 }
}
```

Le slug s'écrit en minuscules, chiffres et tirets, de 3 à 40 caractères. Les trois autres
champs acceptent `null` : sans `hostId` on choisit un nœud capable, sans `projectId`
l'application rejoint le projet par défaut, sans `dimensions` elle prend la taille
d'entrée de gamme.

Pour connaître le prix avant de créer :

```http
POST /api/apps/quote
```

```json
{ "hostId": null, "dimensions": { "cpuCores": 1, "ramGb": 2, "storageGb": 10 } }
```

```json
{
  "hostId": "cmr25fjrv000311zcupybejnu",
  "hostLabel": "Gravelines — France",
  "dimensions": { "cpuCores": 1, "ramGb": 2, "storageGb": 10 },
  "monthlyMillicents": 1620
}
```

Deux listes aident à remplir ces champs :

```http
GET /api/apps/nodes
GET /api/apps/projects
```

## Pousser une image

Le registre parle le protocole Docker standard. Les identifiants ne sont pas ton jeton :
demande-les, ils sont propres à ton compte et n'ouvrent que ton espace.

```http
GET /api/registry/credentials
```

```json
{
  "host": "registry.arkya.gg",
  "project": "c-7f3a91c04d2e",
  "username": "robot$c-7f3a91c04d2e+cli-a91f3c",
  "secret": "Ark1…"
}
```

```bash
echo "$SECRET" | docker login registry.arkya.gg -u "$USERNAME" --password-stdin
docker build --platform linux/amd64 -t registry.arkya.gg/c-7f3a91c04d2e/crm:2026-09-18 .
docker push registry.arkya.gg/c-7f3a91c04d2e/crm:2026-09-18
```

<Warning>
Construis pour l'architecture du nœud. Une image `arm64` poussée vers un nœud `amd64`
démarre puis meurt sans message clair — `platform`, dans la liste, dit ce qui tourne.
</Warning>

Une fois l'image poussée, déclare la version :

```http
POST /api/apps/{slug}/releases
```

```json
{ "tag": "2026-09-18", "digest": "sha256:6246f76d…" }
```

Le `digest` est celui que `docker push` affiche à la fin : c'est lui qui identifie
l'image, le `tag` n'est qu'un nom qu'on peut déplacer.

```http
GET /api/apps/{slug}/releases
```

## Lire l'analyse d'une version

Toute image reçue est analysée. Cette route rend l'état de l'analyse, et l'attend si elle
court encore :

```http
GET /api/apps/{slug}/releases/{tag}/scan
```

```json
{
  "tag": "2026-09-18",
  "digest": "sha256:6246f76d…",
  "scanStatus": "WARNED",
  "scannedAt": "2026-09-18T09:12:44.000Z",
  "scanVerdict": {
    "counts": { "Critical": 0, "High": 3, "Medium": 21, "Low": 8 },
    "blocking": [],
    "warnings": [
      {
        "id": "CVE-2024-2511",
        "package": "openssl",
        "version": "3.0.11",
        "fixVersion": "3.0.13",
        "severity": "High"
      }
    ],
    "warningCount": 3,
    "scanner": "Trivy"
  }
}
```

| `scanStatus` | Ce que ça veut dire |
| --- | --- |
| `PENDING` | L'analyse court encore. |
| `PASSED` | Rien de sérieux. |
| `WARNED` | Des failles sérieuses, mais qui ne concernent que ton application. |
| `BLOCKED` | Une faille laisse sortir du conteneur : cette version ne sera pas mise en service. |
| `FAILED` | L'analyse n'a pas abouti. Elle ne retient pas la mise en service. |

Une entrée de `blocking` porte en plus un `reason` : `isolation` quand le paquet touché
est celui qui isole les conteneurs, `scope` quand le vecteur CVSS dit que l'impact dépasse
le composant vulnérable.

Le même rapport s'ouvre dans l'espace client, onglet **Versions** du service : chaque faille
y porte son paquet, la version qui corrige, ce qu'en dit la publication, et la raison pour
laquelle elle bloque ou non.

Les images en service sont réanalysées chaque nuit — les failles se découvrent après coup.
Si le verdict d'une image déjà en service bascule au bloquant, **rien n'est coupé** : tu es
prévenu, ton application continue de tourner, et c'est la prochaine mise en service qui
demandera une image corrigée. `scanStatus` dans `GET /api/apps` porte le verdict de la
version en service.

## Mettre en service

```http
POST /api/apps/{slug}/deploy
```

```json
{ "tag": "2026-09-18" }
```

Le nœud remplace le conteneur par celui de cette version et attend qu'il tienne debout.
La route ne rend la main qu'une fois l'application repartie, ou en erreur si elle ne
démarre pas.

Revenir en arrière, c'est redéployer un `tag` précédent : les versions restent.

L'analyse de l'image est lue avant que le nœud soit touché :

| Verdict | Ce que fait la route |
| --- | --- |
| `PASSED`, `WARNED` | La mise en service suit son cours. |
| `PENDING` | La route attend le verdict jusqu'à trente secondes, puis répond `422` en disant que l'analyse court encore. Réessaie. |
| `BLOCKED` | `422`, avec la CVE, le paquet et la version qui corrige. Rien n'est réservé, rien n'est touché sur le nœud. |
| `FAILED` | La mise en service passe. Une trace part au journal interne : une analyse en panne ne doit pas arrêter la plateforme. |

Un `BLOCKED` se lève au cas par cas, côté Arkya, sur une version précise et avec un motif
écrit. Écris au support si tu penses qu'une faille bloquante ne devrait pas l'être :
l'exception se pose sur la version, jamais sur l'application.

## Environnement et journaux

```http
PUT /api/apps/{slug}/environment
GET /api/apps/{slug}/logs
```

```json
{ "environment": { "NODE_ENV": "production", "DATABASE_URL": "${crm-db.URL}" } }
```

L'envoi **remplace** tout l'environnement : renvoie les clés à garder, ou elles
disparaissent. Quatre règles, toutes vérifiées avant écriture :

- cent variables au maximum ;
- un nom s'écrit en lettres, chiffres et tirets bas, et ne commence pas par un chiffre ;
- `PORT` est réservée — Arkya la pose depuis le port publié, la définir est refusé ;
- une référence `${alias.CHAMP}` qui ne désigne aucun voisin du projet est refusée, plutôt
  que résolue en chaîne vide au démarrage.

L'environnement d'une application accepte les [références entre
services](/api/variables#les-références-entre-services), `${crm-db.URL}` compris.

<Note>
Une fois créée, une application est un service comme un autre : toutes les routes de
[`/api/resources`](/api/services) s'y appliquent — taille, réseau, fichiers,
sauvegardes. Ces routes-ci ne portent que ce qui est propre au registre.
</Note>
