---
title: "Configuração do Servidor"
description: "Todas as chaves que o Servidor do TrustBridge lê: o padrão, se um Administrador pode alterá-la em tempo de execução, se exige reinício e de onde ela vem."
canonical: "https://docs.lacunasoftware.com/articles/trustbridge/configuration"
locale: "pt-BR"
product: "TrustBridge"
last_updated: "2026-10-13"
---

# Configuração do Servidor

Todas as chaves que o Servidor lê, o padrão de cada uma, se um Administrador pode alterá-la em tempo
de execução e se uma alteração exige reinício. O Workstation Service e o Tray App são configurados à
parte: veja [Configuração das estações de trabalho](https://docs.lacunasoftware.com/articles/trustbridge/workstation-configuration).

## De onde vem um valor

Seis fontes, da menor para a maior precedência. Uma fonte posterior prevalece sobre uma anterior,
chave por chave:

| | Fonte | Onde existe |
|---|---|---|
| 1 | `appsettings.json` | Embutido na imagem. Não é um arquivo que a implantação edita. |
| 2 | `appsettings.{ASPNETCORE_ENVIRONMENT}.json` | O `appsettings.Development.json` está na imagem e fica inerte porque uma implantação não é o ambiente Development. **Nunca execute uma implantação como `Development`**: esse arquivo contém a licença de exemplo do PKI SDK e as credenciais de um Signer de teste compartilhado. |
| 3 | User secrets | Somente na máquina de um desenvolvedor. |
| 4 | Variáveis de ambiente | O container: o `environment:` do Compose ou os application settings do App Service. `__` substitui `:`. |
| 5 | Linha de comando | Nenhum dos dois cenários usa. |
| 6 | **Os overrides que um Administrador salvou** | Uma tabela no banco de dados da plataforma, lida na inicialização e relida sempre que uma configuração é salva na tela **Configurações e usuários**. |

**A fonte 6 é silenciosa e prevalece.** Se você define uma das sete chaves que aceitam override na
implantação e o valor não tem efeito, alguém salvou um valor na tela **Configurações e usuários**; o
seu valor é o padrão para o qual a chave volta quando o override é limpo.

| Chave que um Administrador pode sobrescrever | Padrão |
|---|---|
| `Sessions:DefaultDuration` | 1 h |
| `Sessions:MaxDuration` | 24 h |
| `Sessions:PinPromptTimeout` | 60 s |
| `Sessions:MaxPinFailures` | 5 |
| `Sessions:PinFailureWindow` | 15 min |
| `Signing:WorkstationReconnectGrace` | 15 s |
| `Audit:RetentionDays` | 365 |

Essas sete entram em vigor sem reinício. Uma linha salva com qualquer outra chave é ignorada, e não
aplicada. **Trate todas as outras chaves desta página como exigindo reinício.**

## Como escrever uma chave

`:` num arquivo, `__` no ambiente:

```json
{ "Sessions": { "MaxDuration": "12:00:00" } }
```

```yaml
environment:
  Sessions__MaxDuration: "12:00:00"
```

```bash
az webapp config appsettings set -g <rg> -n <app> --settings Sessions__MaxDuration=12:00:00
```

- Um único sublinhado não faz nada: `Sessions_MaxDuration` é uma variável que o Servidor nunca lê.
- **Uma lista é indexada**, e o índice faz parte da chave: `KeyRingProtector__RetiredCertificates__0__Certificate`.
- **Uma duração é `[d.]hh:mm:ss`, e as horas precisam ficar abaixo de 24.** `24:00:00` são vinte e
  quatro *dias*; um dia é `1.00:00:00`.
- Um tamanho é uma contagem simples de bytes. Um booleano é `true` ou `false`, em minúsculas.
- Uma categoria de log mantém os pontos: `Logging__LogLevel__TrustBridge.Server.Common.Vault`.

## Banco de dados e chaveiros

### `ConnectionStrings:TrustBridge`

**Padrão** uma string local com autenticação do Windows, só para desenvolvimento · **Override pelo Administrador** não · **Exige reinício** sim

O único banco de dados da plataforma: schema, Trilha de Auditoria, os dois chaveiros e os overrides do
Administrador. O padrão da imagem não funciona no Linux, então uma implantação que esquecer esta chave
tem a inicialização recusada depois de cerca de noventa segundos de tentativas.

```yaml
# Docker on Linux, SQL Server beside it
ConnectionStrings__TrustBridge: >-
  Server=sqlserver,1433;Database=TrustBridge;User Id=sa;
  Password=${MSSQL_SA_PASSWORD};Encrypt=True;TrustServerCertificate=True
```

```bash
# Azure App Service, managed identity
ConnectionStrings__TrustBridge="Server=tcp:<sqlserver>.database.windows.net,1433;Database=TrustBridge;Authentication=Active Directory Managed Identity;Encrypt=True"
```

### `Database:MigrateOnStart`

**Padrão** `true` · **Override pelo Administrador** não · **Exige reinício** sim

Se o Servidor aplica as migrações pendentes ao banco de dados na inicialização, antes de atender
qualquer coisa. `false` não significa "iniciar mesmo assim": uma migração pendente, nesse caso,
**recusa a inicialização**, citando as migrações e o comando a executar. Dois estados recusam a
inicialização qualquer que seja o valor: um banco de dados com migrações que este build não contém, e
um banco de dados que não pode ser acessado em cerca de noventa segundos. Aplicar migrações exige
`db_ddladmin`; um banco de dados atualizado não precisa de nenhum DDL.

Três limites de tempo são constantes, e não chaves: cerca de noventa segundos para o banco de dados
começar a responder, cinco minutos para a migração de outro processo terminar e **trinta minutos para
cada comando de uma migração**. No Azure App Service, o `WEBSITES_CONTAINER_START_TIME_LIMIT` (padrão
de 230 s) é atingido primeiro; aumente-o para 1800.

### `KeyRingProtector:Certificate`

**Padrão** não definido · **Override pelo Administrador** não · **Exige reinício** sim

**Obrigatória em toda implantação**, inclusive numa que não guarda certificados próprios. Um
**PKCS#12 codificado em base64, numa única linha e sem cabeçalho PEM**, com uma chave privada **RSA**.
Não é um caminho nem um thumbprint. Quatro recusas impedem o host de iniciar: não configurada, não é
base64, não pôde ser aberta (senha errada ou não é um PKCS#12) e nenhuma chave privada RSA utilizável.
Veja [Segurança](https://docs.lacunasoftware.com/articles/trustbridge/security#o-protetor-do-chaveiro).

```yaml
environment:
  KeyRingProtector__Certificate: ${KEY_RING_PROTECTOR_CERTIFICATE:?set KEY_RING_PROTECTOR_CERTIFICATE in .env}
```

```bash
# App Service: an app setting, or better, a Key Vault reference
KeyRingProtector__Certificate="@Microsoft.KeyVault(SecretUri=https://<vault>.vault.azure.net/secrets/<name>/)"
```

### `KeyRingProtector:CertificatePassword`

**Padrão** não definido · **Override pelo Administrador** não · **Exige reinício** sim

A senha que abre o protetor. Ausente significa que o PKCS#12 não precisa de senha, que é o padrão
honesto para um protetor fornecido como configuração.

### `KeyRingProtector:RetiredCertificates:{n}:Certificate` e `…:{n}:CertificatePassword`

**Padrão** vazio · **Override pelo Administrador** não · **Exige reinício** sim

Protetores que esta implantação já usou para selar, mantidos para que o material de chave selado com
um deles continue legível. **Só para decifrar**, e mantidos **permanentemente**: a rotação nunca
re-cifra as chaves armazenadas. É uma lista de objetos, porque cada PKCS#12 aposentado tem a própria
senha, pareada pelo índice. **Não coloque uma entrada vazia em templates**: uma entrada cujo
`Certificate` resulta numa string vazia recusa a inicialização.

```yaml
environment:
  KeyRingProtector__Certificate: <the new one>
  KeyRingProtector__RetiredCertificates__0__Certificate: <the one it replaced>
  KeyRingProtector__RetiredCertificates__0__CertificatePassword: <if that one had a password>
```

## Logs

### `Logging:LogLevel:{category}`

**Padrão** `Information`, com `Microsoft.AspNetCore` em `Warning` · **Override pelo Administrador** não · **Exige reinício** sim

Os níveis de log do framework, e o único lugar em que um nível é decidido: o console, o arquivo
rotativo e o Application Insights recebem exatamente as linhas que esses níveis deixam passar. Sete
categorias trazem as linhas que vale a pena reconhecer, todas em `Information` ou acima, então o
padrão já as mostra:

| Categoria | O que informa |
|---|---|
| `TrustBridge.Server.Common.Persistence.SchemaUpgrade` | Quais migrações foram aplicadas nesta inicialização, ou por que o schema foi recusado |
| `TrustBridge.Server.Common.Persistence.SchemaGate` | Que os overrides do Administrador não puderam ser relidos depois da etapa do schema |
| `TrustBridge.Server.Common.KeyRings.KeyRingPosture` | Qual protetor está selando agora, com o thumbprint e o número de aposentados |
| `TrustBridge.Server.Common.Vault.VaultReadiness` | Se o cofre está desligado, aberto ou travado fechado |
| `TrustBridge.Server.Common.Audit.AuditLog` | Uma linha de auditoria que não conseguiu chegar ao banco de dados, em `Critical`, com a própria linha |
| `TrustBridge.Server.Common.Licensing.PkiLicenseGate` | Que o PKI SDK recusou a licença deste Servidor, de modo que nem a frota nem o cofre conseguem assinar até ela ser renovada, ou que não há licença e o Servidor não vai iniciar |
| `TrustBridge.Pki.Validation.LacunaPkiRuntime` | Que o PKI SDK está licenciado e pronto na inicialização, e onde ficam os caches dele. Em `Error`, por que uma licença configurada não pôde ser carregada |

Aumente o nível de uma categoria, não o nível mínimo geral: o log de debug do framework soterra essas
sete linhas.

### `Logging:File:Directory`

**Padrão** `trustbridge-logs` no diretório temporário do processo (`/tmp/trustbridge-logs`) · **Override pelo Administrador** não · **Exige reinício** sim

Onde o arquivo rotativo em JSON compacto é gravado, um por dia, com nova rotação ao passar de um
gigabyte. A tela **Log do Servidor** do console administrativo lê esse arquivo. **Vazio desliga o
arquivo**, que é o que um sistema de arquivos somente leitura define. Um diretório que o Servidor não
consegue criar deixa o arquivo desligado durante toda a vida do processo, com um `Warning`; o console
não é afetado.

### `Logging:File:RetainedDays`

**Padrão** 14 · **Override pelo Administrador** não · **Exige reinício** sim

Quantos dias de arquivos são mantidos. Abaixo de 1, a inicialização é recusada. É um limite de uso de
disco, e não uma política de retenção: o pipeline do console é o registro.

### `Logging:ApplicationInsights:ConnectionString`

**Padrão** não definido · **Override pelo Administrador** não · **Exige reinício** sim

Uma connection string do Application Insights. Quando definida, toda linha também é enviada para lá
como um trace, com o id de correlação e a categoria como dimensões personalizadas. **Quando não
definida, `APPLICATIONINSIGHTS_CONNECTION_STRING` é lida no lugar dela**, então um workspace associado
pelo portal do App Service não precisa de uma segunda configuração. Um valor que não seja uma
connection string recusa a inicialização.

### `AllowedHosts`

**Padrão** `*` · **Override pelo Administrador** não · **Exige reinício** sim

O filtro de header Host do framework. Nenhum dos dois cenários precisa dele.

## Atrás de um proxy reverso

Quem o Servidor acredita que está chamando. **Uma implantação sem nada na frente não define nada aqui,
e o App Service também não**, porque usa a opção do próprio framework.

Três coisas leem o endereço de quem chama: o limite por endereço da admissão
([`Admission:RequestsPerAddress`](https://docs.lacunasoftware.com/articles/trustbridge/configuration#admissão-de-estações-de-trabalho)), o limite de desafios do acesso
com certificado ([`Admin:CertificateSignIn:Enabled`](https://docs.lacunasoftware.com/articles/trustbridge/configuration#admincertificatesigninenabled)) e o `ClientIp`
de toda linha de auditoria. Por padrão, é o endereço da conexão; atrás de um proxy reverso, então, é o
endereço do proxy, e todos os clientes viram um só. Citar o proxy faz o Servidor aceitar
`X-Forwarded-For` e `X-Forwarded-Proto` vindos dele, e de mais ninguém.

**Há duas formas de ligar isso, e uma implantação usa uma delas:**

| Configuração | Para | Confia em |
|---|---|---|
| `ForwardedHeaders:TrustedProxies` | Um proxy que você opera e consegue citar: Docker atrás de nginx, HAProxy ou um balanceador de carga | Nos endereços listados, por tantos saltos quantos forem listados |
| `ASPNETCORE_FORWARDEDHEADERS_ENABLED=true` | O App Service, cujos front ends não podem ser listados | **Em quem se conectar**, por um salto. Só é seguro porque o container é acessível por esses front ends e por nada mais |

**Definir as duas recusa a inicialização.** Uma implantação Docker que definiu
`ASPNETCORE_FORWARDEDHEADERS_ENABLED` seguindo um guia antigo continua funcionando, confiando em quem se
conectar, e registra um `Warning` na inicialização avisando disso; passe a usar a lista.

```yaml
environment:
  ForwardedHeaders__TrustedProxies__0: "10.0.0.5"
  ForwardedHeaders__TrustedProxies__1: "10.0.1.0/24"
```

### `ForwardedHeaders:TrustedProxies`

**Padrão** vazio, o que significa o próprio endereço da conexão · **Override pelo Administrador** não · **Exige reinício** sim

Os proxies cujos `X-Forwarded-For` e `X-Forwarded-Proto` este Servidor considera. Cada entrada é um
endereço (`10.0.0.5`) ou uma rede em notação CIDR (`10.0.1.0/24`), IPv4 ou IPv6. Uma conexão de
qualquer outro lugar tem os dois headers ignorados, então um cliente que chega ao Servidor contornando o
proxy não consegue informar o próprio endereço. Considerar `X-Forwarded-Proto` é o que dá a um proxy que
termina o TLS cookies `Secure` e redirecionamentos de login para `https://`.

- **Liste o proxy, não a rede em que estão os clientes dele.** Um cliente dentro de uma rede listada é
  tratado como proxy e pode informar o endereço que quiser.
- **É uma lista, mesmo com um item.** No ambiente, toda entrada leva um índice,
  `ForwardedHeaders__TrustedProxies__0`. `ForwardedHeaders__TrustedProxies=10.0.0.5` seria descartada
  sem aviso, então o Servidor recusa a inicialização.
- **Uma cadeia é percorrida de volta até o primeiro endereço não listado.** Com um balanceador de carga
  na frente do proxy, liste os dois. Um proxy na borda deve **substituir** o `X-Forwarded-For` pelo
  endereço que viu (o `$remote_addr` do nginx); um proxy atrás de um balanceador listado deve
  **acrescentar** ao header (o `$proxy_add_x_forwarded_for` do nginx). Um proxy atrás de um balanceador
  que termina o TLS repassa o `X-Forwarded-Proto` do balanceador, em vez de trocá-lo por `http`.
- **Só o endereço e o esquema.** `X-Forwarded-Host` e `X-Forwarded-Prefix` nunca são considerados, e o
  header padrão `Forwarded` (RFC 7239) não é lido. O proxy precisa repassar o header `Host` do navegador
  (no nginx, `proxy_set_header Host $host`), ou o redirecionamento de login aponta para o endereço do
  upstream.

A inicialização é recusada para uma lista que confiaria nos pares errados:

| Entrada | Por que é recusada |
|---|---|
| `0.0.0.0/0`, `::/0` | Confia em todo mundo. |
| Bits definidos além do prefixo, como `10.0.0.5/24` | É assim que uma interface imprime o próprio endereço; lida como rede, confiaria na sub-rede inteira. |
| Fora do formato decimal com pontos, como `010.0.0.5`, `10` ou `0x0a000005` | O parser as lê como outras máquinas. Escreva o endereço como o Servidor o imprime. |
| IPv4 na forma IPv6 (`::ffff:10.0.0.5`) | Escreva o endereço IPv4; ele também corresponde à forma dual-stack. |
| IPv6 link-local (`fe80::…`) | O Servidor vê esse par com um escopo anexado, então a entrada nunca corresponderia. |
| Algumas redes IPv6 | As que o .NET 10 interpretaria erradamente como contendo clientes IPv4. Um endereço IPv6 exato é sempre seguro, assim como uma rede de /96 ou menos cujo comprimento seja múltiplo de 8 (`/32`, `/48`, `/64`). |
| Algo que não é um endereço | Citada pelo índice. |
| Um único valor sem índice, ou qualquer outra chave sob `ForwardedHeaders` (como `KnownProxies`) | De outro modo, não seria lida por nada. |
| Definida junto com `ASPNETCORE_FORWARDEDHEADERS_ENABLED` | A opção do framework já confia em todo mundo, então a lista não restringiria nada. |

**Quando as linhas de auditoria continuam mostrando o proxy**, leia a linha de inicialização que começa
com `Forwarded headers:`: ela cita a lista, diz que nada é considerado, ou avisa que
`ASPNETCORE_FORWARDEDHEADERS_ENABLED` confia em quem se conectar. Uma conexão de um par que a lista não
cita só é registrada em `Debug`, na categoria `Logging:LogLevel:Microsoft.AspNetCore.HttpOverrides`;
aumente o nível dela enquanto verifica de qual endereço o proxy realmente se conecta.

**Configuração da implantação, e nunca uma tela**: quais máquinas podem falar pelo endereço de quem
chama é um fato da rede na frente do Servidor, e um valor errado é um jeito de contornar todas as regras
por endereço.

## O serviço de assinatura

Onde fica o Lacuna Signer e como o TrustBridge se autentica nele. **As duas coisas ou nenhuma**: um
Signer não configurado é uma implantação suportada, em que `/v1/signatures` recusa as chamadas e todo
o resto funciona; um endereço sem chave **recusa a inicialização**.

```yaml
environment:
  Signer__BaseAddress: https://signer.example.com
  Signer__ApiKey: My Application|43fc0da834e48b4b840fd6e8c371…
  Signer__ApplicationName: My Application    # only for a key that is the secret half alone
  Signer__FolderId: f81d4fae-7dec-11d0-a765-00a0c91e6bf6
  Signer__RequestTimeout: "00:00:10"
```

| Chave | Padrão | Significado |
|---|---|---|
| `Signer:BaseAddress` | não definido | O endereço base do Signer. Defini-lo torna `ApiKey` obrigatória. |
| `Signer:ApiKey` | não definido | A única chave de API do Signer que o TrustBridge guarda em nome de todas as Aplicações Cliente, na forma completa `application\|secret` que o Signer publica. |
| `Signer:ApplicationName` | não definido | A parte da aplicação, só para uma chave que traga apenas o secret. Ignorada quando a chave contém `\|`. |
| `Signer:FolderId` | não definido | A pasta do Signer em que todos os documentos são colocados, como GUID. Não definida, é a raiz da organização. |
| `Signer:RequestTimeout` | 10 s | O limite de uma chamada ao Signer. Um valor não positivo recusa a inicialização. |

Todas exigem reinício, e nenhuma aceita override.

## Sessões de Assinatura

Cinco das sete chaves que aceitam override do Administrador estão aqui. `Sessions:SignatureAllowance`
não tem padrão e está ausente do exemplo de propósito.

```yaml
environment:
  Sessions__DefaultDuration: "01:00:00"
  Sessions__MaxDuration: "1.00:00:00"
  Sessions__MaxPinFailures: "5"
  Sessions__PinFailureWindow: "00:15:00"
  Sessions__PinPromptTimeout: "00:01:00"
  Sessions__PinRelayTimeout: "00:00:05"
  Sessions__ExpirySweepInterval: "00:05:00"
```

| Chave | Padrão | Override | Reinício | Significado |
|---|---|---|---|---|
| `Sessions:DefaultDuration` | 1 h | **sim** | não | A janela de ociosidade concedida quando o cliente não pede nenhuma. Um valor não positivo recusa a inicialização. |
| `Sessions:MaxDuration` | 24 h (`1.00:00:00`) | **sim** | não | A maior janela que um cliente pode pedir; um pedido maior é reduzido a esse limite, e o valor reduzido é devolvido. Abaixo de `DefaultDuration`, recusa a inicialização. |
| `Sessions:MaxPinFailures` | 5 | **sim** | não | Comparações de PIN com falha, dentro da janela, que bloqueiam um CPF para criação de sessão, contadas por CPF somando todas as chaves de API. É também o tamanho dos limites de provas com falha descritos abaixo. Zero recusa a inicialização. |
| `Sessions:PinFailureWindow` | 15 min | **sim** | não | A janela deslizante do bloqueio e o tempo de espera, e a janela em que os limites de provas com falha são contados. Um valor não positivo recusa a inicialização. |
| `Sessions:PinPromptTimeout` | 60 s | **sim** | não | Por quanto tempo um pedido de PIN no Tray App fica na tela antes de a criação desistir. Quando esse prazo acaba, o Servidor tira o pedido da tela de uma máquina no protocolo 5 na hora. O `Tray:PromptTimeout` da própria estação de trabalho (90 s) é mais folgado de propósito, e é o que encerra um pedido que o Servidor não consegue alcançar. |
| `Sessions:PinRelayTimeout` | 5 s | não | sim | Quanto tempo a passagem pelo PIN em cache espera as respostas das máquinas. Todas as máquinas candidatas são desafiadas ao mesmo tempo dentro dele, e não uma de cada vez, então uma máquina que nunca responde custa isso e nada mais. A verificação da resposta de um Certificado A1 acontece depois, no Servidor. Um valor não positivo recusa a inicialização. O nome é o do antigo repasse do PIN e foi mantido por ser uma chave de configuração. |
| `Sessions:ExpirySweepInterval` | 5 min | não | sim | A frequência com que a varredura encerra no registro as sessões vencidas. Não muda nenhum veredito. Não é validada na inicialização. |

### Os limites de PIN com falha

`Sessions:MaxPinFailures` dentro de `Sessions:PinFailureWindow` define três limites de uma vez. Reduzir
o número aperta os três.

| Limite | O que conta | Quando se esgota |
|---|---|---|
| O bloqueio do CPF | Comparações de PIN com falha para um CPF, no máximo uma por requisição, somando todas as chaves de API | A criação de sessão para esse CPF responde `TOO_MANY_PIN_ATTEMPTS` até as falhas saírem da janela. Uma criação bem-sucedida não as zera; elas saem da janela com o tempo. |
| O limite de provas com falha de uma estação de trabalho | Provas de PIN (*PIN Proofs*) que uma máquina deu para um CPF e que não conferiram, mesmo que outra candidata tenha correspondido, exceto numa criação em que uma prova da própria máquina conferiu | Essa máquina não recebe desafio de PIN nem pedido de PIN para esse CPF e é considerada inacessível para esse titular até as falhas saírem da janela. Registrado em `Warning` e auditado como `pin.proof-budget-spent`. |
| O limite do Cofre de Certificados | Senhas que o cofre tentou para um CPF e que não abriram nada, uma vez por criação, mesmo que outra candidata tenha correspondido | O cofre não tenta nenhuma para esse CPF até elas saírem da janela. Ele continua candidato, mas não é tentado, então o cliente recebe `PIN_NOT_AVAILABLE` em vez de `WORKSTATION_OFFLINE`. Auditado como `pin.proof-budget-spent`, sem citar máquina. |

As falhas de uma máquina nunca contam contra outra, mas **a própria máquina do titular pode ser deixada
de lado antes de o bloqueio disparar**: um PIN errado custa a ela uma prova com falha para cada
certificado do titular que ela guarda para a sessão, enquanto o bloqueio conta uma. Um titular que se
vincula por um token cujo PIN não é a senha dele no cofre também gasta o limite do cofre, e vê o cofre
deixado de lado depois dessa quantidade de sessões assim dentro da janela.

### `Sessions:SignatureAllowance`

**Padrão** não definido (sem limite) · **Override pelo Administrador** não · **Exige reinício** sim

Quantas **chamadas** de assinatura uma Sessão de Assinatura pode fazer antes de terminar como
**Esgotada** (*Exhausted*), o que vier primeiro entre seus dois limites. Uma unidade por chamada, não
por documento: um lote de cinquenta gasta uma, e uma chamada cujos documentos falharam todos antes de o
certificado assinar não gasta nenhuma. Uma franquia de `1` dá uma sessão de uso único. Zero ou menos recusa a inicialização. É concedida na criação, então uma
alteração só governa as sessões criadas depois dela. O protocolo não informa o saldo restante; a tela
**Sessões de Assinatura** mostra o quanto foi gasto.

## A superfície de assinatura

```yaml
environment:
  Signing__RequestBudget: "00:01:00"
  Signing__HashSignTimeout: "00:00:25"
  Signing__SignerCancelBudget: "00:00:03"
  Signing__StrandedAfter: "00:10:00"
  Signing__StrandedSweepInterval: "00:05:00"
  Signing__WorkstationReconnectGrace: "00:00:15"
  Signing__ReconnectPollInterval: "00:00:00.250"
  Signing__MaxDocumentBytes: "10485760"
  Signing__TransportHeadroomBytes: "65536"
  Signing__MaxBatchItems: "50"
  Signing__SignerConcurrency: "32"
```

| Chave | Padrão | Override | Significado |
|---|---|---|---|
| `Signing:RequestBudget` | 60 s | não | Toda uma assinatura síncrona, da admissão da requisição até os bytes assinados. Estourar esse prazo responde `SIGN_TIMEOUT`. `HashSignTimeout` mais `WorkstationReconnectGrace` precisam caber nele, ou a inicialização é recusada. |
| `Signing:HashSignTimeout` | 25 s | não | Quanto tempo uma assinatura de hash despachada pode levar antes de a máquina ser considerada inacessível. Bem acima dos cerca de 340 ms que um token gasta, porque a máquina pode estar ocupada com o documento de outra pessoa. |
| `Signing:SignerCancelBudget` | 3 s | não | Quanto tempo a limpeza pode levar quando uma execução que não assinou nada encerra o documento no Signer. |
| `Signing:StrandedAfter` | 10 min | não | Quanto tempo uma Operação de Assinatura pode ficar em `Pending` antes de a varredura encerrá-la como `Failed`, com `RUN_STRANDED`. Precisa ser maior que `RequestBudget` mais `SignerCancelBudget`. |
| `Signing:StrandedSweepInterval` | 5 min | não | A frequência dessa varredura. Uma passagem também roda na inicialização. Não é validada na inicialização. |
| `Signing:WorkstationReconnectGrace` | 15 s | **sim** | Quanto tempo uma assinatura espera a estação de trabalho vinculada voltar antes de responder `WORKSTATION_OFFLINE`. Zero recusa na hora e é válido; um valor negativo recusa a inicialização. |
| `Signing:ReconnectPollInterval` | 250 ms | não | A frequência com que essa tolerância verifica de novo a presença. Um valor não positivo recusa a inicialização. |
| `Signing:MaxDocumentBytes` | 10 MB (`10485760`) | não | O maior documento que o Servidor assina, medido nos bytes **decodificados**. Acima do limite, responde 413 no envelope do contrato. Limita a memória, não o armazenamento. Os limites de transporte subjacentes são derivados dele. |
| `Signing:TransportHeadroomBytes` | 64 KB | não | Folga, além do documento codificado, para o restante do corpo da requisição. |
| `Signing:MaxBatchItems` | 50 | não | Quantos documentos uma chamada em lote pode citar. Uma chamada com mais é recusada antes de qualquer documento ser assinado. Junto com `MaxDocumentBytes`, define o que um lote em andamento pode ocupar: 500 MB com os padrões. |
| `Signing:SignerConcurrency` | 32 | não | Quantas execuções de assinatura podem estar em andamento no Signer ao mesmo tempo, em todo o Servidor. Um lote maior roda em ondas. Zero recusa a inicialização. |

Todas exigem reinício, exceto `WorkstationReconnectGrace`.

## O throttling global

Um limite para quantas chamadas do contrato o Servidor executa ao mesmo tempo, com uma fila curta à
frente dele. Uma chamada descartada responde `429 SERVER_BUSY`, sem `Retry-After`. Ele vale somente
para a superfície do SignSession Contract; o canal das estações de trabalho, o console administrativo,
o probe de saúde, a página inicial, a documentação, a admissão de estações de trabalho e os downloads
de Versões da Estação de Trabalho ficam fora dele (os downloads têm
[um limite próprio](https://docs.lacunasoftware.com/articles/trustbridge/configuration#downloads-de-versões-da-estação-de-trabalho)).
Os padrões ficam quase sete vezes acima da carga projetada: é um disjuntor (circuit breaker), não um
teto de vazão.

| Chave | Padrão | Significado |
|---|---|---|
| `Throttle:ConcurrencyLimit` | 200 | Quantas chamadas do contrato podem rodar ao mesmo tempo. Zero recusa a inicialização; de propósito, não há como desligá-lo. É `Signing:SignerConcurrency`, e não este, que molda a vazão de assinaturas. |
| `Throttle:QueueLimit` | 50 | Quantas chamadas podem esperar por uma vaga antes de a seguinte ser descartada. Zero é válido e significa sem fila; um valor negativo recusa a inicialização. |

## Admissão de estações de trabalho

Quais máquinas podem pedir para entrar, quanto da fila um desconhecido pode ocupar e por quanto tempo
um pedido abandonado permanece. Uma implantação padrão não define nenhuma destas. Todas exigem
reinício, e nenhuma está na tela **Configurações e usuários**.

```yaml
environment:
  Admission__InstallerKeys__0: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE…"
  Admission__MaxPending: "500"
  Admission__RequestsPerAddress: "300"
  Admission__RequestWindow: "00:01:00"
  Admission__PendingExpiry: "7.00:00:00"
  Admission__RequireHardwareBackedKeys: "false"
```

| Chave | Padrão | Significado |
|---|---|---|
| `Admission:InstallerKeys` | A Installer Key distribuída com o TrustBridge | As Installer Keys com que este Servidor aceita pedidos de admissão assinados, cada uma um `SubjectPublicKeyInfo` DER em base64 de uma chave pública ECDSA P-256. **Definir esta chave substitui o padrão, em vez de somar a ele.** Liste duas durante uma troca: adicione a nova ao lado da antiga, atualize a frota e então remova a entrada antiga. Um valor que não pode ser interpretado recusa a inicialização. |
| `Admission:MaxPending` | 500 | Quantas máquinas podem estar aguardando um Administrador ao mesmo tempo. Acima disso, o pedido de uma máquina que este Servidor nunca viu é recusado. Zero recusa a inicialização. |
| `Admission:RequestsPerAddress` | 300 | Quantos pedidos de admissão um endereço de origem pode fazer por janela, com um endereço IPv6 contado pelo seu /64. É um limite contra incômodo: atrás de NAT, um escritório inteiro é um único endereço, e atrás de um proxy reverso a frota inteira conta como o proxy, a menos que [`ForwardedHeaders:TrustedProxies`](https://docs.lacunasoftware.com/articles/trustbridge/configuration#forwardedheaderstrustedproxies) o cite. |
| `Admission:RequestWindow` | 1 min | A janela fixa do limite acima. |
| `Admission:PendingExpiry` | 7 dias | Quanto tempo o pedido de uma máquina fica na fila depois da última vez que o Servidor teve notícias dela. É contado a partir do **último** pedido, então uma máquina que continua pedindo nunca expira. A expiração não é uma recusa. |
| `Admission:RequireHardwareBackedKeys` | `false` | Se a admissão de uma máquina cuja Chave da Estação de Trabalho não está em hardware deve ser recusada. Essa máquina continua aparecendo na fila, e a tela explica por que o controle **Admitir** a recusa. A postura é uma declaração da própria máquina; nada a atesta. |

## O console administrativo

O primeiro Administrador, se uma senha sozinha dá acesso ao console, se um certificado dá, e se uma
senha ainda dá acesso.

```yaml
environment:
  Admin__Bootstrap__Email: admin@example.com
  Admin__Bootstrap__Password: ${ADMIN_BOOTSTRAP_PASSWORD}
  Admin__Bootstrap__DisplayName: Platform Administrator
  Admin__SecondFactor__Required: "true"
  Admin__CertificateSignIn__Enabled: "false"
  Admin__CertificateSignIn__WebPkiLicense: ${WEB_PKI_LICENSE}
  Admin__CertificateSignIn__Required: "false"
```

### `Admin:Bootstrap:Email`, `Admin:Bootstrap:Password`, `Admin:Bootstrap:DisplayName`

**Padrão** não definido · **Override pelo Administrador** não · **Exige reinício** sim

O primeiro Administrador. Lidas num Servidor cuja tabela de contas está **vazia**, e inertes em todas
as inicializações seguintes: uma senha configurada nunca recria uma conta excluída nem redefine uma
existente. A senha precisa atender às regras de senha da identidade, ou a conta não é criada e o motivo
é registrado no log. Remova as duas depois de entrar. Com elas não definidas e a tabela vazia, o
Servidor registra um `Warning` e não cria nada; ninguém consegue entrar no console até que algo
coloque uma conta lá.

### `Admin:SecondFactor:Required`

**Padrão** `true` · **Override pelo Administrador** **não, de propósito** · **Exige reinício** sim

Se uma Conta Administrativa precisa apresentar um segundo fator além da senha. **Configuração da
implantação, e nunca uma tela**: uma opção que desativa o segundo fator, ao alcance de dentro do
console que ele protege, é uma opção que um invasor desliga no caminho. Uma linha salva no banco de
dados com esta chave é ignorada. Vale para toda a plataforma, sem exceção por conta.

É também o **acesso de emergência (break-glass)** para um Administrador que perdeu o autenticador; o
procedimento está em [Segurança](https://docs.lacunasoftware.com/articles/trustbridge/security#acesso-de-emergência-break-glass-autenticador-perdido). A postura
enfraquecida é anunciada a cada inicialização enquanto a opção estiver desligada.

### `Admin:CertificateSignIn:Enabled`

**Padrão** `false` · **Override pelo Administrador** **não, de propósito** · **Exige reinício** sim

Se a página de login oferece o Acesso com Certificado, com um e-CPF ICP-Brasil. O certificado é lido no
computador da própria pessoa pelo **Lacuna Web PKI**, uma extensão de navegador com uma parte nativa que
cada pessoa instala uma vez; só em navegadores de desktop. Num celular ou tablet, a página de login
avisa isso. Só certificados e-CPF são listados.

**Desligada, a página de login é exatamente o que era**: nenhum script é servido a ela e nenhuma
licença é escrita nela. **Ligada, ela precisa de
[`Admin:CertificateSignIn:WebPkiLicense`](https://docs.lacunasoftware.com/articles/trustbridge/configuration#admincertificatesigninwebpkilicense)**, ou o Servidor
recusa a inicialização citando essa chave. O formulário de senha continua ao lado do controle de
certificado, a menos que [`Admin:CertificateSignIn:Required`](https://docs.lacunasoftware.com/articles/trustbridge/configuration#admincertificatesigninrequired) o
retire.

**Ligada, uma conta com CPF vinculado entra só com o certificado**: sem senha e sem código do Segundo
Fator. Um Administrador digita o CPF de outra conta na tela de contas, ao criá-la ou com **Definir CPF
vinculado** na linha dela, e só enquanto esta chave está ligada; ninguém define o próprio, então
ninguém entra com certificado até outro Administrador ter definido um. Uma conta que ainda não tem
Segundo Fator, onde [`Admin:SecondFactor:Required`](https://docs.lacunasoftware.com/articles/trustbridge/configuration#adminsecondfactorrequired) está ligada, é levada a
cadastrar um primeiro.

A página de login pede um desafio em `/admin/login/certificate/challenge`, com limite de dez por minuto
por endereço de origem. **Atrás de um proxy reverso, cite-o em
[`ForwardedHeaders:TrustedProxies`](https://docs.lacunasoftware.com/articles/trustbridge/configuration#forwardedheaderstrustedproxies)**, ou todos os navegadores dividem
os dez do proxy, e qualquer pessoa que alcance a página de login pode esgotá-los por um minuto de cada
vez.

Uma linha salva no banco de dados com esta chave é ignorada, pelo motivo de `Admin:SecondFactor:Required`:
o que decide como uma pessoa chega ao console nunca é configurado de dentro dele.

### `Admin:CertificateSignIn:WebPkiLicense`

**Padrão** não definido · **Override pelo Administrador** não · **Exige reinício** sim

**Obrigatória quando `Admin:CertificateSignIn:Enabled` está ligada.** A licença do **Web PKI**, no
**formato de uma linha** que a Lacuna emite, escrita como uma única string. Não o formato JSON: escrita
como objeto, não é lida por nada, e o Servidor recusa a inicialização como se nenhuma licença estivesse
definida. **Não** é a licença do PKI SDK ([`Certificates:Validation:License`](https://docs.lacunasoftware.com/articles/trustbridge/configuration#certificatesvalidationlicense)).

**Não é um segredo.** Ela é avaliada no navegador, então qualquer pessoa que carregue a página de login
pode lê-la, e ela está vinculada aos domínios que cita. **Ela precisa citar o hostname que as pessoas
digitam**: uma licença que não o cobre falha na primeira vez que o componente é usado, e a página de
login informa isso como um problema de licença para o operador.

### `Admin:CertificateSignIn:Required`

**Padrão** `false` · **Override pelo Administrador** **não, de propósito** · **Exige reinício** sim

Se as senhas estão **desligadas** para o console administrativo. Ligada, a página de login não tem
formulário de senha, uma senha enviada mesmo assim é recusada sem nada ser verificado nem contado contra
a conta, e um certificado do CPF vinculado da conta é a única forma de entrar. O cadastro do Segundo
Fator num primeiro acesso com certificado funciona como funciona sem ela.

**Ela exige `Admin:CertificateSignIn:Enabled` ligada.** Ligada enquanto aquela está desligada descreve
um console sem nenhuma forma de entrar, então o Servidor recusa a inicialização citando as duas chaves.
É a única inicialização que esta chave recusa.

**Uma conta sem CPF vinculado não consegue entrar enquanto ela está ligada**, seja Administrador ou
Auditor. Outro Administrador define um na tela de contas, e as senhas continuam desligadas enquanto
isso. Ninguém consegue definir o próprio, então **um Administrador sozinho não consegue desligar as
senhas**: uma implantação que faz isso precisa de pelo menos dois Administradores, cada um com um CPF
vinculado que o outro definiu, antes de ligar esta chave.

**Ela avisa a cada inicialização enquanto está ligada.** O aviso conta quantas contas habilitadas não
têm CPF vinculado, quantas delas são de Administradores e quantos Administradores habilitados existem.
Quando o segundo número chega ao terceiro, ninguém consegue administrar a plataforma. Onde nenhum proxy
é citado em `ForwardedHeaders:TrustedProxies`, o aviso também diz que todos os navegadores dividem os
dez desafios por minuto do proxy, então qualquer pessoa que alcance a página de login consegue deixar
todo mundo de fora. **Cite o proxy antes de ligar esta chave.**

**Ela é o acesso de emergência (break-glass).** Quando ninguém consegue apresentar um certificado que o
Servidor aceite (uma Autoridade Certificadora Confiável removida, uma licença da PKI vencida, uma
licença do Web PKI que não cita mais o hostname, uma atualização de navegador que quebrou a extensão,
ou o CPF vinculado do último Administrador removido), defina-a como `false` e reinicie: o login com
senha volta sem nenhuma outra mudança. Ligue-a de novo depois; nada vai lembrar você, porque o aviso
só aparece enquanto as senhas estão desligadas.

Uma sessão aberta com senha antes da mudança não é encerrada por ela. Um login com senha que estava
parado na etapa do código quando o Servidor reiniciou volta para o formulário de login.

## A Trilha de Auditoria

### `Audit:RetentionDays`

**Padrão** 365 · **Override pelo Administrador** **sim** · **Exige reinício** não

Por quanto tempo os eventos de auditoria são mantidos; uma limpeza diária exclui as linhas mais
antigas. Abaixo de 1, a inicialização é recusada: uma retenção de zero apagaria toda a trilha na
passagem seguinte.

Não há configuração para o destino de uma gravação de auditoria que falhou. Uma entrada que o banco de
dados não aceita é gravada no Log do Servidor no nível `Critical`, com a própria linha, e o console
administrativo mostra um alerta. Ela chega até você pelo pipeline de logs que a implantação tiver,
então o fallback da auditoria faz parte da sua **retenção de logs**, e não dos seus backups.

## Idiomas

```yaml
environment:
  Language__DefaultCulture: pt-BR
  Language__SupportedCultures__0: en
  Language__SupportedCultures__1: pt-BR
```

| Chave | Padrão | Significado |
|---|---|---|
| `Language:DefaultCulture` | `en` | O idioma em que é respondida uma requisição sem um `Accept-Language` utilizável. Não é um idioma de toda a implantação: a preferência do navegador do leitor e o seletor de idioma prevalecem. Um valor fora do conjunto suportado, ou um erro de digitação, recusa a inicialização. |
| `Language:SupportedCultures` | vazio, o que significa `en` e `pt-BR` | Todos os idiomas em que as páginas respondem. Informar um conjunto substitui o padrão. |

A superfície do contrato, os logs e a Trilha de Auditoria ficam permanentemente em inglês.

## A documentação publicada

### `Documentation:ApiReferenceEnabled`

**Padrão** `true` · **Override pelo Administrador** não · **Exige reinício** sim

Se a referência navegável é servida em `/scalar`. O documento legível por máquina em
`/openapi/v1.json` é o contrato de integração, é sempre servido e não é afetado. Os dois respondem sem
credencial.

## O canal das estações de trabalho

Como uma máquina admitida prova que é ela mesma a cada conexão, com uma asserção de curta duração
assinada pela Chave da Estação de Trabalho, e por quanto tempo o Servidor espera ter notícias de uma
máquina conectada. As duas chaves da asserção não são algo que uma implantação saudável precise mexer;
`ClientTimeout` é para mudar uma vez, depois que a frota estiver atualizada.

```yaml
environment:
  Workstations__ClockSkewTolerance: "00:01:00"
  Workstations__MaxAssertionLifetime: "00:05:00"
  Workstations__ClientTimeout: "00:00:30"
```

| Chave | Padrão | Significado |
|---|---|---|
| `Workstations:ClockSkewTolerance` | 60 s | A diferença máxima entre o relógio do Servidor e o de uma estação de trabalho antes que essa máquina não consiga se conectar. Aplicada nas duas pontas. O log do Servidor cita a máquina com o relógio desviado e a diferença, positiva quando a máquina está atrás do Servidor (`A workstation assertion was refused: Expired` ou `NotYetValid`). Um valor negativo ou acima de uma hora recusa a inicialização. |
| `Workstations:MaxAssertionLifetime` | 5 min | A maior janela de validade aceita numa asserção; uma declaração mais longa é recusada. Limita o cache de nonces, que guarda no máximo 100.000 nonces e, quando cheio, recusa em vez de esquecer. Um valor não positivo ou acima de uma hora recusa a inicialização. |
| `Workstations:ClientTimeout` | 30 s | Por quanto tempo uma máquina conectada pode ficar em silêncio antes de o Servidor desligar a conexão dela. Veja abaixo. |

Todas exigem reinício, e nenhuma aceita override.

### `Workstations:ClientTimeout`

**Padrão** 30 s (`00:00:30`) · **Override pelo Administrador** não · **Exige reinício** sim

Por quanto tempo uma estação de trabalho pode ficar sem que o Servidor tenha notícias dela, uma chamada
ou um ping de keep-alive, antes de o Servidor fechar a conexão. A máquina então aparece offline na
página da frota na próxima atualização (em até 10 s), o último contato dela é gravado e a desconexão
entra na Trilha de Auditoria. Uma máquina que continua lá se reconecta sozinha. Isso só importa para
máquinas que somem: um cabo desconectado, Wi-Fi perdido, a tampa de um notebook fechada, um firewall
que derruba a conexão em silêncio. Uma máquina que desliga ou para o Service de forma limpa aparece
offline na próxima atualização, qualquer que seja este valor.

**Defina-a como `00:00:15` quando todas as máquinas rodarem uma versão da Estação de Trabalho
posterior à 1.1.0, e não antes.** O Servidor e um Workstation Service atual fazem ping a cada 5 s; a
Estação de Trabalho 1.0.x e a 1.1.0 fazem ping a cada 15 s, e nada que o Servidor diga muda isso.
Limitadas a 15 s, essas máquinas seriam desconectadas depois de quase todo ping, e cada desconexão é
uma reconexão, um handshake e um par de conexão/desconexão na Trilha de Auditoria. A coluna **Versão**
da página da frota é onde verificar. Cada máquina para a qual o valor é curto demais é citada no log do
Servidor em `Warning`, uma vez por conexão: procure
`pings every 00:00:15, but Workstations:ClientTimeout is`.

Com 15 s, uma máquina que some aparece offline em cerca de 25 s; com o padrão, em cerca de 40 s. **Uma
máquina em long polling** (atrás de um proxy que recusa WebSockets, por exemplo) **não é desconectada
por este timeout**: ela nunca faz ping, e a conexão termina pelo timeout de ociosidade da própria
camada de conexão quando as requisições de polling param.

Uma linha de desconexão na Trilha de Auditoria diz o motivo, como `"reason"` nos detalhes: `timed-out`
para este timeout, `closed`, `revoked`, `returned-to-queue`, `server-stopping` ou `error`. A aba
**Eventos** da máquina na página da frota mostra a mesma palavra. Uma máquina em long polling que ficou
em silêncio aparece como `closed`, por isso a linha também cita o `transport` dela.

**Escreva no formato `hh:mm:ss`.** Um `15` sozinho vira quinze *dias*, e `0:15`, quinze minutos.
Menos de 10 s (o dobro dos 5 s em que uma máquina atual faz ping) ou mais de 5 minutos **recusa a
inicialização**.

### Atualizar um Servidor e sua frota para o protocolo 5

O canal está no **protocolo 5**: a versão em que o Servidor deixa de enviar o PIN da aplicação cliente
a qualquer estação de trabalho. A criação de sessão desafia cada máquina candidata com um nonce, a
máquina responde com uma **prova de PIN** (*PIN Proof*) sobre o PIN que tem em cache (ou, para um
Certificado A1, sobre o verificador do PIN do Certificado A1), e o Servidor decide se correspondeu.
**A janela deste Servidor é 4–5.**

| Máquina | Protocolo | Com o Servidor 1.13.0 |
|---|---|---|
| Estação de Trabalho 1.5.0 | 5 | Totalmente suportada. |
| Estação de Trabalho 1.4.x | 4 | Conecta, é licenciada, assina para as sessões já vinculadas a ela e pode receber a oferta de uma versão. **Ela nunca recebe desafio de PIN nem pedido de PIN**, então, para a criação de sessão, conta como offline: um titular cujas máquinas estão todas no protocolo 4 recebe `422 WORKSTATION_OFFLINE`, e o Servidor registra um `Warning` citando a máquina cada vez que a deixa de lado. |
| Estação de Trabalho 1.0.x a 1.3.0 | 2 ou 3 | Recusada no handshake, com *atualização necessária* na linha dela na frota e na Trilha de Auditoria. Ela também não pode receber oferta de versão: atualize-a manualmente ou por Group Policy. |

**Esta é a única atualização que não segue "primeiro o Servidor, a frota dentro de uma versão".** Uma
máquina no protocolo 4 não atende novas sessões até ser atualizada, então atualize o Servidor e a
frota juntos: envie a Estação de Trabalho 1.5.0 na tela **Versões da Estação de Trabalho** como parte da
atualização do Servidor e ofereça-a na hora. Uma máquina no protocolo 5 não consegue se conectar a um
Servidor anterior ao 1.13.0 (janela 3–4): ela recebe *update the Server first*, não recebe licença, e
todo certificado aparece como `UNTRUSTED` no Tray App até o Servidor ser atualizado.

**O que muda para os integradores: nada.** O PIN continua chegando em `POST /v1/sessions` e não vai
além do Servidor. `INVALID_PIN` agora significa que todas as candidatas deram uma prova e nenhuma
conferiu com o PIN enviado.

### Atualizar um Servidor e sua frota para o protocolo 4

O protocolo 4 (Servidor 1.9.0, Estação de Trabalho 1.4.0) é a versão em que o Servidor diz a uma
máquina qual **Versão da Estação de Trabalho** lhe é oferecida, então as atualizações entregues pelo
Servidor precisam dele nas duas pontas. Em todo handshake, uma máquina no protocolo 4 ou posterior
informa ao Servidor se a Group Policy desligou as atualizações entregues pelo Servidor, o Publisher Pin
com que o Service dela foi gerado e como terminou a última atualização. O Servidor mostra as três
informações na linha da máquina e grava `workstation.update-installed`, `workstation.update-failed` ou
`workstation.update-refused` na Trilha de Auditoria quando o resultado informado muda.

Uma oferta só alcança uma máquina Admitida abaixo da versão, cujo Publisher Pin liste o assinante da
versão e cuja política deixe as atualizações ligadas. Oferecer de novo a uma máquina a versão que ela
já tem é a forma de tentar outra vez depois de uma instalação que falhou, porque uma máquina nunca
tenta de novo sozinha. **Uma versão oferecida a qualquer máquina não pode ser excluída** até as ofertas
serem retiradas. As telas estão descritas em [Console administrativo](https://docs.lacunasoftware.com/articles/trustbridge/admin-console).

## Downloads de Versões da Estação de Trabalho

Como o Servidor entrega a uma máquina a Versão da Estação de Trabalho que lhe foi oferecida. Uma versão
oferecida a todas as máquinas são até 500 máquinas × cerca de 100 MB vindos de um único Servidor, então
a única chave daqui serve para evitar um estouro de downloads simultâneos. **Uma implantação padrão não
a define.**

Uma máquina baixa de `GET /workstations/releases/{version}`. Toda requisição, tanto uma faixa retomada
quanto a primeira, traz uma asserção nova assinada pela Chave da Estação de Trabalho e só é atendida
enquanto o Servidor oferece aquela versão àquela máquina, então retirar uma oferta interrompe um
download na requisição seguinte. Toda recusa responde o mesmo `401`; o log do Servidor diz qual
verificação falhou (`A Workstation Release download was refused`). Os downloads ficam fora do
[throttling global](https://docs.lacunasoftware.com/articles/trustbridge/configuration#o-throttling-global) e não gravam linha própria na Trilha de Auditoria; a trilha
registra o que cada máquina fez depois com o arquivo.

Um download é servido da cópia em `/tmp/trustbridge-releases`, recriada a partir do banco de dados
quando falta, ou lida direto do banco onde esse diretório não pode ser gravado, o que o Servidor
registra uma vez em `Warning`.

```yaml
environment:
  WorkstationReleases__MaxConcurrentDownloads: "20"
```

### `WorkstationReleases:MaxConcurrentDownloads`

**Padrão** 20 · **Override pelo Administrador** não · **Exige reinício** sim

Quantos downloads de versões o Servidor envia ao mesmo tempo. Um download ocupa a vaga enquanto a
requisição dele durar. **Um acima do limite recebe `503` com `Retry-After`**, nunca o `429 SERVER_BUSY`
do contrato, com uma espera sorteada de novo entre 30 e 90 segundos, para que máquinas recusadas juntas
não voltem juntas. Cada máquina também espera um atraso aleatório de até dez minutos antes de começar,
o que espalha a maior parte de uma distribuição antes de este limite ser atingido.

Aumente-o para um Servidor com um link rápido cuja frota deva se atualizar mais cedo; reduza-o onde o
link é compartilhado com tráfego de assinatura que não pode esperar. Zero ou negativo recusa a
inicialização.

## O Cofre de Certificados

```yaml
environment:
  Vault__Enabled: "true"
  Vault__RevalidationInterval: "01:00:00"
```

### `Vault:Enabled`

**Padrão** `false` · **Override pelo Administrador** **não, de propósito** · **Exige reinício** sim

Se o Cofre de Certificados existe neste Servidor, isto é, se ele pode guardar uma chave privada
própria. Desligado por padrão porque esse nível torna condicional a premissa fundadora da plataforma;
o operador decide ativá-lo. Uma linha salva no banco de dados com esta chave é ignorada, pelo mesmo
motivo de `Admin:SecondFactor:Required`. Ligá-lo dá à licença do PKI SDK, que toda implantação já
tem, uma segunda função (ler os Certificados do Cofre do próprio Servidor) e traz junto a trava do
cofre. Desligá-lo não exclui nada: as linhas permanecem, a revalidação para, e
os caminhos que as leriam respondem que este Servidor não tem chaveiro.

### `Vault:RevalidationInterval`

**Padrão** 1 h · **Override pelo Administrador** não · **Exige reinício** sim

A frequência com que o Servidor verifica de novo o veredito do que o cofre guarda. Não é validada na
inicialização: um valor não positivo é informado no nível `Critical`, e o worker não roda.

## Validação de certificados e licença do PKI SDK

O mesmo nome de seção que o Workstation Service lê, com duas chaves que só fazem sentido lá. **A
licença é obrigatória e carregada em toda inicialização, com ou sem cofre**, porque é a licença com que
toda a frota funciona. Só uma implantação com o cofre ligado resolve o conjunto de âncoras; os timeouts
e a janela de tolerância são validados em toda inicialização, de qualquer forma.

```yaml
environment:
  Certificates__Validation__License: ${PKI_LICENSE}
  Certificates__Validation__UseIcpBrasil: "true"
  Certificates__Validation__UseMachineRootStore: "false"
  Certificates__Validation__UseLacunaTestPki: "false"
  Certificates__Validation__RevocationGraceWindow: "1.00:00:00"
  Certificates__Validation__RevocationTimeout: "00:00:05"
  Certificates__Validation__CheckTimeout: "00:00:30"
```

### `Certificates:Validation:License`

**Padrão** não definido · **Override pelo Administrador** não · **Exige reinício** sim

**Obrigatória em toda implantação, com ou sem Cofre de Certificados.** O `LacunaPkiLicense.config`
inteiro que a Lacuna emite, codificado em base64 numa única linha, e não um caminho. A imagem não traz
licença, então uma renovação é uma alteração de configuração e um reinício, e não um novo build.

**É a licença da frota, além da do Servidor.** O Servidor entrega o valor, exatamente como está
configurado, a toda estação de trabalho Admitida na resposta ao handshake dela; é assim que um
Workstation Service instalado é licenciado, e não há licença a definir numa máquina. A máquina a guarda
só em memória e a recebe de novo a cada conexão, então reiniciar o Servidor alcança a frota inteira:
toda máquina conectada se reconecta e aplica o valor, e uma máquina que estava offline o recebe quando
se conectar de novo. Uma máquina Pendente não recebe nada.

```bash
base64 -w0 LacunaPkiLicense.config
```

```powershell
[Convert]::ToBase64String([IO.File]::ReadAllBytes("LacunaPkiLicense.config"))
```

| Estado | O que acontece |
|---|---|
| **Ausente** (não definida, vazia ou em branco) | **O Servidor recusa a inicialização**, citando esta chave. Verificado antes da etapa do schema, então um Servidor que vai recusar não migra o banco de dados antes. |
| **Recusada** (vencida, corrompida, não é base64, não é uma licença) | O Servidor **inicia** e registra uma linha `Critical` citando esta configuração, com uma linha `Error` dizendo o motivo quando ele é conhecido. Toda página administrativa mostra um banner dizendo que nem a frota nem o cofre conseguem assinar até a licença ser renovada. O valor continua sendo entregue à frota, e cada máquina que o recusa avisa em `Critical`. Com o cofre ligado, todos os Certificados do Cofre informam `UNTRUSTED` e as importações são recusadas de imediato. |
| **Carregada** | Uma linha na inicialização dizendo que o SDK está licenciado e pronto. |

**Ela vence, e nada avisa antes dessa data.** O primeiro aviso é a linha `Critical` e o banner, e a
essa altura nada assina, nem no cofre nem em nenhuma máquina. Renová-la é substituir este valor e
reiniciar o Servidor; não há caminho em tempo de execução, e um Administrador não pode sobrescrevê-la
pelo console. Coloque o vencimento dela no calendário que acompanha as renovações de certificados. O
SDK só lê uma licença a partir de um arquivo, então o valor é gravado em
`/tmp/trustbridge-pki` e excluído na mesma chamada; um sistema de arquivos somente leitura sem um
`/tmp` gravável não consegue carregar a licença.

### As fontes de âncoras

| Chave | Padrão | Significado |
|---|---|---|
| `Certificates:Validation:UseIcpBrasil` | `true` | Confiar nas raízes da ICP-Brasil que o PKI SDK traz: o conjunto de âncoras de produção, embutido, sem download. Desligar é uma decisão de confiança. |
| `Certificates:Validation:UseMachineRootStore` | `false` | Confiar também no repositório de raízes da própria máquina. No Servidor, um container Linux, não faz nada; é uma opção da estação de trabalho. |
| `Certificates:Validation:UseLacunaTestPki` | `false` | Confiar também na PKI de teste da Lacuna. **Somente para desenvolvimento**: a Lacuna publica as chaves privadas desses certificados de exemplo. Um Servidor com esta opção ligada registra `Critical` na inicialização e mostra um **banner de aviso em todas as páginas administrativas**. |
| `Certificates:Validation:UseServerTrustedCas` | `true` | Uma opção da **estação de trabalho**. Inerte no Servidor. |
| `Certificates:Validation:TrustedRootPaths` | vazio | **Não é considerada no Servidor.** Um Servidor que a encontra definida inicia mesmo assim e registra uma linha `Critical` citando a chave e a página administrativa que a substituiu. Em vez dela, adicione uma **Autoridade Certificadora Confiável** em `/admin/trusted-cas`. |

Duas linhas `Critical` descrevem um conjunto de âncoras que não ancora nada: nenhuma âncora, e âncoras
que são só intermediárias, sem raiz autoassinada, de modo que nenhuma cadeia consegue terminar. As duas
situações tornam todo certificado `UNTRUSTED`.

### Os limites de revogação

| Chave | Padrão | Significado |
|---|---|---|
| `Certificates:Validation:RevocationGraceWindow` | 24 h (`1.00:00:00`) | Por quanto tempo a última verificação de revogação bem-sucedida mantém um certificado `ACTIVE` enquanto a AC dele está inacessível; passado esse prazo, o certificado vira `STALE`. **Esta é a decisão de confiança**: exatamente por quanto tempo um certificado continua assinando depois que a AC parou de responder, revogado ou não. Zero é válido e é o mais rigoroso; um valor negativo recusa a inicialização. |
| `Certificates:Validation:RevocationTimeout` | 5 s | Quanto tempo um download de LCR ou uma busca de emissor pode levar antes de a AC ser considerada inacessível. Também limita cada busca do percurso AIA da descoberta de Autoridades Certificadoras Confiáveis. Um valor não positivo recusa a inicialização. |
| `Certificates:Validation:CheckTimeout` | 30 s | O limite externo de toda a verificação de um certificado, incluindo o fallback para OCSP do SDK, que não tem limite próprio. Zero espera indefinidamente e não é recusado. |

## Configurações que não são chaves de appsettings

| Configuração | Pertence a | Onde |
|---|---|---|
| `ASPNETCORE_ENVIRONMENT` | O framework | **Não definida nos dois cenários**, e não definida significa `Production`, o que mantém o `appsettings.Development.json` inerte. |
| `ASPNETCORE_URLS` | Kestrel | `https://+:8443` no Docker, somente HTTPS. `http://+:8443` no Docker atrás de um proxy reverso que termina o TLS, sem as configurações `Kestrel__Certificates__*`. Não definida no App Service. |
| `Kestrel__Certificates__Default__Path` / `__Password` | Kestrel | O certificado TLS do próprio Servidor no Docker. Nada a definir no App Service. |
| `WEBSITES_PORT` | App Service | `8080`. |
| `WEBSITES_CONTAINER_START_TIME_LIMIT` | App Service | `1800`, para que uma migração longa não seja encerrada e reiniciada em loop. |
| `ASPNETCORE_FORWARDEDHEADERS_ENABLED` | O framework | `true` no App Service, ou toda linha de auditoria registra o front end da plataforma em vez de quem fez a chamada. **Ela confia em quem se conectar**, então um proxy que você opera é citado em [`ForwardedHeaders:TrustedProxies`](https://docs.lacunasoftware.com/articles/trustbridge/configuration#forwardedheaderstrustedproxies); definir as duas recusa a inicialização. |
| `APPLICATIONINSIGHTS_CONNECTION_STRING` | App Service | Lida quando `Logging:ApplicationInsights:ConnectionString` não está definida. |
| `TMPDIR` | O processo | Move os três diretórios temporários (`trustbridge-pki`, `trustbridge-logs`, `trustbridge-releases`). De propósito, não existe configuração do TrustBridge para isso. |
