loghubident

package module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: MIT Imports: 14 Imported by: 0

README

lib-loghub-ident

Biblioteca Go que resolve e expõe a identidade de um software dentro do ecossistema Loghub. Preenche seis campos — DataDir, MachineID, AgentName, AgentUUID, Hostname, Workspace — a partir de variáveis de ambiente, arquivos e fallbacks do sistema. Projetada para microserviços em containers.

  • Leve e rápida: após Initialize, cada getter é apenas a leitura de uma variável de pacote — sem locks, adequado a centenas de threads chamando milhares de vezes por milissegundo.
  • Imutável após Initialize: os valores são escritos uma única vez no boot e nunca mais modificados.
  • Multiplataforma: UUIDv7 gerado localmente via go-loghub-uuid, sem dependência de /proc.

A especificação técnica completa está modularizada em docs/00-INDEX.md (com sumário em SPEC.md). Para um guia prático de integração e receitas para novos serviços, consulte a skill/.

Instalação

go get github.com/patrickbrandao/go-loghub-ident

Requer Go 1.22+.

Uso

Arquivo: go.mod

module ident-test

go 1.22

require github.com/patrickbrandao/go-loghub-ident v0.4.0

Arquivo: main.go

// Defina LOGHUB_IDENT_DEBUG=1 para ver a origem de cada valor em stderr.
package main

import (
    "fmt"

    lhident "github.com/patrickbrandao/go-loghub-ident"
)

func main() {
    lhident.Initialize() // encerra o processo se algo falhar

    fmt.Printf("DataDir:   %s\n", lhident.DataDir())
    fmt.Printf("MachineID: %s\n", lhident.MachineID())
    fmt.Printf("AgentName: %s\n", lhident.AgentName())
    fmt.Printf("AgentUUID: %s\n", lhident.AgentUUID())
    fmt.Printf("Hostname:  %s\n", lhident.Hostname())
    fmt.Printf("Workspace: %s\n", lhident.Workspace())
}

Compilar:

go mod tidy && go build -o ident-test .

Compilar (multiplataforma):

# Windows
GOOS=windows GOARCH=amd64 go build -o ident-test.exe .

# macOS
GOOS=darwin GOARCH=amd64 go build -o ident-test .

# Linux (outros processadores)
GOOS=linux GOARCH=arm64 go build -o ident-test .

Rodar:

./ident-test;
    # DataDir:   /data
    # MachineID: 0123456789abcdef0123456789abcdef
    # AgentName: ident-test
    # AgentUUID: 018f3a5b-7c8d-7e9f-8a1b-2c3d4e5f6a7b
    # Hostname:  node01.example.com
    # Workspace: default
Regras de uso
  • Chame Initialize() uma única vez, no início de main(), antes de criar qualquer goroutine que vá ler os getters. Esse contrato é o que torna a leitura concorrente segura sem sincronização.
  • Uma segunda chamada a Initialize() encerra o processo com código 112.
  • Antes de Initialize(), todos os getters devolvem "" (zero value). Use IsInitialized() para validar em runtime se a inicialização já ocorreu.

API

Função Retorno Descrição
Initialize() — resolve a identidade; encerra em falha
IsInitialized() bool informa se Initialize concluiu com sucesso
DataDir() string diretório de dados
MachineID() string id da máquina (32 hex, sem hífen)
AgentName() string nome do agente
AgentUUID() string UUIDv7 canônico (com hífens)
Hostname() string hostname
Workspace() string workspace/tenant

Resolução de cada campo

Precedência geral: variável de ambiente → arquivo de disco → fallback. Para cada fonte, env e arquivo igualmente: ler 1ª linha → remover BOM → trim de espaços e caracteres de controle → lowercase → sanear → validar. Uma fonte ausente cai para a próxima; uma env presente mas inválida encerra o processo.

As regexes abaixo descrevem o conjunto de caracteres aceito, mas não são compiladas: a validação usa verificadores manuais equivalentes, o que mantém o pacote regexp fora da árvore de dependências. Além do conjunto de caracteres, valem as regras que uma classe não expressa:

  • AGENT_NAME e WORKSPACE: no máximo 64 caracteres, e . e .. são recusados (evita travessia de diretório em quem usa o valor para compor um caminho).
  • HOSTNAME: regras de rótulo da RFC 1123 — no máximo 253 caracteres, rótulos de 1 a 63, sem rótulo vazio e sem hífen nas bordas.
  • Qualquer arquivo-fonte é lido com teto de 4 KiB e precisa ser um arquivo comum: um FIFO ou um /dev/zero apontado por engano vira "fonte inválida" em vez de travar ou consumir toda a memória do nó.
Campo Env Fontes de fallback Regex
DATADIR DATADIR /data (existência validada de forma preguiçosa) caminho absoluto
MACHINE_ID MACHINE_ID $MACHINE_ID_FILE (def. /etc/machine-id) → $DATADIR/machine_id → gerado ^[0-9a-f]{32}$
AGENT_NAME AGENT_NAME $DATADIR/agent_name → argv[0] (base, sem .exe) ^[a-z0-9._-]+$
AGENT_UUID AGENT_UUID $DATADIR/agent_uuid → gerado (UUIDv7) UUIDv7 canônico
HOSTNAME HOSTNAME os.Hostname() ^[a-z0-9.-]+$
WORKSPACE WORKSPACE $DATADIR/workspace → default ^[a-z0-9.-]+$
Persistência
  • Apenas identidades geradas aleatoriamente (machine_id, agent_uuid) são gravadas em $DATADIR, para que o mesmo valor seja reobtido em execuções futuras. Valores determinísticos (argv[0], default) nunca gravam arquivo.
  • A permissão 0644 é garantida, não um teto: a biblioteca aplica um chmod explícito, então um umask restritivo do container não deixa os arquivos 0600 e ilegíveis para um sidecar com outro UID.
  • A gravação é durável (fsync) e atômica: a criação usa exclusão mútua do filesystem e a regeneração usa arquivo temporário + rename. Um crash no meio da escrita não deixa arquivo truncado, e processos irmãos que sobem ao mesmo tempo sobre o mesmo volume convergem para uma identidade única — quem perde a corrida adota o valor de quem chegou primeiro.
  • Isso vale para os dois cenários: arquivo ainda inexistente (arranque a frio) e arquivo existente porém corrompido (recuperação depois de uma queda). No segundo, a disputa é arbitrada por um registro de regeneração $DATADIR/.machine_id.regen / $DATADIR/.agent_uuid.regen — arquivos ocultos que só aparecem se uma identidade corrompida tiver sido substituída, e que a biblioteca preserva de propósito: apagá-los devolveria o nome à disputa. Um registro obsoleto é removido sozinho quando a identidade é criada do zero.
  • $DATADIR precisa ser um caminho absoluto; um caminho relativo aborta com 100. Do contrário a identidade dependeria do diretório de trabalho do processo.
  • A biblioteca não cria o DATADIR; o container/orquestrador deve montar o volume. No caminho feliz (todas as envs presentes), $DATADIR nunca é tocado — compatível com filesystem read-only. Se alguma env faltar, o $DATADIR é apenas lido: um diretório ausente é tratado como fonte ausente e a cadeia segue para o fallback. Ele só é obrigatório quando há identidade a gerar e persistir (machine_id, agent_uuid). Erros de permissão ou I/O ao verificar ou ler o diretório abortam com código 100.
  • Segurança e symlinks: arquivos de identidade dentro de $DATADIR são lidos sem seguir links simbólicos (O_NOFOLLOW). Um link simbólico é recusado, impedindo exfiltração de arquivos sensíveis do host ou container. Além disso, avisos de erro nunca expõem o conteúdo bruto de arquivos inválidos.
  • Atenção ao compartilhamento de volume: réplicas ou containers independentes NUNCA devem compartilhar o mesmo volume montado em $DATADIR para escrita. Processos que compartilham o $DATADIR convergem para a mesma identidade (machine_id e agent_uuid), gerando colisão de nós no servidor Loghub. O compartilhamento de volume gravável só é aceitável entre processos do mesmo nó (ex.: sidecars de um mesmo pod) que intencionalmente compartilham a identidade.

Diagnóstico

Defina a env LOGHUB_IDENT_DEBUG (qualquer valor não vazio) para que Initialize() escreva em stderr uma linha por campo — os seis, DATADIR inclusive — com a origem do valor (env / file / generated / fallback) e o valor final:

lib-loghub-ident: debug: DATADIR: env = "/data"
lib-loghub-ident: debug: MACHINE_ID: file /etc/machine-id = "0123456789abcdef0123456789abcdef"
lib-loghub-ident: debug: AGENT_NAME: fallback argv[0] = "ident-test"
lib-loghub-ident: debug: AGENT_UUID: generated = "018f3a5b-7c8d-7e9f-8a1b-2c3d4e5f6a7b"
lib-loghub-ident: debug: HOSTNAME: os.Hostname = "node01.example.com"
lib-loghub-ident: debug: WORKSPACE: fallback = "default"

As linhas saem antes do tratamento de uma eventual falha: no caminho de erro elas são a única pista sobre qual fonte alimentou cada campo já resolvido.

Avisos

Independentemente do modo debug, a biblioteca escreve em stderr um aviso sempre que descarta uma identidade persistida — um $DATADIR/machine_id ou $DATADIR/agent_uuid com conteúdo inválido, que a regra de regeneração manda substituir:

lib-loghub-ident: aviso: MACHINE_ID: /data/machine_id tinha conteúdo inválido (16 bytes, hash ...) e será REGERADO; a identidade desta máquina muda a partir de agora

Sem esse aviso, um agente voltaria com outro machine_id depois de um crash e apareceria no servidor Loghub como uma máquina nova, sem rastro para correlacionar.

Códigos de saída

Em qualquer falha, escreve lib-loghub-ident: <VARIÁVEL>: <motivo> em stderr e chama os.Exit(<código>).

Código Variável Motivo
100 DATADIR diretório necessário ausente, não é diretório, caminho relativo ou com controle, erro de I/O, ou symlink recusado
100 MACHINE_ID_FILE caminho informado está inacessível, relativo ou com controle, ou não é arquivo comum utilizável (diretório, fifo, device, > 4 KiB)
102 MACHINE_ID env não casa com ^[0-9a-f]{32}$
103 AGENT_NAME todas as fontes vazias (argv[0] saneou para vazio/./separador)
104 AGENT_NAME valor não casa com ^[a-z0-9._-]+$
105 AGENT_UUID falha na geração local de UUIDv7
106 AGENT_UUID gravação em $DATADIR/agent_uuid falhou
107 AGENT_UUID valor não casa com a regex de UUIDv7
108 HOSTNAME env vazia e os.Hostname() retornou erro
109 HOSTNAME valor não casa com ^[a-z0-9.-]+$
111 WORKSPACE valor não casa com ^[a-z0-9.-]+$
112 (geral) Initialize() chamado mais de uma vez
113 MACHINE_ID gravação em $DATADIR/machine_id falhou
114 MACHINE_ID falha na geração local do UUIDv7 base do machine-id

Em relação ao rascunho original: removidos os códigos 101 (machine-id é sempre regenerável) e 110 (workspace tem fallback default); adicionados o 113 (falha de gravação do machine_id) e o 114 (falha de geração do machine_id — o 105 é exclusivo do AGENT_UUID).

A linha de erro é a última que a biblioteca escreve; avisos e, em modo debug, linhas de diagnóstico podem precedê-la.

Exemplo

Veja skill/examples/minimal para um programa executável.

Licença

Ver LICENSE.

Documentation

Overview

Package loghubident resolve e expõe a identidade de um software dentro do ecossistema Loghub. É carregado uma única vez no boot do processo (Initialize) e disponibiliza seis valores imutáveis por meio de getters de pacote.

Os seis campos resolvidos são:

  • DataDir — diretório de dados persistente (env DATADIR ou /data)
  • MachineID — identificador da máquina (env MACHINE_ID, arquivo configurável via MACHINE_ID_FILE, $DATADIR/machine_id, ou gerado)
  • AgentName — nome do agente (env AGENT_NAME, $DATADIR/agent_name ou argv[0])
  • AgentUUID — UUIDv7 persistente (env AGENT_UUID, $DATADIR/agent_uuid ou gerado)
  • Hostname — hostname (env HOSTNAME ou os.Hostname)
  • Workspace — workspace/tenant (env WORKSPACE, $DATADIR/workspace ou "default")

Precedência geral de cada campo: variável de ambiente → arquivo de disco → fallback do sistema. Uma fonte ausente cai para a próxima; uma env presente mas inválida encerra o processo com o código do campo. Todos os valores são saneados (1ª linha, sem BOM, sem espaços nem caracteres de controle nas bordas) e normalizados para lowercase antes da validação — env e arquivo passam pelo mesmo pipeline.

As identidades geradas (MachineID e AgentUUID) são persistidas em $DATADIR com permissão 0644 garantida, gravação durável e atômica: processos que sobem ao mesmo tempo sobre o mesmo volume convergem para uma identidade única, e um crash no meio da escrita não deixa arquivo truncado. Descartar uma identidade persistida inválida sempre gera aviso em stderr.

O UUIDv7 é gerado localmente via github.com/patrickbrandao/go-loghub-uuid, sem dependência de /proc, sendo portanto multiplataforma.

Uso rápido

package main

import (
    "fmt"

    lhident "github.com/patrickbrandao/go-loghub-ident"
)

func main() {
    lhident.Initialize() // encerra o processo se algo falhar
    fmt.Println("agent:", lhident.AgentName(), lhident.AgentUUID())
}

Defina a variável de ambiente LOGHUB_IDENT_DEBUG para que Initialize escreva em stderr a origem e o valor final de cada um dos seis campos resolvidos.

Index

Constants

View Source
const DefaultDataDir = "/data"

DefaultDataDir é o caminho padrão do diretório de dados quando a variável de ambiente DATADIR está ausente ou vazia.

View Source
const DefaultMachineIDFile = "/etc/machine-id"

DefaultMachineIDFile é o caminho padrão do arquivo de machine-id quando a variável de ambiente MACHINE_ID_FILE está ausente ou vazia.

View Source
const DefaultWorkspace = "default"

DefaultWorkspace é o valor usado para WORKSPACE quando nenhuma fonte fornece um valor.

View Source
const EnvDebug = "LOGHUB_IDENT_DEBUG"

EnvDebug é a variável de ambiente que, quando definida (valor não vazio), ativa o log de diagnóstico em stderr durante Initialize.

Variables

This section is empty.

Functions

func AgentName added in v0.2.0

func AgentName() string

AgentName retorna o nome do agente.

func AgentUUID added in v0.2.0

func AgentUUID() string

AgentUUID retorna o UUIDv7 canônico do agente (com hífens).

func DataDir added in v0.2.0

func DataDir() string

DataDir retorna o diretório de dados resolvido.

func Hostname added in v0.2.0

func Hostname() string

Hostname retorna o hostname resolvido.

func Initialize

func Initialize()

Initialize resolve os seis campos de identidade a partir de variáveis de ambiente, arquivos e fallbacks do sistema, e os disponibiliza pelos getters de pacote (DataDir, MachineID, AgentName, AgentUUID, Hostname, Workspace).

Deve ser chamada UMA ÚNICA VEZ, no início de main(), ANTES de criar qualquer goroutine que vá ler os getters. Esse contrato estabelece a relação happens-before que torna a leitura posterior segura sem sincronização.

Em qualquer falha de resolução, escreve em stderr no formato

lib-loghub-ident: <VARIÁVEL>: <motivo>

e encerra o processo com o código correspondente (ver SPEC.md / README). A segunda chamada encerra com código 112.

Eventos operacionais — hoje, o descarte de uma identidade persistida que estava corrompida — são escritos em stderr SEMPRE, com o prefixo "lib-loghub-ident: aviso: ", independentemente do modo debug.

Se a variável de ambiente LOGHUB_IDENT_DEBUG estiver definida, escreve em stderr uma linha por campo indicando a origem e o valor final resolvido.

func IsInitialized added in v0.3.1

func IsInitialized() bool

IsInitialized informa se Initialize já concluiu com sucesso. Getters chamados antes disso devolvem "" (zero value) e não devem ser usados — ver SPEC §3.

func MachineID added in v0.2.0

func MachineID() string

MachineID retorna o identificador da máquina (32 hex, sem hífen).

func Workspace added in v0.2.0

func Workspace() string

Workspace retorna o workspace (tenant) resolvido.

Types

This section is empty.

Directories

Path Synopsis
Pacote tests contém a suíte de testes de integração da biblioteca go-loghub-ident: exercita a API pública (Initialize e os seis getters) contra o sistema operacional REAL, sem o fakeSystem.
Pacote tests contém a suíte de testes de integração da biblioteca go-loghub-ident: exercita a API pública (Initialize e os seis getters) contra o sistema operacional REAL, sem o fakeSystem.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL