Reflang — Reference Language

Reflang é a linguagem de substituição declarativa usada nos templates do DeployAlly. Ela permite referenciar valores de diversos contextos (inputs do usuário, secrets, contexto do deploy, metadados do sistema), aplicar funções para gerar ou transformar valores, e fazer indireção via substituição aninhada.

Sintaxe básica: ${namespace.path}. Tudo entre ${...} é resolvido em tempo de deploy.


Namespaces

Cada namespace expõe um contexto diferente:

Namespace Contexto Quando disponível Exemplo
input Inputs declarados em inputs.{required,optional,advanced} Após o wizard ${input.WEB_HOSTNAME}
secrets Secrets resolvidos (via provider chain) Após secrets: ser processado ${secrets.DB_PASSWORD}
self Campos do próprio template (image, port, etc.) Sempre ${self.image}
instance Metadados da instance (uid, slug, namespace) Sempre ${instance.uid}
context Contexto do deploy (profile, dry_run, source) Sempre ${context.profile}
tenant Tenant ativo (multi-tenant deploys) Quando há tenant scope ${tenant.id}
system Atributos do host (memória, CPUs, hostname) Após preflight ${system.memory_mb}
env Variáveis de ambiente do host Sempre ${env.HOME}
needs Valores expostos por assets satisfeitos via needs Após auto-provision ${needs.main_db.host}
asset Asset compartilhado declarado por classe Após registry lookup ${asset.mysql.connection_string}

Como `needs` e `asset` se relacionam

  • needs referencia o asset conforme o consumer declarou na lista de necessidades. Útil quando o template declara várias opções (ex: needs: [{ class: mysql }, { class: postgresql }]) e você quer referenciar o que foi escolhido sem se importar com qual.
  • asset referencia um asset específico por classe, lookup direto no registry. Útil quando o template assume uma classe específica.

Funções

Funções podem aparecer em qualquer expressão Reflang. Argumentos podem ser strings literais, números ou outras expressões ${...}.

Geradores

Função Retorno Descrição
random.hex(N) string N caracteres hex (0-9a-f)
random.alnum(N) string N caracteres alfanuméricos (a-zA-Z0-9)
random.password(N) string N caracteres password-safe
random.uuid() string UUID v4

Exemplo:

secrets:
  API_TOKEN: ${random.hex(32)}
  ADMIN_PASSWORD: ${random.password(24)}
  INSTANCE_ID: ${random.uuid()}

Lógica e Controle

Função Retorno Descrição
if(cond, a, b) qualquer Retorna a se cond truthy, senão b
default(v, fallback) qualquer Retorna v se definido e não vazio, senão fallback
exists(v) bool true se a expressão resolve para valor não vazio

Exemplo:

environment:
  LOG_LEVEL: ${if(input.DEBUG, "debug", "info")}
  DB_HOST: ${default(input.DB_HOST, "localhost")}
  HAS_SMTP: ${exists(input.SMTP_HOST)}

Strings

Função Retorno Descrição
concat(...) string Concatena os argumentos
lower(s) string Lowercase
upper(s) string Uppercase
replace(s, from, to) string Substitui from por to em s

Exemplo:

environment:
  DB_NAME: ${concat("app_", lower(input.TENANT_SLUG))}
  SAFE_HOST: ${replace(input.HOST, ".", "_")}

Substituição Aninhada

Reflang resolve referências aninhadas de dentro pra fora. Útil quando o nome do campo depende de outro valor:

environment:
  # SLUG_UPPER = "PROD", então acessa input.PROD_DOMAIN
  TARGET_DOMAIN: ${input.${SLUG_UPPER}_DOMAIN}

O motor resolve ${SLUG_UPPER} primeiro (resultando em, por exemplo, PROD), depois acessa input.PROD_DOMAIN.


Provider Chain em Secrets

Secrets têm uma cadeia de providers tentada em ordem. O primeiro que retorna valor é usado.

secrets:
  DB_PASSWORD:
    provider: input        # tenta input.DB_PASSWORD
    fallback:
      provider: env        # se vazio, tenta env.DB_PASSWORD
      fallback:
        generate:          # último fallback: gera
          type: alnum
          length: 32

Forma simplificada (provider único + generate como fallback automático):

secrets:
  DB_PASSWORD:
    provider: input
    generate:
      type: hex
      length: 32

Tipos de provider:

Provider Origem
input Input do template (key implícito = nome do secret)
env Variável de ambiente do host
generate Gera novo valor usando função (hex, alnum, password, uuid)
asset Lê do asset declarado em needs

Quando o provider tem input ou env sem :KEY, o motor usa o próprio nome do secret como key.


Conditional Environment

Variáveis de ambiente podem ser geradas condicionalmente:

environment:
  - name: SMTP_HOST
    value: ${input.SMTP_HOST}
    when: ${exists(input.SMTP_HOST)}
  - name: SMTP_PORT
    value: ${default(input.SMTP_PORT, "587")}
    when: ${exists(input.SMTP_HOST)}

Variáveis cuja expressão when resulta falsa são omitidas do container — não viram strings vazias.


Exemplos Práticos

Template de Application com Banco

species: meu-app
archetype: application

image: meu-org/app:1.0

inputs:
  required:
    - WEB_HOSTNAME
    - ADMIN_EMAIL
  advanced:
    - DEBUG  # default: false

secrets:
  DB_PASSWORD:
    provider: input
    generate:
      type: alnum
      length: 32
  SESSION_KEY:
    generate:
      type: hex
      length: 64

needs:
  - class: mysql
    options:
      - { species: mysql, variant: mysql-8.4, default: true }
      - { species: mariadb, variant: latest }

environment:
  WEB_HOSTNAME: ${input.WEB_HOSTNAME}
  ADMIN_EMAIL: ${input.ADMIN_EMAIL}
  DB_HOST: ${needs.main_db.host}
  DB_USER: ${needs.main_db.user}
  DB_PASSWORD: ${secrets.DB_PASSWORD}
  DB_NAME: ${concat("app_", lower(instance.uid))}
  SESSION_KEY: ${secrets.SESSION_KEY}
  LOG_LEVEL: ${if(input.DEBUG, "debug", "info")}

routes:
  - hostname: ${input.WEB_HOSTNAME}
    port: 8080

Asset com Dispatcher per_instance

species: meu-db
archetype: asset

image: meu-org/db:latest

provides:
  asset_class: meu-db
  functions:
    create-database:
      params:
        - name: db_name
          required: true
        - name: db_user
          required: true
          default: ${concat("user_", lower(input.db_name))}
      exec:
        command: ["create-db", "${input.db_name}", "${input.db_user}"]

storage:
  - kind: volume
    name: data
    path: /var/lib/db

Config File via Storage

storage:
  - kind: config_file
    path: /etc/app/config.yaml
    content: |
      app:
        port: ${self.port}
        log_level: ${default(input.LOG_LEVEL, "info")}
      database:
        host: ${needs.main_db.host}
        password: ${secrets.DB_PASSWORD}

O motor renderiza o conteúdo (substituição Reflang aplicada), salva em /opt/deployally/<uid>/config/config.yaml e faz bind-mount single file no container.


Referência Rápida

Namespaces

Namespace Disponível em
input inputs do wizard
secrets secrets resolvidos
self campos do próprio template
instance metadados da instance
context contexto do deploy
tenant tenant ativo
system recursos do host
env env vars do host
needs assets via needs
asset asset por classe

Funções

random.hex(N)
random.alnum(N)
random.password(N)
random.uuid()
if(cond, a, b)
default(v, fallback)
exists(v)
concat(...)
lower(s)
upper(s)
replace(s, from, to)

Provider Chain (Secrets)

input → env → generate → asset

Primeiro provider com valor não vazio vence.


Conceitos Relacionados

  • Templates — visão geral do sistema de templates e dos seis archetypes
  • Taxonomia — kingdom / family / species
  • Manifestos — armazenamento local de configurações resolvidas
  • Ecologia — relações entre serviços e auto-provisão

Operação

By Borlot.com.br on 05/06/2026