---
title: "Instalação do Servidor"
description: "Como instalar o Servidor do TrustBridge, distribuído como imagem de container Linux, com Docker no Linux e exatamente uma instância, usando o pacote de implantação."
canonical: "https://docs.lacunasoftware.com/articles/trustbridge/server-installation"
locale: "pt-BR"
product: "TrustBridge"
last_updated: "2026-10-12"
---

# Instalação do Servidor

O Servidor do TrustBridge é distribuído como uma imagem de container Linux,
`trustbridge-server:<version>`, com duas formas de hospedagem suportadas. As duas executam
**exatamente uma instância**.

| | Docker no Linux (esta página) | [Azure App Service](https://docs.lacunasoftware.com/articles/trustbridge/azure) |
|---|---|---|
| Banco de dados | SQL Server, ao lado do Servidor ou uma instância que você já opera | Azure SQL, com identidade gerenciada e sem senha |
| TLS | O Servidor termina o próprio | A plataforma o termina |
| Cofre de Certificados | Disponível | Disponível |
| Quem programa um reinício | Você | A plataforma, para a própria manutenção |

A Lacuna Software fornece um **pacote de implantação** com a referência da imagem, o arquivo compose
(`docker-compose.yml`), um exemplo de ambiente comentado (`.env.example`) e os modelos de Diretiva de
Grupo para a frota. As instruções abaixo supõem que você tem esse pacote no host de destino.

## Antes de escolher a forma de hospedagem

Quatro coisas valem para toda implantação e determinam como você a verifica.

**Uma instância é uma regra, não uma recomendação.** As estações de trabalho mantêm uma conexão
SignalR persistente e não há backplane, então uma segunda instância não sabe nada das estações de
trabalho conectadas à primeira. A falha é silenciosa: as sessões falham contra máquinas que estão
conectadas e invisíveis. Nunca escale horizontalmente.

**Toda implantação precisa de um Protetor do Chaveiro.** Os dois chaveiros (key rings) do Servidor, o
que está por trás dos cookies do console administrativo e o que está por trás do Cofre de
Certificados, são linhas no próprio banco de dados dele, seladas sob um único certificado X.509 que
você fornece como configuração (`KeyRingProtector:Certificate`). O Servidor se recusa a iniciar sem
ele, com ou sem cofre. Faça backup do PKCS#12 permanentemente, em algum lugar que a plataforma não
consiga acessar. Veja [Segurança](https://docs.lacunasoftware.com/articles/trustbridge/security#o-protetor-do-chaveiro).

**O Servidor atualiza o próprio esquema.** A cada inicialização, antes de começar a escutar, ele
aplica ao banco de dados as migrações pendentes (`Database:MigrateOnStart`, padrão `true`). Uma
implantação é uma imagem e uma connection string; uma atualização é uma nova tag de imagem. O custo
disso é uma permissão permanente de DDL (`db_ddladmin`) para a identidade com que o Servidor se
conecta, ou o interruptor definido como `false` e um DBA aplicando as migrações antes.

| O banco de dados está | O que o Servidor faz |
|---|---|
| Atualizado | Inicia. Nenhum DDL é executado. |
| Atrasado | Aplica as migrações que faltam, registra os nomes delas no log e então inicia. |
| Atrasado, com `MigrateOnStart=false` | **Recusa-se a iniciar**, citando as migrações e o comando a executar. |
| À frente deste build (um rollback que atravessa uma migração) | **Recusa-se a iniciar.** Código antigo sobre um esquema mais novo responderia errado em vez de falhar. |
| Inacessível | Aguarda cerca de noventa segundos e então **se recusa a iniciar**. |

Cada comando de migração tem um limite próprio de trinta minutos; nada limita uma atualização como um
todo.

**"Iniciou" não é "funciona".** Um Servidor em execução responde `GET /` com 200 mesmo com o banco de
dados inacessível, de propósito, para que uma instabilidade do banco não seja uma indisponibilidade
que alguém precise encerrar. A verificação que toca o banco de dados é `GET /healthz`: ela faz uma ida
e volta ao SQL e responde `Healthy` (200) ou `Unhealthy` (503), não exige credencial e não grava
nenhuma linha na Trilha de Auditoria. Aponte o seu monitoramento para ela.

## Pré-requisitos

- Um host Linux com Docker Engine e o plugin Compose.
- **Um certificado TLS em PKCS#12 cuja cadeia todas as estações de trabalho já considerem
  confiável.** O Workstation Service recusa uma cadeia não confiável, e o sintoma são estações de
  trabalho que nunca aparecem, sem nenhum erro no Servidor. Um certificado autoassinado não serve. Use
  a sua AC interna ou uma pública.
- **Um Protetor do Chaveiro**, um segundo PKCS#12, bem diferente do primeiro. O passo 3 abaixo o cria.
- Credenciais do **Lacuna Signer**: o endereço base e a chave de API no formato `application|secret`
  que o Signer publica. As duas ou nenhuma. Um endereço sem chave impede a inicialização.
- **A licença do Lacuna PKI SDK**, somente se você for ligar o Cofre de Certificados. A imagem não traz
  licença.
- Um SQL Server. O arquivo compose pode executar um ao lado do Servidor, mas leia a observação sobre a
  edição dele.

## 1. O diretório do host

Um diretório, que o Servidor apenas lê, como uid **1654**:

```bash
sudo mkdir -p /etc/trustbridge/tls /opt/trustbridge
```

O Servidor não grava nada no seu disco. O único diretório em que ele grava é `/tmp`, dentro do
container, com os caches do PKI SDK de emissores e LCRs baixados (`/tmp/trustbridge-pki`) e o próprio
arquivo de log rotativo (`/tmp/trustbridge-logs`). Os dois são caches de algo que já está registrado
em outro lugar, e perdê-los não custa nada além de latência e das linhas da tela **Log do Servidor**.
Nada ali precisa de backup.

## 2. TLS

Não há nada na frente deste Servidor, então ele termina o próprio TLS:

```bash
sudo cp your-certificate.pfx /etc/trustbridge/tls/server.pfx
sudo chown 1654:1654 /etc/trustbridge/tls/server.pfx
sudo chmod 400 /etc/trustbridge/tls/server.pfx
```

O container escuta na **8443**; o host publica a **443**. Um processo sem privilégios não consegue
abrir a porta 443, então o `dockerd` a abre e encaminha. Esse mapeamento já está no arquivo compose.

Se preferir terminar o TLS em um proxy reverso que você já opera, aponte-o para a porta 8443 do
container e remova as variáveis `Kestrel__Certificates__*` do arquivo compose, mas então defina também
`ASPNETCORE_FORWARDEDHEADERS_ENABLED=true`, ou toda linha de auditoria vai registrar o endereço do seu
proxy como o de quem chamou.

## 3. Configuração

```bash
cd /opt/trustbridge
cp <package>/docker-compose.yml .
cp <package>/.env.example .env
chmod 600 .env
```

O arquivo compose deve ficar no controle de versão e não contém nenhum segredo; o `.env` contém todos
os segredos e não pode ser versionado. A página [Configuração do Servidor](https://docs.lacunasoftware.com/articles/trustbridge/configuration) lista
todas as chaves que o Servidor aceita; as que esta forma de hospedagem exige estão abaixo.

| Variável do `.env` | O que é |
|---|---|
| `TRUSTBRIDGE_IMAGE` | A imagem, com uma **tag específica**. Nunca `latest`. |
| `MSSQL_SA_PASSWORD` | A senha do `sa` do SQL Server incluído (8 ou mais caracteres de três classes de caracteres). |
| `TLS_CERT_PASSWORD` | A senha de `/etc/trustbridge/tls/server.pfx`. |
| `KEY_RING_PROTECTOR_CERTIFICATE` | O Protetor do Chaveiro, uma linha de base64. **Obrigatório.** |
| `KEY_RING_PROTECTOR_PASSWORD` | Somente se esse PKCS#12 tiver senha. |
| `VAULT_ENABLED` | `false`, a menos que este Servidor vá guardar Certificados do Cofre. |
| `PKI_LICENSE` | A licença do Lacuna PKI SDK, em base64 em uma linha. Necessária com o cofre ligado. |
| `SIGNER_BASEADDRESS`, `SIGNER_APIKEY` | O Lacuna Signer. As duas ou nenhuma. |
| `ADMIN_BOOTSTRAP_EMAIL`, `ADMIN_BOOTSTRAP_PASSWORD` | O primeiro administrador. Lidas somente enquanto a tabela de contas estiver vazia; remova-as depois da primeira entrada. |

### Usando um SQL Server que você já opera

Essa é a forma normal em produção. Exclua o serviço `sqlserver` do arquivo compose, remova o
`depends_on` e aponte `ConnectionStrings__TrustBridge` para a instância real:

```yaml
ConnectionStrings__TrustBridge: >-
  Server=sql.example.com,1433;Database=TrustBridge;User Id=trustbridge;
  Password=${DB_PASSWORD};Encrypt=True
```

A identidade precisa de `db_datareader`, `db_datawriter` e, para que o Servidor aplique as próprias
migrações, `db_ddladmin`. O banco de dados em si precisa existir. Aplicar uma migração nunca cria um
banco de dados, exceto na forma com banco incluído, em que o Servidor se conecta como `sa` e pode
criá-lo.

> **Cuidado**
>
> Se você mantiver o banco de dados incluído, note que o arquivo compose define `MSSQL_PID=Developer`,
> que a Microsoft licencia somente para desenvolvimento e teste, **não para produção**. Defina uma
> edição para a qual você tenha licença, ou use um SQL Server que a sua organização já opera e do qual
> já faz backup. Nada vai avisar você.

### O Protetor do Chaveiro

Crie um. Um certificado **RSA** autoassinado é o formato certo: nada nunca o valida como certificado,
então não há nada que uma AC precise atestar. Ele é um par de chaves com uma impressão digital
(thumbprint).

```bash
openssl req -x509 -newkey rsa:2048 -nodes -days 18250 \
  -keyout protector.key -out protector.crt \
  -subj "/CN=TrustBridge Key Ring Protector"
openssl pkcs12 -export -inkey protector.key -in protector.crt -out protector.pfx -passout pass:
base64 -w0 protector.pfx
```

- **RSA, não ECDSA.** O framework sela o material de chave por meio de `EncryptedXml`, que só usa RSA.
  Um certificado ECDSA é recusado na inicialização.
- **Cinquenta anos de validade.** As datas de um protetor nunca são verificadas, mas um protetor que
  você se sinta obrigado a substituir é um que, de qualquer forma, terá de guardar para sempre.
- **`base64 -w0`**: uma linha, sem cabeçalho PEM, que é o que a configuração lê.
- **Sem senha** é a escolha honesta: o único lugar para guardá-la seria o mesmo `.env` que contém os
  bytes que ela abre.

Cole o base64 no `.env` como `KEY_RING_PROTECTOR_CERTIFICATE`. Depois, coloque `protector.pfx` em
algum lugar que este host não consiga acessar e remova-o do host:

```bash
shred -u protector.key protector.pfx      # only after copying the .pfx somewhere safe
```

> **Perigo**
>
> Perder o protetor destrói o Cofre de Certificados, e nada o recupera. Nem a Lacuna, nem você. Não há
> custódia de recuperação (escrow), de propósito. É um arquivo pequeno que nunca muda: guarde-o onde a
> sua organização guarda aquilo que não pode recriar.

A rotação é aditiva. Veja [Operações](https://docs.lacunasoftware.com/articles/trustbridge/operations#rotação-do-protetor-do-chaveiro).

### Ligando o Cofre de Certificados

`VAULT_ENABLED=false` é o padrão e a forma mais comum: toda chave privada fica em uma estação de
trabalho. Defina `true` somente em um Servidor que vá guardar Certificados do Cofre, e defina
`PKI_LICENSE` junto. Leia antes [Segurança](https://docs.lacunasoftware.com/articles/trustbridge/security#o-cofre-de-certificados): com o recurso
ligado, a licença do PKI SDK passa a ser crítica, e um PKCS#12 a importar precisa estar no formato PFX
legado.

```bash
echo "PKI_LICENSE=$(base64 -w0 LacunaPkiLicense.config)" >> .env
```

## 4. O esquema

**Nada a fazer.** O Servidor cria e migra o próprio esquema na inicialização. Pule para o passo 5.

Há dois motivos para fazer isso você mesmo: o seu controle de mudanças não concede DDL ao Servidor em
execução, ou um DBA quer ler o SQL antes. Em qualquer um dos casos, defina
`Database__MigrateOnStart: "false"` no arquivo compose e, antes de cada inicialização em uma versão
nova, aplique as migrações com o .NET SDK e uma cópia do código na versão implantada, ou gere um
script idempotente e aplique-o com o `sqlcmd`:

```bash
dotnet tool restore
dotnet ef migrations script --idempotent --project src/TrustBridge.Server -o schema.sql
sqlcmd -S <host> -U <user> -P <password> -C -I -b -d TrustBridge -i schema.sql
```

O `-I` é obrigatório (`QUOTED_IDENTIFIER ON`; sem ele, o primeiro índice filtrado falha com
`Msg 1934`), e o `-b` também (sem ele, uma aplicação parcial informa sucesso). Se você executar
`dotnet ef database update` em vez disso, acrescente `Command Timeout=3600` à connection string: o
padrão da ferramenta é trinta segundos, menos do que leva a criação de um índice sobre uma tabela
`AuditEvents` grande.

Com o interruptor desligado, o Servidor **se recusa a iniciar** quando há uma migração pendente, em
vez de iniciar sem ela. Esse é o objetivo do interruptor.

## 5. Inicie o Servidor

```bash
cd /opt/trustbridge && docker compose up -d
docker compose logs -f server
```

Uma primeira inicialização saudável:

```text
info: TrustBridge.Server.Common.KeyRings.KeyRingPosture[0]
      Every key ring this Server keeps in its database is encrypted with the Key Ring Protector
      76766DC2741A8DA070CC47E3830D34023D9ED885, and 0 retired protector(s) are kept readable beside
      it. Losing a protector makes every key ever sealed under it — and so every Vault Certificate
      it wrapped — unrecoverable, so backing them all up, permanently, is part of running this
      Server (ADR-0024).
info: TrustBridge.Server.Common.Vault.VaultReadiness[0]
      The Certificate Vault is off on this Server, so it holds no certificates of its own. Every
      private key it signs with is on a workstation (ADR-0020).
info: TrustBridge.Server.Common.Admin.AdminAccountBootstrap[0]
      Created the first admin account admin@example.com in the Administrator role.
info: Now listening on: https://[::]:8443
info: Application started. Press Ctrl+C to shut down.
```

**A primeira linha é a que deve ser lida.** Ela informa a impressão digital do protetor que o seu
backup precisa conter e conta os protetores aposentados com que este Servidor ainda consegue ler. Se
você fez uma rotação e ela diz `0 retired protector(s)`, o protetor antigo não está configurado e
tudo o que foi selado sob ele está ilegível.

Com o cofre ligado, a segunda linha passa a ser *The Certificate Vault is open: its key ring holds N
key(s)…*. Um `warn` no lugar dela dizendo que o cofre está *closed until they have run* significa que
o Servidor subiu antes do banco de dados. Essa é a [trava do cofre](https://docs.lacunasoftware.com/articles/trustbridge/operations#a-trava-do-cofre).

## 6. Verifique se ele realmente funciona

```bash
curl -s https://trustbridge.example.com/healthz                                        # Healthy
curl -o /dev/null -w '%{http_code}\n' https://trustbridge.example.com/                 # 200 front door
curl -o /dev/null -w '%{http_code}\n' https://trustbridge.example.com/admin            # 302 to login
curl -o /dev/null -w '%{http_code}\n' https://trustbridge.example.com/api/v1/sessions  # 401 no API key
```

Em seguida, **entre em `/admin`** com a conta inicial. Isso lê o banco de dados pelo caminho que uma
requisição real percorre, e não pelo de um probe. Defina uma senha real, cadastre o segundo fator e
então remova `ADMIN_BOOTSTRAP_EMAIL` e `ADMIN_BOOTSTRAP_PASSWORD` do `.env` e recrie o container.
Elas não têm efeito depois da primeira inicialização; removê-las é uma questão de higiene.

Por fim, registre uma Aplicação Cliente em `/admin/applications`, emita uma chave de API e exercite a
[API REST](https://docs.lacunasoftware.com/articles/trustbridge/rest-api) uma vez, de ponta a ponta.

## Backups

| O quê | Como | Se você perdê-lo |
|---|---|---|
| O banco de dados | `BACKUP DATABASE`, ou os backups de SQL Server que você já faz | Sessões, Trilha de Auditoria, Registro de Certificados, **os dois chaveiros** e o texto cifrado do cofre |
| O `.pfx` do Protetor do Chaveiro | Copie o arquivo uma vez. Ele nunca muda | **Nenhum Certificado do Cofre pode mais ser aberto, permanentemente**, e todos os administradores entram de novo |
| O `.pfx` de cada protetor **aposentado** | Da mesma forma, guardado para sempre | Tudo o que foi selado sob aquele protetor, permanentemente |
| `/etc/trustbridge/tls` | Onde quer que os seus certificados fiquem | Emita de novo |
| **O log do container**, para onde quer que o seu logging driver o envie | A sua retenção de logs, não uma cópia de arquivo | Linhas de auditoria que o banco de dados não aceitou. A trilha tem um buraco exatamente onde o banco esteve fora do ar |

O logging driver `json-file` do arquivo compose guarda as linhas no diretório do próprio container, e
o Docker as apaga **junto com o container**; então `docker compose down` e a atualização descrita
abaixo as descartam. Aponte o driver de `logging:` para algo fora desta máquina, ou execute um agente
de log sobre os arquivos, antes de precisar deles.

Restaurar exige que duas coisas cheguem juntas. Um banco de dados sem o protetor dele impede a
inicialização; um protetor sem o banco de dados inteiro (linhas do cofre presentes, linhas do chaveiro
ausentes) é recusado pelo motivo simétrico.

## Atualizações

```bash
docker compose pull && docker compose up -d
```

A imagem nova aplica as migrações de que precisa ao subir e registra os nomes delas no log. Se você
definiu `Database__MigrateOnStart: "false"`, aplique as migrações **antes**; a imagem nova se recusa
a iniciar contra um esquema atrasado.

Conte com uma indisponibilidade visível de um ou dois minutos: uma instância significa que não há
rolling restart. As estações de trabalho se reconectam sozinhas, e as sessões vinculadas a estações
de trabalho sobrevivem intactas. As sessões vinculadas a um **Certificado do Cofre**, não; elas
terminam como **Interrompida** e os clientes criam novas.

Atualize **primeiro o Servidor, depois as estações de trabalho**, dentro de um mesmo ciclo de versão. Um
Servidor aceita o protocolo atual das estações de trabalho e o anterior; uma estação de trabalho à
frente do Servidor não consegue se conectar. Veja
[Operações](https://docs.lacunasoftware.com/articles/trustbridge/operations#ordem-de-atualização-primeiro-o-servidor-depois-a-frota).

Um banco de dados criado pelo Servidor **1.2.0 ou anterior** não pode ser atualizado: o histórico de
migrações foi consolidado em uma única linha de base depois disso, e um banco assim é recusado na
inicialização como um que tem migrações que este build não contém. Descarte-o e deixe o Servidor
criar um novo.

## Quando o Servidor não inicia

| O log diz | Causa |
|---|---|
| `KeyRingProtector:Certificate is not configured` | Não há protetor. Toda implantação precisa de um. |
| `… is not base64, so it cannot be a PKCS#12` | O valor tem um cabeçalho PEM ou quebras de linha. Use `base64 -w0`. |
| `… could not be opened as a PKCS#12` | `CertificatePassword` errada, ou os bytes não são um PKCS#12. O Servidor não consegue distinguir os dois casos. |
| `… carries no RSA private key this process can use` | Um certificado ECDSA, ou um exportado sem a chave. |
| `The Certificate Vault's key ring holds N key(s) and the Key Ring Protector configured here opens none of them` | Um protetor trocado. Recoloque o antigo, ou adicione-o em `RetiredCertificates`. |
| `The Certificate Vault holds N certificate(s) and its key ring is empty` | Uma restauração parcial. Restaure o banco de dados inteiro. |
| `Signer:ApiKey is required when Signer:BaseAddress is configured` | Signer configurado pela metade. Defina os dois ou nenhum. |
| `… is behind the build pointed at it by N migration(s)` | `MigrateOnStart` está desligado e o esquema precisa dessas migrações. |
| `… has N migration(s) applied that this build does not contain` | Uma imagem mais antiga contra um esquema mais novo, ou um banco de dados da 1.2.0 ou anterior. |
| `… could not be reached in 90 seconds` | O banco de dados nunca respondeu. Verifique a connection string e se o SQL Server está no ar. |
| `… could not apply N pending migration(s)` | Em geral, falta de permissão de DDL para a identidade que se conecta. |
| `… ran out of time applying N pending migration(s)` | Um comando de migração passou de trinta minutos. Nada ficou aplicado pela metade. |
| Inicia, mas as estações de trabalho nunca se conectam | As estações de trabalho não confiam na cadeia TLS. |

Duas linhas do framework parecem falhas do protetor e não são: `KeyRingProvider[48] An error
occurred while reading the key ring` e `XmlKeyManager[24] … Unable to retrieve the decryption key`.
Em um Servidor que iniciou sem erros, elas significam que o **banco de dados** está inacessível. As
recusas do próprio protetor sempre citam `KeyRingProtector:` e param o host.
