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()