Multi-tenant SME finance platform — banking sync, cash flow, invoices, AI insights via optional ContextMemory or direct OpenAI-compatible LLM. Clean Architecture, .NET 9, Blazor Server, AdminLTE 4.
Contents: Run · Docker · Secrets · ContextMemory · Architecture · License
dotnet restore
dotnet build
dotnet run --project src/Fincheck.Web --urls http://localhost:5188Abra http://localhost:5188. Em Development, o seeder aplica migrações e cria dados demo.
O Fincheck não altera o docker-compose de C:\Network\infra. Sobe contentores extra (fincheck, keycloak) na rede network-local já usada por gateway, laravox, companybrain, etc.
# 1) Infra partilhada (Postgres em network-postgres)
cd C:\Network\infra
docker compose up -d
# 2) Fincheck na mesma rede (Keycloak no servidor por defeito via .env)
cd c:\Network\_work\FinCheck\Fincheck
cp .env.example .env # preencher secrets
docker compose up --build -d
# Keycloak local opcional (offline):
# docker compose --profile local-keycloak up -d
# ContextMemory opcional (GHCR):
# docker compose -f docker-compose.yml -f docker-compose.contextmemory.yml up --build -d| Serviço | URL |
|---|---|
| Fincheck | http://localhost:5300 |
| Keycloak (servidor) | https://keycloak.kortexio.io — realm fincheck, client fincheck-web |
| Keycloak local (profile) | http://localhost:8180 (admin / admin) |
Backup: Postgres remoto + volume Docker fincheck_documents (App_Data/documents) e fincheck_data (DP keys).
Se a rede tiver outro nome: CM_DOCKER_NETWORK=<nome> docker compose up -d.
O serviço db-init garante as bases fincheck e keycloak no Postgres partilhado quando usa o profile local-keycloak.
Push para main (ou Run workflow manual) faz deploy neste PC via self-hosted runner.
- Docker Desktop a correr e network-infra na rede
network-local(ou o nome emCM_DOCKER_NETWORKno.env). - No GitHub: Settings → Actions → Runners → New self-hosted runner → Windows e copia o token.
- No PowerShell (neste repositório):
.\scripts\setup-github-runner.ps1 -Token "COLE_O_TOKEN_AQUI" -Start- Deixa a janela do runner aberta (sessão de utilizador — necessário para o Docker Desktop).
- Faz push para
mainou em Actions → Deploy Docker Desktop → Run workflow.
O workflow está em .github/workflows/deploy-docker-desktop.yml. App: http://localhost:5300.
dotnet test| Campo | Valor |
|---|---|
demo@fincheck.local |
|
| Password | Demo!23456 |
| Ambiente | Onde |
|---|---|
| Docker / deploy | Ficheiro .env na raiz (gitignored). O workflow self-hosted copia C:\Network\_work\FinCheck\Fincheck\.env. Template: .env.example. |
dotnet run local |
dotnet user-secrets no projecto Web |
appsettings.json não deve conter passwords nem ApiKeys.
Configure user-secrets (dotnet user-secrets set ... --project src/Fincheck.Web):
| Chave | Descrição |
|---|---|
ConnectionStrings:DefaultConnection |
PostgreSQL (produção/dev remoto) |
EnableBanking:ApplicationId |
Application ID Enable Banking |
EnableBanking:PrivateKeyPath |
Caminho para a chave privada JWT (PEM) |
EnableBanking:RedirectUri |
URI de callback OAuth (ex. http://localhost:5188/banking/callback) |
EnableBanking:WebhookSecret |
Segredo opcional para webhooks inbound |
EnableBanking:AspspName / AspspCountry |
ASPSP sandbox (ex. Model Bank / FI ou banco PT) |
EnableBanking:UseDemo |
false para usar o agregador real |
Email:DedicatedInboxDomain |
Domínio da caixa dedicada (invoices+{schema}@dominio) |
Mollie:ApiKey |
API key Mollie |
Mollie:WebhookUrl |
URL do webhook Mollie |
Mollie:WebhookSecret |
Segredo opcional partilhado; se definido, o webhook exige o header X-Mollie-Signature (ou query signature) igual a este valor. A Mollie não assina webhooks nativamente — isto é uma proteção simples anti-spoofing, não HMAC. |
Mollie:UseDemo |
false para pagamentos reais |
Email:Provider |
imap / gmail / outlook / demo |
Email:Username |
Utilizador IMAP/SMTP |
Email:Password |
Password / app password IMAP |
Email:SmtpHost |
Host SMTP |
Email:UseDemo |
false para email real |
Ai:BaseUrl |
Host OpenAI-compatible sem /v1 (ex. http://localhost:11434 ou vLLM). O cliente chama POST {BaseUrl}/v1/chat/completions |
Ai:Model |
Nome do modelo (ex. qwen3.5:9b) |
Ai:UseDemo |
false para Insights/Categorizer via LLM |
Ai:TimeoutSeconds |
Timeout HTTP para a IA |
Providers externos ficam em modo demo por defeito (Integrations:UseDemoProviders=true). A IA é controlada à parte por Ai:UseDemo.
O chat financeiro e a memória de insights podem usar o gateway ContextMemory. Sem gateway, a app fala com o modelo via Ai:* (OpenAI-compatible) ou fica em demo.
"ContextMemory": {
"BaseUrl": "http://localhost:5100",
"AppId": "fincheck",
"ApiKey": "cm_live_…",
"NumCtx": 32768
}- Com
BaseUrl+ApiKey: chat viaPOST /v1/chat/completions+ query à wiki do tenant; cada conversa temsessionIdemlocalStorage. - Insights gravados na BD são também upsert à wiki (
sourceId=tenant-{tenantId}). - Sem
ApiKey: chat continua em demo/LLM directo OpenAI-compatible (Ai:*).
Self-host: clone/run Kortexio/ContextMemory, ou:
docker compose -f docker-compose.yml -f docker-compose.contextmemory.yml up --build -dMais detalhe: docs/llm-openai-compatible.md.
Docker env: CONTEXT_MEMORY_BASE_URL, CONTEXT_MEMORY_APP_ID, CONTEXT_MEMORY_API_KEY.
- Crie uma aplicação em Enable Banking e descarregue a chave privada JWT.
- Configure secrets:
dotnet user-secrets set "EnableBanking:ApplicationId" "<app-id>" --project src/Fincheck.Web
dotnet user-secrets set "EnableBanking:PrivateKeyPath" "C:/secrets/enablebanking.pem" --project src/Fincheck.Web
dotnet user-secrets set "EnableBanking:RedirectUri" "http://localhost:5188/banking/callback" --project src/Fincheck.Web
dotnet user-secrets set "EnableBanking:UseDemo" "false" --project src/Fincheck.Web
dotnet user-secrets set "Integrations:UseDemoProviders" "false" --project src/Fincheck.Web- No painel Enable Banking, registe o redirect URI exatamente como acima.
- Em Contas bancárias → ligar banco → após SCA o callback
/banking/callbackcompleta a sessão. - A
BankConnection.ExternalConnectionIdguarda osession_id(persiste após restart). Em Consentimentos pode sincronizar ou reconectar se o estado forRequiresReauth. - Webhook (opcional):
POST /api/webhooks/banking/{provider}comEnableBanking:WebhookSecret.
Endereço por tenant: invoices+{schema}@{Email:DedicatedInboxDomain}. Configure IMAP real (Email:UseDemo=false, Email:Provider=imap) numa mailbox partilhada; o job de 5 min encaminha anexos PDF para o schema correspondente.
- Fincheck.Domain — entidades, value objects, regras puras
- Fincheck.Application — casos de uso (MediatR), ports, DTOs
- Fincheck.Infrastructure — EF Core + Identity, adapters (Enable Banking / demo, OpenAI-compatible LLM / ContextMemory / demo IA, Mollie / demo, email), jobs em background
- Fincheck.Web — Blazor Server + AdminLTE 4 + Minimal APIs
Isolamento primário por TenantId. Em PostgreSQL, o provisioning cria um schema por tenant (CREATE SCHEMA + CREATE TABLE … LIKE public.* para tabelas de negócio) e o TenantSchemaInterceptor define search_path para {schema}, public. Tabelas Identity / Tenants / Memberships permanecem em public.
Dashboard · Contas bancárias · Transações · Cash flow · Categorias · Faturas/revisão · Contas a pagar · Insights IA · Alertas · Relatórios · Equipa · Plano e pagamento · Documentos · Definições (RGPD) · Admin tenants
Nota sobre faturação: o Fincheck cobra por pagamento único via Mollie (sem subscrição recorrente automática nem cobrança silenciosa). Quando o período pago termina e o tenant é suspenso, ações premium (sync bancário, chat financeiro IA) ficam bloqueadas até novo pagamento (ver IPlanGate).
AGPL-3.0 for this open-source core (same as KYC / ContextMemory). Commercial / hosted offerings: kortexio.io. See docs/license-and-support.md and CONTRIBUTING.md.
Suggested GitHub topics: fintech, sme, cashflow, open-banking, csharp, dotnet, blazor, postgresql, contextmemory, multi-tenant, self-hosted, agpl, open-source, mollie, pme.