Skip to content

Variable Resolution

Documentation du modèle de données et de l'algorithme de résolution des variables d'environnement.

Data Model

Variable

typescript
interface Variable {
  key: string       // Nom de la variable d'env (ex: DATABASE_URL)
  value: string     // Valeur (chiffrée si secret: true)
  secret: boolean   // Si true, chiffré AES-256-GCM sur disque
}

Les variables sont indexées par UUID dans l'environnement :

json
{
  "a1b2c3d4-...": { "key": "DATABASE_URL", "value": "postgres://...", "secret": false },
  "e5f6g7h8-...": { "key": "API_KEY",      "value": "<ciphertext>",  "secret": true }
}

L'UUID permet le renommage sans casser les références.

Environnement global

typescript
interface Environment {
  id: string
  name: string           // ex: "development"
  immutable?: boolean    // Si true, protégé contre la suppression
  variables: Record<string, Variable>  // variableId → Variable
}

Service-scoped environment

Un service peut avoir des overrides et attacher des environnements globaux via des références :

typescript
interface ServiceScopedEnv {
  reference: string     // nom de l'environnement global
  activateOn: string    // nom de l'environnement actif quand ce service-scoped env s'active
  overrides: Record<string, Variable>  // variableId → Variable override
}

Resolution Algorithm

resolveServiceVariables(service, activeEnvironmentName)

        ├─ 1. Trouver l'environnement global dont le nom = activeEnvironmentName
        │       variables_base = env_global.variables

        ├─ 2. Pour chaque service-scoped env du service :
        │       si activateOn == activeEnvironmentName :
        │           fusionner overrides par-dessus variables_base

        ├─ 3. Déchiffrer les valeurs secrètes

        └─ 4. Construire le Record<string, string> final
               { DATABASE_URL: "postgres://...", API_KEY: "..." }

Cascade Renaming

Quand un environnement global est renommé :

typescript
// environment-store.update()
if (existing.name !== input.name) {
  // Met à jour tous les services qui référencent cet environnement
  await serviceStore.renameByReference(existing.name, input.name)
  // Met à jour tous les services dont activateOn référence cet environnement
  await serviceStore.renameByActivateOn(existing.name, input.name)
}

Parser Resolution

resolveServiceVariables() résout aussi les parserIds en définitions complètes :

typescript
// Input dans services.json
{ parserIds: ["uuid-1", "uuid-2"] }

// Après résolution (champ transient, non persisté)
{ resolvedLogParsers: [
    { id: "uuid-1", name: "JSON Parser", code: "...", version: 2 },
    { id: "uuid-2", name: "Level extractor", code: "...", version: 2 },
] }

L'agent reçoit les définitions complètes pour compiler les parsers localement.

Secret Encryption

Les valeurs secret: true sont stockées au format :

<iv_hex>:<authTag_hex>:<ciphertext_hex>

Avec :

  • Algorithm : AES-256-GCM
  • Clé : 256 bits depuis data/.encryption.key
  • IV : 12 bytes aléatoires par chiffrement
  • Auth tag : 16 bytes GCM (intégrité garantie)
typescript
// Chiffrement
const iv = crypto.randomBytes(12)
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv)
const encrypted = Buffer.concat([cipher.update(value), cipher.final()])
const tag = cipher.getAuthTag()
return `${iv.hex}:${tag.hex}:${encrypted.hex}`

// Déchiffrement — throw sur échec (jamais de retour silencieux)
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv)
decipher.setAuthTag(tag)
return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString()

Released under the MIT License.