---
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: TLS ou proxy reverso, o Protetor do Chaveiro, a licença do PKI SDK, atualizações e recusas na inicialização."
canonical: "https://docs.lacunasoftware.com/articles/trustbridge/server-installation"
locale: "pt-BR"
product: "TrustBridge"
last_updated: "2026-10-13"
---

# 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, ou um proxy reverso que você informa o termina | 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. Os
exemplos usam o Servidor **1.13.0**, imagem `trustbridge-server:1.13.0`.

## Antes de escolher a forma de hospedagem

Cinco 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).

**Toda implantação precisa de uma licença do PKI SDK.** A licença do Lacuna PKI SDK
(`Certificates:Validation:License`) é a licença com que a frota inteira funciona: o Servidor entrega a
própria a toda máquina Admitida, e nada lê um certificado sem ela. Por isso o Servidor **se recusa a
iniciar sem uma**, guarde ele certificados próprios ou não. A imagem não traz licença. Uma licença
vencida não impede o Servidor de iniciar, mas nada na frota nem no cofre consegue assinar até que ela
seja renovada.

**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 | Tenta de novo por cerca de noventa segundos e então **se recusa a iniciar**, informando quanto tempo esperou. |

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**, para o Servidor ou para o proxy reverso na frente dele. 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.
- **A licença do Lacuna PKI SDK**, o `LacunaPkiLicense.config` emitido pela Lacuna. Obrigatória em
  toda implantação, com ou sem cofre.
- 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.
- Um SQL Server. O arquivo compose pode executar um ao lado do Servidor, mas leia a observação sobre a
  edição dele.
- Somente se você for ligar o
  [Acesso com Certificado](https://docs.lacunasoftware.com/articles/trustbridge/configuration#admincertificatesigninenabled) no console administrativo:
  uma **licença do Lacuna Web PKI** no formato de uma linha, citando o nome de host que as pessoas
  digitam para chegar ao console.

## 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, em três lugares:

| Diretório | O que contém |
|---|---|
| `/tmp/trustbridge-pki` | Os caches do PKI SDK de emissores e LCRs baixados, e o arquivo pelo qual a licença da PKI é entregue ao SDK |
| `/tmp/trustbridge-logs` | O próprio arquivo de log rotativo do Servidor, que a tela **Log do Servidor** lê |
| `/tmp/trustbridge-releases` | Uma cópia de cada Versão da Estação de Trabalho guardada na tela **Versões da Estação de Trabalho**, de onde são servidos os downloads da frota, e um upload enquanto o Servidor o lê |

Os três são cópias de algo guardado em outro lugar: os caches se preenchem de novo, toda linha de log
foi antes para o log do container, e cada cópia de uma versão é reconstruída a partir da linha dela no
banco de dados no próximo download. Nada ali precisa de backup. O terceiro é o que cresce: uma Versão
da Estação de Trabalho tem cerca de 100 MB, uma cópia por versão guardada. Se esse diretório não puder
ser gravado, ou se o `/tmp` estiver cheio, a tela Versões da Estação de Trabalho recusa todo upload,
citando o motivo, enquanto os downloads continuam sendo servidos a partir do banco de dados. Se você
montar um `tmpfs` sobre o `/tmp`, monte um volume em disco sobre o próprio
`/tmp/trustbridge-releases`: um `tmpfs` conta contra o limite de memória do container.

## 2. TLS

No arquivo compose padrão 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.

### Atrás de um proxy reverso que você já opera

Se preferir terminar o TLS em um proxy (nginx, HAProxy, um balanceador de carga), o Servidor fala HTTP
simples com ele:

1. **Remova as variáveis `Kestrel__Certificates__*`** do arquivo compose e defina
   `ASPNETCORE_URLS: http://+:8443`. O Kestrel configurado para servir HTTPS sem certificado não
   inicia.
2. **Publique a porta somente no loopback**: `127.0.0.1:8443:8443` no lugar de `443:8443`, que agora é
   do proxy. Assim, nada chega ao container pela porta publicada além do próprio host.
3. **Informe o proxy** em `ForwardedHeaders__TrustedProxies__0` (e `__1`, `__2` para mais entradas: a
   lista precisa de índice mesmo com uma entrada só). Use o endereço do qual o container vê o proxy se
   conectar, que nem sempre é o que você conhece:
   - **Um proxy no host do Docker** se conecta a partir do gateway da rede bridge
     (`docker network inspect`).
   - **Um proxy em outro container** na mesma rede do compose se conecta a partir de um endereço da
     sub-rede dessa rede. Listar a sub-rede confia em todos os containers dela.

   Sem isso, o Servidor toma o seu proxy por todos os chamadores: toda linha de auditoria registra o
   endereço do proxy, e todos os navegadores na página de login e todas as máquinas pedindo admissão
   compartilham um único limite por endereço. O log de inicialização diz o que o Servidor entendeu da
   lista, numa linha que começa com `Forwarded headers:`. Se as linhas de auditoria continuarem
   mostrando o endereço do proxy, a lista não contém o endereço do qual o proxy se conecta. As regras
   das entradas estão em
   [`ForwardedHeaders:TrustedProxies`](https://docs.lacunasoftware.com/articles/trustbridge/configuration#forwardedheaderstrustedproxies).

O proxy precisa enviar o endereço e o esquema de quem chamou, **repassar o header `Host`** (o Servidor
nunca lê `X-Forwarded-Host`, e o nginx envia o nome do upstream se não for instruído do contrário) e
**transportar WebSockets**. O canal das estações de trabalho e o console administrativo são conexões
WebSocket. Sem os headers de upgrade, os dois recorrem a long polling: o console continua funcionando,
só que mais lento, e a frota se conecta como uma sequência de requisições de polling, cada uma
verificada contra o banco de dados. No nginx, no nível `http`, uma vez:

```nginx
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
```

O nginx na borda, o único proxy na frente do Servidor, substitui o header de endereço pelo único
endereço que ele viu:

```nginx
proxy_http_version 1.1;
proxy_set_header Upgrade           $http_upgrade;
proxy_set_header Connection        $connection_upgrade;
proxy_set_header Host              $host;
proxy_set_header X-Forwarded-For   $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
```

O nginx atrás de um balanceador de carga que termina o TLS, com os dois listados em `TrustedProxies`,
mantém o que o balanceador escreveu: acrescenta ao endereço e repassa o esquema do balanceador, porque
o `$scheme` dele é o `http` que o balanceador usou para falar com ele.

```nginx
proxy_http_version 1.1;
proxy_set_header Upgrade           $http_upgrade;
proxy_set_header Connection        $connection_upgrade;
proxy_set_header Host              $host;
proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
```

Defina tudo isso num lugar só: o nginx deixa de herdar as linhas `proxy_set_header` de um bloco
externo assim que um `location` define uma própria.

**Deixe passar uma Versão da Estação de Trabalho.** Um upload para
`/admin/workstation-releases/upload` é uma única requisição de até 256 MB. Aumente o limite de corpo
do proxy para esse caminho: o `client_max_body_size` do nginx é 1 MB por padrão, o que recusa todas as
versões.

**Não use `ASPNETCORE_FORWARDEDHEADERS_ENABLED`.** Esse interruptor é para o App Service: ele confia em
quem se conectar, então qualquer cliente que consiga chegar à porta do container sem passar pelo proxy
poderia informar o próprio endereço. Fora do App Service, o Servidor registra um Warning na
inicialização quando ele está definido, e defini-lo junto com `TrustedProxies` impede a
inicialização. Se uma implantação anterior sua o definiu, remova-o e informe o proxy.

## 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**, como `trustbridge-server:1.13.0`. 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`. Desnecessária atrás de um proxy reverso. |
| `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. |
| `PKI_LICENSE` | A licença do Lacuna PKI SDK, em base64 em uma linha. **Obrigatória.** O arquivo compose não inicia o Servidor com ela vazia. |
| `VAULT_ENABLED` | `false`, a menos que este Servidor vá guardar Certificados do Cofre. |
| `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).

### A licença do PKI SDK

Toda implantação precisa dela, com ou sem cofre. Ela é uma configuração, não um arquivo, então vai no
`.env`, ao lado do Protetor do Chaveiro, como o `LacunaPkiLicense.config` inteiro codificado em base64
em uma linha:

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

**Ela vence, e uma licença vencida não impede o Servidor de iniciar.** O Servidor inicia, registra uma
linha `Critical` citando `Certificates:Validation:License`, e toda página do console administrativo
mostra um banner dizendo que nada na frota e nada no cofre consegue assinar até que ela seja renovada.
Renová-la é substituir a linha no `.env` e recriar o container, o que também entrega a licença nova a
cada máquina quando ela se reconecta. Veja
[Operações](https://docs.lacunasoftware.com/articles/trustbridge/operations#renovação-da-licença-do-pki-sdk).

### 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. Ele não precisa de
outra licença além da acima, mas é mais uma coisa que depende dela: com ela vencida, todo Certificado
do Cofre também aparece como `UNTRUSTED` e uma importação é recusada. Leia antes
[Segurança](https://docs.lacunasoftware.com/articles/trustbridge/security#o-cofre-de-certificados): um PKCS#12 a importar precisa estar no formato PFX
legado.

## 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).

Atrás de um proxy reverso, o Servidor escuta em `http://[::]:8443`, e uma linha que começa com
`Forwarded headers:` cita os proxies em que ele confia.

## 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.

A conta inicial entra com senha, então deixe `Admin:CertificateSignIn:Required` desligado na primeira
inicialização. Se você pretende desligar as senhas depois, crie antes um segundo Administrador: um
**CPF vinculado** é sempre definido por outro Administrador, nunca pelo dono da conta, e uma conta sem
um não consegue entrar quando as senhas estão desligadas. Veja
[`Admin:CertificateSignIn:Required`](https://docs.lacunasoftware.com/articles/trustbridge/configuration#admincertificatesigninrequired).

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, Versões da Estação de Trabalho, **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

**Faça antes um backup do banco de dados.** Esse backup é o seu caminho de volta (abaixo).

```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.

**Fazer rollback é restaurar esse backup, não trocar a tag.** Toda versão do Servidor desde a 1.9.0
traz migrações, e uma imagem mais antiga se recusa a iniciar contra um banco de dados que uma mais nova
migrou. Para voltar, restaure o backup anterior à atualização e implante a tag anterior contra ele.

Vindo do **Servidor 1.6.x**, defina `PKI_LICENSE` no `.env` antes de atualizar: desde a 1.7.0 toda
implantação precisa dela, e o arquivo compose não inicia o Servidor sem ela. Atrás de um proxy reverso
configurado com `ASPNETCORE_FORWARDEDHEADERS_ENABLED`, passe a usar `ForwardedHeaders:TrustedProxies`
como no [passo 2](https://docs.lacunasoftware.com/articles/trustbridge/server-installation#atrás-de-um-proxy-reverso-que-você-já-opera).

**Atualizar para a 1.13.0 move o Servidor e a frota juntos.** Um Servidor aceita o protocolo atual
das estações de trabalho e o anterior, e a 1.13.0 fala o protocolo 5, então a janela dela é 4–5:

| Uma máquina com | Contra o Servidor 1.13.0 |
|---|---|
| Estação de Trabalho 1.5.0 (protocolo 5) | Funciona. Ela não consegue se conectar a um Servidor anterior à 1.13.0. |
| Estação de Trabalho 1.4.x (protocolo 4) | Conecta-se, continua assinando para as sessões já vinculadas a ela e pode receber uma versão do Servidor, mas **nunca recebe uma sessão nova**. Um titular cujas máquinas estão todas na 1.4.x recebe `422 WORKSTATION_OFFLINE` até que uma delas seja atualizada. |
| Estação de Trabalho 1.3.0 ou anterior | **Recusada no handshake**, com **Atualização necessária**. Ela recebe a 1.5.0 manualmente ou por Diretiva de Grupo. |

Por isso, atualize o Servidor e, logo em seguida, faça upload de
`TrustBridge-Workstation-1.5.0-x64.msi` na tela **Versões da Estação de Trabalho** e ofereça-o à frota
na tela **Estações de Trabalho**. As máquinas na 1.4.x com atualizações ligadas o instalam sozinhas; as
mais antigas precisam receber o MSI por você. Veja
[Atualizar um Servidor e sua frota para o protocolo 5](https://docs.lacunasoftware.com/articles/trustbridge/configuration#atualizar-um-servidor-e-sua-frota-para-o-protocolo-5)
e [Atualizações entregues pelo Servidor](https://docs.lacunasoftware.com/articles/trustbridge/workstation-installation#atualizações-entregues-pelo-servidor).

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. |
| `Certificates:Validation:License is not configured` | Não há licença do PKI SDK. Toda implantação precisa de uma, com ou sem cofre. |
| `set PKI_LICENSE in .env` (do `docker compose`, antes de o container iniciar) | `PKI_LICENSE` está vazia no `.env`. A mesma mensagem cita `TLS_CERT_PASSWORD` ou `KEY_RING_PROTECTOR_CERTIFICATE` quando essas estão vazias. |
| `… 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. |
| `ForwardedHeaders:TrustedProxies:N is '…'` | Uma entrada que não é um endereço ou uma rede simples, que confia em todos os endereços (`/0`), que tem bits definidos além do prefixo, ou que é IPv4 mapeado ou link-local. Escreva o endereço do próprio proxy, como o Servidor o mostra. |
| `ForwardedHeaders:TrustedProxies is the one value '…'` | A lista informada como um valor único. Use índice: `ForwardedHeaders__TrustedProxies__0`. |
| `ForwardedHeaders:TrustedProxies is set, and so is ASPNETCORE_FORWARDEDHEADERS_ENABLED` | Os dois definidos. Fora do App Service, remova a variável. |
| `Admin:CertificateSignIn:Enabled is on, but Admin:CertificateSignIn:WebPkiLicense is not set` | O Acesso com Certificado precisa da licença do Web PKI, no formato de uma linha. |
| `Admin:CertificateSignIn:Required is on, which switches passwords off, but Admin:CertificateSignIn:Enabled is off` | Um console sem nenhuma forma de entrar. Ligue `Enabled`, ou desligue `Required`. |
| `Workstations:ClientTimeout of … is shorter than` / `is longer than` | Fora do intervalo de 10 segundos a 5 minutos. Escreva como `hh:mm:ss`: um `15` sozinho são quinze dias. |
| `Language:DefaultCulture` recusado | Um idioma padrão fora do conjunto suportado. |
| `… 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 (restaure o backup anterior à atualização), ou um banco de dados da 1.2.0 ou anterior. |
| `This Server's database did not answer in N 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. |
| `… database stopped answering while it applied N pending migration(s)` | A conexão caiu durante a migração. Verifique se o banco de dados está no ar e se nada o reiniciou. |
| Inicia, mas toda página do console mostra um banner de licença | A licença do PKI SDK venceu ou foi recusada. O Servidor funciona, e nada consegue assinar até que ela seja renovada. |
| Inicia, mas as estações de trabalho nunca se conectam | As estações de trabalho não confiam na cadeia TLS, ou as máquinas executam uma versão da Estação de Trabalho que este Servidor recusa (**Atualização necessária** nas linhas delas na frota). |

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.
