Arquitetura
client ──HTTPS──▶ Caddy ──▶ nelcota (single binary) ──▶ Postgres 17
│ axum: /health, /rest/v1/*, /storage/v1/*
│ auth: validates JWT → Claims
│ core: pool (authenticator) + transaction with role/claimsPré-requisitos
Esta página descreve propriedade de módulos e orçamentos da fonte atual. Para compilá-la, use o Rust fixado no projeto; validação em execução exige PostgreSQL. Escolhas históricas ficam no registro de decisões, e artefatos publicados podem ser anteriores a capacidades exclusivas da fonte.
Crates
| Crate | Responsabilidade |
|---|---|
nelcota-core |
configuração (figment), erros HTTP, Claims/Role, pool, migrações, begin_request |
nelcota-auth |
trait JwtVerifier, chaves EdDSA/HS256 e JWKS, extrator Auth, cadastro/login/renovação/logout, links de e-mail (recuperação, confirmação, link mágico), provedores OAuth/OIDC, argon2id, limitação de requisições |
nelcota-api |
introspecção do catálogo, construtor SQL, CRUD/RPC, OpenAPI, tipos TS |
nelcota-storage |
arquivos: buckets e objetos sob RLS (esquema storage), bytes em disco ou S3 (object_store), URLs assinadas, coletor de órfãos |
nelcota-webhooks |
eventos transacionais de linhas, concessões persistentes de entrega, assinatura, envio HTTP seguro, novas tentativas e retenção |
nelcota-admin |
painel em /admin: API JSON (/admin/api) + SPA Svelte embutida (ui/dist) |
nelcota-cli |
host com N projetos (init, up, projects, remove, migrate, backup, upgrade, panel-login...). Módulos: host (registro), naming, scaffold, caddy, registry, panel_login, lifecycle, backup, upgrade |
nelcota-deployment |
tipos de versão/protocolo, jobs duráveis, leases do host e decisões irreversíveis de recuperação; CLI/admin adaptam efeitos do runtime e HTTP do operador |
nelcota-server |
o binário nelcota: despacha a CLI ou inicia o servidor (rastreamento, timeout, CORS, gzip) |
O backend compila em um binário, estático com musl na imagem Docker. O tamanho depende da versão/arquitetura e dos assets incorporados; builds antigos não determinam tamanho atual ou capacidade do deploy.
O compilador de consultas separa a representação validada da requisição (query/mod.rs), a análise da URL (query/parse.rs) e a geração SQL (query/sql.rs), por trás da interface pública existente. Handlers do painel são agrupados em admin/src/tables/, users/ e policies/, com pequenas fachadas na raiz que expõem handlers nomeados ao roteador. Tabelas cuidam de consultas de linhas, estrutura, exportações e adaptação DDL; tables/catalog fornece metadados também usados pela visão geral e pelas políticas. A geração DDL pura fica em ddl/, a execução em apply e a adaptação de erros SQL em error. O recurso SQL também cuida de seu endpoint de esquema para preenchimento automático.
Os handlers HTTP de autenticação adaptam requisições para accounts e sessions. db cuida das transações com o papel de autenticação e request dos metadados HTTP. links cuida dos links de e-mail de uso único (armazenamento de tokens, texto do e-mail, envio e login por link); recovery acrescenta a troca de senha, confirmation a política de cadastro e verify encaminha um link por seu type. oauth cuida do login por provedor: provider (com um módulo por provedor) se comunica com o provedor, flow armazena logins em andamento, identities decide qual conta abrir e redirects mantém a lista de permissões. A raiz do painel monta as rotas; auth, middleware, error, assets e state cuidam de seus respectivos comportamentos.
O módulo upload do armazenamento cuida de escritas em streaming e do commit dos metadados, operations cuida de leituras/listagem/exclusão e signing de concessões para arquivos assinados. Sua interface recebe opções de upload e um stream de bytes e retorna dados tipados. O adaptador http trata cabeçalhos, status da resposta, URLs e entrega de arquivos tanto para rotas públicas quanto para o painel. As verificações de políticas ainda ocorrem antes do consumo dos bytes, e escritas com falha ainda limpam suas versões não publicadas.
A CLI separa o ciclo de vida do projeto e as atualizações de versão dos fluxos de backup. backup/workflow coordena captura, retenção e restauração; backup/remote cuida do transporte S3. A captura do banco e dos arquivos imutáveis mantém um único responsável para preservar a consistência do snapshot.
Webhooks separam configuração/histórico do painel (management), política de destino e DNS (destination), HMAC (signing), persistência de concessões (queue), entrega HTTP (transport) e agendamento assíncrono (worker). Um trigger do PostgreSQL faz commit dos eventos junto das linhas de negócio; um pool dedicado de duas conexões e quatro envios concorrentes mantêm os envios independentes do tráfego da API e do painel. Veja eventos e webhooks para garantias e limites de entrega.
Os DTOs de comunicação do painel ficam em admin/src/contracts.rs e nos tipos existentes de estrutura/DDL. Seus TypeScript e JSON Schemas gerados são versionados junto da UI; validadores independentes rejeitam respostas incompatíveis no cliente. Valores de linhas continuam sendo strings ou NULL para preservar a precisão de decimal e bigint.
O editor SQL transmite mensagens por conexões dedicadas com timeout de instrução de 30 segundos, quatro execuções concorrentes, 1000 linhas retidas por resultado, orçamento de 8 MiB para valores retidos e 32 resultados exibidos. Ele consome todo o lote após atingir um limite de exibição, preservando a semântica de transações. Descartar o future HTTP envia uma requisição de cancelamento ao Postgres e fecha sua conexão dedicada. As conexões comuns do pool da API permanecem separadas.
As prévias de tabelas reutilizam uma conexão administrativa e revertem o papel do chamador antes de ler os metadados como dono. As exportações de tabelas usam conexões separadas, com dois downloads simultâneos, limite de 30 segundos por comando e duração de dois minutos para a resposta. Descartar uma resposta cancela a consulta; o prazo fecha a conexão mesmo quando o corpo não é consumido. A vaga continua ocupada durante o cancelamento, e um stream interrompido falha em vez de informar que o arquivo está completo. As contagens de tabelas pequenas examinam no máximo 10.001 linhas com limite de 250 ms por comando; estimativas de tabelas pequenas que cresceram deixam o total desconhecido.
A listagem de diretórios do storage busca por uma chave de bytes gerada a partir do caminho pai, pulando a subárvore de cada pasta retornada. Cada busca consulta storage.objects sob o RLS do chamador. Segmentos UTF-8 separados por NUL preservam a ordenação C e isolam prefixos literais, incluindo Unicode, % e _. Um índice ordenado por hash de escopo e nome pagina os arquivos diretamente na pasta. Seu hash inteiro gerado permite buscas sob RLS; verificações exatas de bucket e caminho pai rejeitam colisões de hash. As transações de listagem desabilitam varreduras por bitmap localmente para favorecer buscas ordenadas de páginas quando faltam estatísticas. As funções de chave apenas transformam suas entradas; a execução pública preserva os escritores SQL customizados sem alterar o RLS dos objetos. O navegador de um bucket solicita apenas o uso daquele bucket. As migrações V14 e V15 adicionam índices de ordenação de usuários e diretórios. A V15 calcula as chaves derivadas para os metadados existentes e reconstrói a tabela sob o lock de migração, então upgrades de storages grandes precisam de tempo para essa reescrita única.
DDL faz commit uma vez e informa applied e catalog_pending. Uma falha de introspecção após COMMIT não transforma uma mutação bem-sucedida em erro. Recargas do catálogo são serializadas, e um worker repete atualizações que falharam com espera progressiva; o cliente atualiza sua visão sem repetir a alteração.
RemoteResource cuida de carregamento, erros, cancelamento e ordenação de resultados nas páginas de tabelas, usuários e armazenamento. A fila de uploads do armazenamento cuida do progresso, falhas parciais e novas tentativas após conflitos, capturando o bucket e a pasta antes de mudanças de navegação. Esses módulos têm testes unitários; fluxos de navegador e regressões com Postgres real executam na CI. TableRows cuida da seleção de linhas, carregamento e reconciliação de mutações, incluindo chaves primárias capturadas e navegação durante escritas. A página de tabela cuida de sua navegação, diálogos e apresentação; seu adaptador HTTP fornece operações de linhas.
As operações de recuperação cuidam da emissão e consumo atômico de links, atualização de senhas, revogação de sessões e entrega de e-mail após commit. Handlers HTTP apenas extraem metadados da requisição, aplicam limites de requisições dos endpoints públicos e adaptam seus resultados. Operações de migração retornam registros tipados e fazem commit da alocação de versões, checksums e acompanhamento de alterações juntos; seu módulo files cuida de nomes, renderização e descoberta de diretórios, enquanto a raiz trata downloads HTTP.
A raiz REST compõe rotas. Seu adaptador http trata URLs, preferências, status e cabeçalhos; operations compila/executa CRUD/RPC com o papel do solicitante, retornando JSON gerado pelo PostgreSQL e metadados de intervalo. O catálogo separa model, introspection, refresh e listener atrás da interface pública CatalogHandle. Mantenha o proprietário ReloadListener retornado até o encerramento; descartá-lo cancela listener/driver. Descartar o último proprietário do catálogo cancela a reconciliação, inclusive esperas pelo pool.
O executor SQL cuida do ciclo de vida das conexões, concorrência, limites de resultados e cancelamento por trás de execute; a rota trata status HTTP e serialização. SqlExecution do painel cuida do estado e duração da requisição, e StorageBrowser da paginação de pastas e atualizações após uploads/exclusões. Ambos aceitam adaptadores e descartam respostas antigas quando cancelados ou após navegar para outra página.
A implantação nativa separa provisionamento de máquina/pacotes, unidades systemd, configuração PostgreSQL e substituição atômica do binário. O Caddy cuida de suas próprias escritas de configuração. PITR separa a configuração privada dos adaptadores de processo e da sequência de restauração; a restauração ainda coordena parar serviços, restaurar, aguardar promoção, fazer backup na nova linha do tempo e iniciar o aplicativo.
Organização do código-fonte
Nelcota é um monólito modular: módulos de recursos são distribuídos em um binário. Use uma pequena fachada para expor uma operação completa, mantendo sua validação, transação e limpeza juntas. Adaptadores HTTP traduzem requisições, resultados e erros. Introduza adaptadores intercambiáveis onde as implementações realmente variam, como armazenamento em disco/S3 e entrega de e-mail SMTP/de teste. Funções concretas e tipos existentes bastam nos demais casos; não é necessário um repositório genérico ou outra camada para envolver o PostgreSQL.
Mantenha os limites existentes entre crates: cada crate cuida de um recurso do produto, e server os reúne. Adicione um módulo interno quando um comportamento precisar de um responsável independente; o tamanho de um arquivo, por si só, não exige um novo crate ou camada. Fachadas de módulos Rust usam exportações explícitas. Tipos retornados por um método público devem ser acessíveis por sua interface pública, incluindo erros como SqlBusy junto de SqlExecutor.
Mutações de buckets pertencem à interface de operações do armazenamento em StorageState. Tanto as rotas públicas quanto o painel a chamam; privilégios de buckets, RLS e chaves estrangeiras continuam sendo responsabilidade do PostgreSQL. Operações retornam registros tipados e erros de buckets, enquanto cada adaptador HTTP preserva seu vocabulário de respostas/erros. Apenas o adaptador do painel remove entradas MIME vazias dos formulários. Uma exclusão com falha faz rollback antes de ler a contagem opcional de arquivos para sua resposta de erro. Operações de usuários do painel aceitam o pool administrativo e retornam dados; seu adaptador HTTP cuida de mensagens e códigos de status. Alterações de senha e revogação de sessões continuam em uma transação dentro da operação.
ui/src/lib/features/<feature>/ do painel cuida de sua página, estado, adaptador HTTP, funções auxiliares puras, componentes e testes unitários adjacentes. shell/ cuida de navegação, paleta de comandos e estrutura do aplicativo. Componentes reutilizáveis do painel ficam em components/shared/; primitivas shadcn permanecem em components/ui/. Metadados de tabelas/políticas e prévias DDL compartilhadas por vários recursos ficam em shared/schema/. Mantenha transporte compartilhado, contratos, carregamento de recursos e persistência no navegador na raiz da biblioteca; a persistência se chama local-storage.ts para diferenciá-la do recurso de armazenamento. Exemplos de uso da API pertencem a features/api/api-examples.ts.
Use imports relativos dentro de um recurso e $lib/... para dependências fora dele. Nomes de arquivos Rust usam snake_case, funções auxiliares TypeScript usam kebab-case e componentes Svelte usam PascalCase. Use structs/enums ou tipos TypeScript simples para dados; módulos com estado compõem módulos existentes de recursos por trás de seus próprios métodos. Reutilize os tipos de comunicação gerados em vez de copiar sua estrutura; SchemaResponse atende tanto ao preenchimento automático quanto ao estado das requisições SQL. Não edite manualmente os contratos ou validadores gerados.
Testes unitários Rust ficam junto de sua implementação. Cenários de integração ficam em server/tests, onde o roteador de produção e uma instância PostgreSQL real são montados. tests/admin.rs carrega módulos de recursos em tests/admin/ como uma suíte de integração. tests/common/panel.rs cuida do login do painel, verificações de cookies e funções auxiliares de requisição compartilhadas com as suítes de armazenamento e arquitetura.
Fluxo de uma requisição
Auth(extrator) transforma o cabeçalhoAuthorizationemClaims.- A operação obtém uma conexão do pool (sempre como
authenticator). db::begin_requestabre a transação e define papel + claims.- A consulta executa sob RLS; o Postgres constrói o JSON (
json_agg) e a API apenas repassa os bytes. - COMMIT. Erros do Postgres são traduzidos por
ApiError::from_db.
db::begin_request é o único caminho para executar SQL em nome de um usuário.
Inicialização
- Carrega a configuração (
NELCOTA_*+nelcota.tomlopcional). db::bootstrap(conexão administrativa): bloqueio consultivo → migrações emmigrations/(tabela de controlenelcota.schema_migrations) → senha deauthenticator(verificador SCRAM calculado localmente).- Cria o pool da API e inicia o servidor HTTP. Encerra de forma limpa em SIGTERM/Ctrl+C.
Rotas
| Prefixo | Servido por | Autorização |
|---|---|---|
/health |
server | público |
/rest/v1/* |
api | JWT → papel → GRANTs + RLS |
/auth/v1/* |
auth | público (cadastro/login/links de e-mail/login por provedor) ou JWT do usuário |
/storage/v1/* |
storage | JWT → papel → RLS em storage.objects; buckets públicos e URLs assinadas sem token |
/admin/* |
admin | login do painel (sessão), conexão administrativa |
Migrações internas
SQL simples em migrations/V<n>__<name>.sql, aplicado por refinery e registrado em nelcota.schema_migrations (versão, nome, data, checksum). Podem ser aplicadas manualmente com psql se o Nelcota deixar de existir. As primeiras (V1–V6) mantêm seus nomes e comentários originais em português: refinery calcula checksums do nome e conteúdo, então editá-las quebraria instalações existentes (D68).
Fluxo de autenticação
sequenceDiagram
participant App as Application client
participant Auth as /auth/v1
participant DB as Private PostgreSQL
App->>Auth: Signup or password sign-in
Auth->>DB: Validate credentials / create session
Auth-->>App: Access JWT and rotating refresh token
App->>Auth: Refresh token (serialize callers)
Auth->>DB: Atomic rotation / reuse detection
Auth-->>App: Replacement token pair
Note over App,DB: REST/storage use access JWT + transaction-local role/claims and RLSO painel do projeto tem sessões administrativas separadas. Atualizações controladas adicionam processos controller/worker usando o mesmo binário e uma origem de operador autorizada separadamente; não concedem autoridade sobre o host a um login de projeto. O controller permanece exclusivo da fonte até nova publicação e validação em VPS.
Guias relacionados
Veja decisões, API REST, autenticação, webhooks e deploy. A configuração de autenticação reutilizável fica no guia exclusivo do repositório em docs/auth-integration.md. Diagramas Mermaid são renderizados no GitHub; o leitor incorporado atual mostra a fonte.
Limites do controlador de host
O gerenciamento de projetos do host mantém comandos puros, DTOs limitados e registros persistentes em nelcota-deployment; efeitos privilegiados, política e recuperação ficam em nelcota-cli. nelcota-admin é um adaptador HTTP sem privilégios. A interface com autenticação separada solicita planos e operações confirmadas ao worker; credenciais de projeto nunca concedem autoridade de host. Atualizações, alterações pela CLI e tarefas do host compartilham um único bloqueio e recusam trabalho não resolvido. Consulte docs/host-management.md para o cadastro, a recuperação e os limites de validação do recurso ainda não lançado.